﻿/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is VisKort.
*
* The Initial Developer of the Original Code is
* IT- og Telestyrelsen / Danish National IT and Telecom Agency.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):

* Lars Klindt Mogensen
* Morten Bødtkjer
* Niels Kinnerup
* Thomas Bergstedt
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

/**
* Global projection
*
*
*/
// Setup proj4j projections
Proj4js.defs["EPSG:25832"] = "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs ";
Proj4js.defs["EPSG:4326"] = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ";
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";

/** 
* Global variable: viskort
* (<VisKort.Application>) Holds the application 
*/
var viskort;

VisKort = {};

/** 
* 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: applicationModeGlobal
    * {<bool>} If true, application mode is global, and itnernal projection is web mercator
    */
    applicationModeGlobal: false,

    /** 
    * Property: googleRouteEnabled
    * {<bool>} If true, google routeplan is enabled.
    */
    googleRouteEnabled: false,

    /** 
    * Property: routeWareEnabled
    * {<bool>} If true, routeware is enabled.
    */
    routeWareEnabled: true,

    /** 
    * Property: rejsePlanEnabled
    * {<bool>} If true, rejseplan is enabled.
    */
    rejsePlanEnabled: true,

    /** 
    * Property: infoBox
    * {<OpenLayers.Control.InfoBox>} The reference to the infobox to show popup balloon information
    */
    infoBox: null,

    /** 
    * Property: infoBox
    * {<OpenLayers.Geometry.Polygon>} The area that the infobox must cover (null: no limitations)
    */
    infoBoxArea: 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: 12,

    /**
    * Property: resolutions
    * {<Array of numbers>} Resolutions of the zoom levels
    */
    resolutions: null,

    /**
    * Property: maxResolution
    * {<Number>} Maximum resolution
    */
    maxResolution: null,

    /**
    * Property: resolutions
    * {OpenLayers.Bounds} Bounds for max extend
    */
    maxExt: null,

    /**
    * Property: resolutions
    * {OpenLayers.Bounds} Bounds for start extend
    */
    startExt: null,

    /**
    * 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: clientLayersKml
    * {String} Holds original Kml strings sent from a parent window. 
    */
    clientLayersKml: "popup",

    /**
    * Property: baseQuerystring
    * (String) constant parameters
    */
    baseQuerystring: null,

    /**
    * Property: internalprojection
    * (String) holds the projection code of the map
    */
    internalprojection: null,


    /**
    * Property: infoBoxThemeMode
    * (String) holds the theme mode information for infobox
    */
    infoBoxThemeMode: null,

    osm_getTileURL: 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_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: 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;
    },

    /**
    * 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, searchResultLayerId, infoBoxSettings, applicationMode, zoomToExt, zoomToExt4326, rejsePlanEnabled, googleRouteEnabled, mSVEEnabled, loadingPanel, routeWareEnabled, infoBoxThemeMode) 
    {
        if (pagemode)
            this.pagemode = pagemode;
        if (applicationMode.toLowerCase() == "global")
            this.applicationModeGlobal = true;

        this.googleRouteEnabled = googleRouteEnabled;
        this.routeWareEnabled = routeWareEnabled;
        this.rejsePlanEnabled = rejsePlanEnabled;
        this.infoBoxThemeMode = infoBoxThemeMode;

        // Setup proxy location and active theme etc.
        OpenLayers.ProxyHost = "Proxy.ashx?url=";
        OpenLayers.ImgPath = '/VisKort/App_Themes/' + activeTheme + '/Images/OpenLayers/';
        this.active_theme = activeTheme;
        this.baseQuerystring = baseQuerystring;

        var includeOverviewMap = true;

        // Setup configuration values according to application mode
        if (!this.applicationModeGlobal) {
            this.internalprojection = "EPSG:25832";
            this.resolutions = new Array(0.8, 1.6, 3.2, 6.4, 12.8, 25.6, 51.2, 102.4, 204.8, 409.6, 819.2, 1638.4);
            this.startExt = new OpenLayers.Bounds(420000, 6025000, 905000, 6450000);
            this.maxExt = new OpenLayers.Bounds(120000, 5661139.2, 958860.8, 6500000);
            // KMS WMTS: bottom: 6500000 - 2*(6919430,4-5241708,8)/4 = 5661139.2, right: 120000 + 2*(1378291,2-(-299430,4))/4 = 958860.8

            // Set InfoBox area (covers Denmark)
            var infoBoxRing = new OpenLayers.Geometry.LinearRing();
            infoBoxRing.addComponent(new OpenLayers.Geometry.Point(440000, 6040000)); // SW
            infoBoxRing.addComponent(new OpenLayers.Geometry.Point(350000, 6330000)); // W
            infoBoxRing.addComponent(new OpenLayers.Geometry.Point(610000, 6430000)); // N
            infoBoxRing.addComponent(new OpenLayers.Geometry.Point(750000, 6220000)); // NE 1
            infoBoxRing.addComponent(new OpenLayers.Geometry.Point(750000, 6140000)); // NE 2
            infoBoxRing.addComponent(new OpenLayers.Geometry.Point(900000, 6160000)); // E
            infoBoxRing.addComponent(new OpenLayers.Geometry.Point(900000, 6100000)); // SE
            infoBoxRing.addComponent(new OpenLayers.Geometry.Point(710000, 6040000)); // S
            this.infoBoxArea = new OpenLayers.Geometry.Polygon(infoBoxRing);
        }
        else { // applicationMode == "global"
            this.zoomlevels = 20;
            this.internalprojection = new OpenLayers.Projection("EPSG:102113");
            this.maxResolution = 156543.0339;
            var tmpBounds = new OpenLayers.Bounds(420000, 6025000, 905000, 6450000);
            this.startExt = tmpBounds.transform(new OpenLayers.Projection("EPSG:25832"), new OpenLayers.Projection("EPSG:102113"));
            //this.startExt = new OpenLayers.Bounds(864805.2323405755, 7239877.261090828, 1765540.217375446, 7968616.670477106);
            this.maxExt = new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34);
            includeOverviewMap = true;
        }

        if (zoomToExt)
            this.startExt = new OpenLayers.Bounds(zoomToExt4326[0], zoomToExt4326[1], zoomToExt4326[2], zoomToExt4326[3]);
        else if (zoomToExt4326) {
            var startExt4326 = new OpenLayers.Bounds(zoomToExt4326[0], zoomToExt4326[1], zoomToExt4326[2], zoomToExt4326[3]);
            this.startExt = startExt4326.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection(this.internalprojection));
        }

        // Initialize controls
        var layerSwitcher = new OpenLayers.Control.StyledLayerSwitcher(mapConfig);
        var scaleLineOptions = { bottomOutUnits: "", bottomInUnits: "" };
        var scaleLine = new OpenLayers.Control.ScaleLineBugFixed(scaleLineOptions);
        var navigation = new OpenLayers.Control.ModifiedNavigator();
        var panZoomBar = new OpenLayers.Control.PanZoomBarBugFix();
        var shortcuts = new OpenLayers.Control.ModifiedKeyboard();

        var overviewMapOptions;
        if (!this.applicationModeGlobal) {
            overviewMapOptions = { projection: this.internalprojection, units: 'm', maxExtent: this.maxExt };
        } else {
            overviewMapOptions = { projection: this.internalprojection, units: 'm', maxResolution: this.maxResolution, maxExtent: this.maxExt };
        }

        var overviewMap = new OpenLayers.Control.StyledOverviewMap({ minRatio: 32, maxRatio: 64, mapOptions: overviewMapOptions });
        var infoBoxLinkGenerators = null;
        var mousecontrol = new OpenLayers.Control.MousePosition();
        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") {
            if (routeWareEnabled)
                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({}, this.infoBoxArea, infoBoxLinkGenerators, infoBoxSettings);

        // Initialize mapcontrols
        var activateInfoBox = true;
        if (includeOverviewMap)
            mapcontrols = [layerSwitcher, scaleLine, panZoomBar, navigation, overviewMap, infoBox, printButton, shortcuts];
        else
            mapcontrols = [layerSwitcher, scaleLine, panZoomBar, navigation, infoBox, printButton, shortcuts];
       

        if (mSVEEnabled)
            mapcontrols[mapcontrols.length] = virtualEarthButton;
        if (loadingPanel)
            mapcontrols[mapcontrols.length] = new OpenLayers.Control.LoadingPanel();

        if (this.pagemode == "print") {
            mapcontrols = [scaleLine, infoBox, panZoomBar, navigation, shortcuts];
        }

        if (this.maxResolution == null) {
            var maxresolution = null;
            if (this.pagemode == "print") {
                var printmapDiv = document.getElementById(mapDivId);
                try {
                    if (parentMapDivHeight >= printmapDiv.clientHeight || parentMapDivWidth >= printmapDiv.clientWidth) {
                        maxresolution = (this.maxExt.top - this.maxExt.bottom) / 164;
                    } else {
                        maxresolution = (this.maxExt.top - this.maxExt.bottom) / 256;
                    }
                } catch (err) {
                    maxresolution = (this.maxExt.top - this.maxExt.bottom) / 164;
                }
            } else {
                maxresolution = (this.maxExt.top - this.maxExt.bottom) / 256;
            }
            this.maxResolution = maxresolution;
        }

        var options = {
            controls: mapcontrols,
            projection: this.internalprojection,
            resolutions: this.resolutions,
            units: "m",
            maxResolution: this.maxResolution,
            maxExtent: this.maxExt,
            numZoomLevels: this.zoomlevels
        }

        var viskortMapPositionChanged_parentMethodExists = false;
        try {
            if (window.parent)
                viskortMapPositionChanged_parentMethodExists = window.parent.viskortMapPositionChanged;
        } catch (nullPointerOrCrossDomainErr) { }
        if (viskortMapPositionChanged_parentMethodExists) {
            options.eventListeners = { "moveend": this.mapMoveZoomEvent.bind(this), "zoomend": this.mapMoveZoomEvent.bind(this) };
        }

        this.map = new VisStedet.Map(mapDivId, options);


        if (this.pagemode != "print") {
            printButton.activate();
            printButton.active_print();

            if (mSVEEnabled) {
                virtualEarthButton.activate();
                virtualEarthButton.active_virtualEarth();
            }
        }

        // Add KML layers from clientside scripting (except searchLayerKml)
        var loadData_parentMethodExists = false;
        try {
            if (window.parent)
                loadData_parentMethodExists = window.parent.loadData;
        } catch (nullPointerOrCrossDomainErr) { }

        if (loadData_parentMethodExists) {
            var clientLayers = window.parent.loadData();
            var clientLayerLoadEndCallback = function () 
	    {
                if (this.formatObject && this.formatObject.kmlDocumentName) {
                    this.name = this.formatObject.kmlDocumentName;
                    this.displayInLayerSwitcher = true;
                }
            }

            if (clientLayers.editKml) {
                var editLayer = new OpenLayers.Layer.KML("editKml", null,
                        			    { isBaseLayer: false,
                        			        formatOptions: { extractStyles: true, extractAttributes: true },
                        			        projection: "EPSG:4326",
                        			        displayInLayerSwitcher: false
                        			    },
                        			    this.internalprojection, clientLayers.editKml);
                this.map.addLayer(editLayer);
            }

            if (clientLayers.viewKml) {
                var currentViewLayer;
                for (var i = 0; i < clientLayers.viewKml.length; i++) {
                    currentViewLayer = new OpenLayers.Layer.KML("viewKml_" + i, null,
                        			    { isBaseLayer: false,
                        			        formatOptions: { extractStyles: true, extractAttributes: true },
                        			        projection: "EPSG:4326",
                        			        displayInLayerSwitcher: false
                        			    }, this.internalprojection, clientLayers.viewKml[i]);
                    this.map.addLayer(currentViewLayer);
                    currentViewLayer.events.register('loadend', currentViewLayer, clientLayerLoadEndCallback);
                }
            }

            this.clientLayersKml = clientLayers;
        }

        // 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++) {
                if (layers[i]["@id"] == searchResultLayerId) {
                    if (clientLayers)
                        this.addLayer(layers[i], clientLayers.searchLayerKml);
                    else
                        this.addLayer(layers[i]);
                }
                else
                    this.addLayer(layers[i]);
            }
        }

        // Add hover control for to be used for KML layers
        var kmlLayers = [];
        for (var i = 0; i < this.map.layers.length; i++) {
            if (this.map.layers[i].CLASS_NAME == "OpenLayers.Layer.Vector" && this.map.layers[i].protocol != null &&
        		this.map.layers[i].protocol.CLASS_NAME == "OpenLayers.Protocol.HTTP" && this.map.layers[i].protocol.format.CLASS_NAME == "OpenLayers.Format.KML") {

                kmlLayers.push(this.map.layers[i]);
            }
        }
        this.setupHoverControl(kmlLayers);

        // Zoom to all of denmark
        this.map.fractionalZoom = false;

        try {
            this.map.zoomToExtent(this.startExt, true);
        } catch (Exception) {
            //this exception occur, when setting exten on google maps layer
            // however, this function DO have effect!
            // Because of this, the exception is ignored
        }

        // Activate infobox
        if (activateInfoBox) {
            infoBox.activate();
        }
        this.infoBox = infoBox;
    },


    /**
    * 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:4326'), pLeftBottomConverted);

        var pRightTopConverted = new Proj4js.Point(bounds.right, bounds.top);
        Proj4js.transform(new Proj4js.Proj('EPSG:25832'), new Proj4js.Proj('EPSG:4326'), pRightTopConverted);
        return new OpenLayers.Bounds(pLeftBottomConverted.x, pLeftBottomConverted.y, pRightTopConverted.x, pRightTopConverted.y);
    },

    /**
    * 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, clientKml) 
    {
        var layer, selectControl;
      
        if (layerConfig["@type"] == "WMS") {
            if (this.applicationModeGlobal)
                layerConfig.options['getFullRequestString'] = this.wmsCoordinateTransform;
            layer = new OpenLayers.Layer.WMS(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
            layer.strategies = new OpenLayers.Strategy.BBOX();
        }

        if (layerConfig["@type"] == "WMS.Untiled") {
            if (this.applicationModeGlobal)
                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") {
            if (clientKml)
                layer = new OpenLayers.Layer.KML(layerConfig.name, null, layerConfig.options, this.internalprojection, clientKml);
            else
                layer = new OpenLayers.Layer.KML(layerConfig.name, layerConfig.url, layerConfig.options, this.internalprojection);
        }
        else if (layerConfig["@type"] == "WMTS") {

            layerConfig.options.tileSize = new OpenLayers.Size(256, 256);
            layerConfig.options.tileOrigin = new OpenLayers.LonLat(this.maxExt.left, this.maxExt.top);
            layerConfig.options.tileFullExtent = this.maxExt.clone();
            //layer = new OpenLayers.Layer.WMTS(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
            for (var opts in layerConfig.options) {
                layerConfig[opts] = layerConfig.options[opts];
            }
            for (var par in layerConfig.params) {
                layerConfig[par] = layerConfig.params[par];
            }
            if (layerConfig["matrixIds"] != null) {
                if (layerConfig["matrixIds"].indexOf(',') > 0) {
                    layerConfig["matrixIds"] = layerConfig["matrixIds"].split(',');
                }
            }

            layer = new OpenLayers.Layer.WMTS(layerConfig);
        }
        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"] == "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_REST") {
            //layerConfig.options['getFullRequestString'] = this.agsCoordinateTransform;
            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"] == "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"] == "BBOXKML") {
            layer = new OpenLayers.Layer.Vector(layerConfig.name, OpenLayers.Util.extend(layerConfig.options, {
                projection: this.internalProjection,
                strategies: [new OpenLayers.Strategy.BBOX()],
                protocol: new OpenLayers.Protocol.HTTP({
                    url: layerConfig.url,
                    format: new OpenLayers.Format.KML()
                }),
                styleMap: new OpenLayers.StyleMap({
                    "default": new OpenLayers.Style({
                        externalGraphic: layerConfig.options.extra.legend.src,
                        graphicWidth: 12,
                        graphicHeight: 12
                    })
                })
            }));
        }
        else if (layerConfig["@type"] == "OSM" || layerConfig["@type"] == "OPEN_STREET_MAPS") {
            layer = new OpenLayers.Layer.TMS(layerConfig.name, layerConfig.url, OpenLayers.Util.extend(layerConfig.options, {
                type: 'png', getURL: this.osm_getTileURL,
                displayOutsideMaxExtent: true,
                attribution: '<a href="http://www.openstreetmap.org/">OpenStreetMap</a>',
                units: "m"
            }));
        }
        else if (layerConfig["@type"] == "GMAPS" || layerConfig["@type"] == "G_NORMAL_MAP") {
            //layer = new OpenLayers.Layer.Google(layerConfig.name, layerConfig.options);
            layer = new OpenLayers.Layer.Google(layerConfig.name, OpenLayers.Util.extend(layerConfig.options, { type: G_NORMAL_MAP, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") }));
        }
        else if (layerConfig["@type"] == "GSAT" || layerConfig["@type"] == "G_SATELLITE_MAP") {
            layer = new OpenLayers.Layer.Google(layerConfig.name, OpenLayers.Util.extend(layerConfig.options, { type: G_SATELLITE_MAP, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") }));
        }
        else if (layerConfig["@type"] == "GHYP" || layerConfig["@type"] == "G_HYBRID_MAP") {
            layer = new OpenLayers.Layer.Google(layerConfig.name, OpenLayers.Util.extend(layerConfig.options, { type: G_HYBRID_MAP, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") }))
        }
        else if (layerConfig["@type"] == "G_PHYSICAL_MAP") {
            layer = new OpenLayers.Layer.Google(layerConfig.name, OpenLayers.Util.extend(layerConfig.options, { type: G_PHYSICAL_MAP, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") }));

        }

        layer.addOptions({ mapConfigId: layerConfig["@id"] });

        this.map.addLayer(layer);

        if (layer.isBaseLayer && layerConfig.options && layerConfig.options.visibility) {
            this.map.setBaseLayer(layer);
        }
    },

    /**
    * 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) {
                    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 currentstate;

        if (this.applicationModeGlobal) {
            var pConverted = viskort.convertPoint(new OpenLayers.Geometry.Point(this.map.getCenter().lon, this.map.getCenter().lat), "EPSG:102113", "EPSG:25832");
            currentstate = "PanToEasting=" + pConverted.x + "&PanToNorthing=" + pConverted.y + "&ZoomLevel=" + Math.round(this.map.getZoom());
        }
        else {
            currentstate = "PanToEasting=" + this.map.getCenter().lon + "&PanToNorthing=" + this.map.getCenter().lat + "&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,status=no,width=850,height=550,resize=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=900,resize=yes', false);
    },

    setupHoverControl: function (kmlLayers) 
    {

        var hover = new OpenLayers.Control.GetFeatures(kmlLayers, { clickTolerance: new OpenLayers.Size(12, 12) });
        hover.events.on({ "featuresretrieved": this.hoverInfo, "scope": this });
        this.map.addControl(hover);
        hover.activate();
    },

    hoverInfo: function (evt) 
    {

        infoBox.showCombinedInfo(evt);
        OpenLayers.Event.stop(evt);
    },

    mapMoveZoomEvent: function (evt) 
    {
        var currentExtent = this.map.getExtent();
        var currentExtentWGS84 = currentExtent.transform(new OpenLayers.Projection(this.internalprojection), new OpenLayers.Projection("EPSG:4326"));
        window.parent.viskortMapPositionChanged(currentExtent.toArray(), currentExtentWGS84.toArray());
    },

    //	hoverInfo: function(evt) {
    //		var lonlat = evt.lonlat;
    //		var features = evt.features;
    //		if (features) lonlat = features[0].geometry.bounds.getCenterLonLat();

    //		var html = "";
    //		for (var i = 0; i < features.length; i++) {
    //			html += "<p class='" + this.CLASS_NAME.replace(/\./g, "") + "Item" + "'><b>" + features[i].layer.extra.infoBoxTitle + "</b><br /><br />" + features[i].data.name + "<br />" + features[i].data.description + "</p>";
    //			if (i != features.length - 1) html += "<hr>";
    //		}

    //		infoBox.showCombinedInfo(lonlat, html);

    //		OpenLayers.Event.stop(evt);
    //	},

    /**
    * Method: reverseConvertPoint
    * Method to convert point in destination coordinate system into source coordinate system
    *
    * Parameters:
    * point - <OpenLayers.Geometry.Point>  point to convert
    * sourceProjection - <string>  EPSG projection
    * destinationProjection - <string>  EPSG projection
    *
    * Returns:
    * point <OpenLayers.Geometry.Point> converted point
    */
    convertPoint: function (point, sourceProjection, destinationProjection) 
    {
        // transforming point coordinates
        var pConverted = new Proj4js.Point(point.x, point.y);
        Proj4js.transform(new Proj4js.Proj(sourceProjection), new Proj4js.Proj(destinationProjection), pConverted);

        return new OpenLayers.Geometry.Point(pConverted.x, pConverted.y);
    },

    /**
    * Method: convertLonLat
    * Method to convert lonlat from sourceprojection into destinationprojection
    *
    * Parameters:
    * lonlat - <OpenLayers.LonLat>  lonlat to convert
    * sourceProjection - <string>  EPSG projection
    * destinationProjection - <string>  EPSG projection
    *
    * Returns:
    * lonlat <OpenLayers.LonLat> converted lonlat
    */
    convertLonLat: function (lonlat, sourceProjection, destinationProjection) 
    {
        // transforming point coordinates
        var pConverted = new Proj4js.Point(lonlat.lon, lonlat.lat);
        Proj4js.transform(new Proj4js.Proj(sourceProjection), new Proj4js.Proj(destinationProjection), pConverted);

        return new OpenLayers.LonLat(pConverted.x, pConverted.y);
    },


    /**
    * 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: agsCoordinateTransform
    * Method to transform WMS layers into EPSG:4326 projection. 
    *
    * Parameters:
    * newParams - <String> newParams for WMS layer
    * altUrl - altUrl option
    */
    agsCoordinateTransform: 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:4326'), new Proj4js.Proj('EPSG:102113'), pMinConverted);

                var pMaxConverted = new Proj4js.Point(xMax, yMax);
                Proj4js.transform(new Proj4js.Proj('EPSG:4326'), new Proj4js.Proj('EPSG:102113'), pMaxConverted);

                // store reprojected coordinates
                allParams[key] = pMinConverted.x + "," + pMinConverted.y + "," + pMaxConverted.x + "," + pMaxConverted.y;
            }
        }
        var paramsString = OpenLayers.Util.getParameterString(allParams);
        if (url instanceof Array) { url = this.selectUrl(paramsString, url) };
        return url + "?" + paramsString;
    },

    /**
    * Method: get_gdk_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;
    },

    CLASS_NAME: "VisKort.Application"
});
