/*
 Class: VisStedet
 VisStedet is the scope for all objects provided by the Vis Stedet project. Many of the objects in here are
 extensions and/or specializations of OpenLayers or Scriptaculous objects.
 The core javascript file also includes utility functions, typically to initialize the standard components with reasonable defaults,
 but these are strictly speaking not a part of the VisStedet scope.
 */
VisStedet = {};

if(typeof Autocompleter!='undefined')
{
/*
 Class: VisStedet.JSONDropdown

 Extension of the Scriptaculous Autocompleter class, adding GeoEvent listener functionality hooked to the JSON objects.
 This is both a fully functional standard component (AJAX dropdown autocompletion of a JSON gazetteer search)
 and a practical example of extending and customizing more generic components to provide a specific service within the GeoEvent model.
 See the get...Dropdown functions for examples on how to wire this component to specific JSON services.
 */
VisStedet.JSONDropdown = Class.create();
Object.extend(Object.extend(VisStedet.JSONDropdown.prototype, Autocompleter.Base.prototype),
{
    listeners: [],
    foundElements: null,
    url: null,
    paramName: null,
    
    /*
     Constructor: VisStedet.JSONDropdown
     Creates an object to handle the async communication to a generic JSON search service.
     
     Parameters:
     url - {String} The URL to the JSON service. This can potentially include URL parameters, for example switches to the specific service.
     paramName - {String} The URL parameter name used when sending the input field to the JSON service.
     element - {String} The HTML element name this object will use when showing the results.
     update - {String} The HTML element name of the DIV tag which will be filled out with the selectable options. By convention in Vis Stedet, this should be the same as element, postfixed with '_choices'.
     options - {Object} An optional object with additional configuration properties. This will typically be the number of characters required before the JSON communication triggers, and the HTML element to function as the "working" indicator.
     */
    initialize: function(url, paramName, element, update, options)
    {
        this.baseInitialize(element, update, options);
        //this.options.asynchronous  = true;
        this.options.onComplete    = this.onSuccess.bind(this);
        this.options.afterUpdateElement = this.fireListeners.bind(this);
        this.url = url;
        this.paramName = paramName;
    },

    getUpdatedChoices: function()
    {
        this.startIndicator();
        this.options.method = 'get';
        this.options.parameters = new Hash();
        this.options.parameters.set(this.paramName, this.getToken());
        new Ajax.Request(this.url, this.options);
	},

	onSuccess: function(transport)
	{
        var elements = transport.responseText.evalJSON();
        if(elements)
        {
            this.foundElements = elements;
            var choices = '<ul>';
            for (var i=0; i<elements.length; i++)
            {
                choices += '<li>' + elements[i].displayName + '</li>';
            }
            choices += '</ul>';
            this.updateChoices(choices);
        }
    },
    
    addListener: function(listenerCallback)
    {
        this.listeners.push(listenerCallback);
    },
    
    fireListeners: function(inputBox, selectedListItem)
    {
        for (var i=0;i<this.listeners.length;i++)
        {
            this.listeners[i](this.foundElements[selectedListItem.autocompleteIndex]);
        }
    }
});
}

/*
 Class: VisStedet.JSONFind
 Non-visual class which contacts an AJAX service, firing a geoevent when a best-case hit is found.
 */
VisStedet.JSONFind = Class.create();
Object.extend(VisStedet.JSONFind.prototype, 
{
    listeners: [],
    url: null,
    displayElement: null,
    running: 0,
    
    initialize: function(urlString, tag)
    {
        this.fireListeners.bind(this);
        this.url = urlString;
        this.displayElement = $(tag);
    },
    
    addListener: function(listenerCallback)
    {
        this.listeners.push(listenerCallback);
    },
    
    find: function(input)
    {
        if (input == null || input.length < 4) return;
        if (this.displayElement != null)
        {
            this.running ++;
            this.displayElement.show();
        }
        new Ajax.Request(this.url+"&input=" + escape(input), {
          method: 'get',
          onSuccess: this.fireListeners.bind(this),
          onFailure: function(transport) {
            alert("Ajax error: " + transport.responseText);
          },
          onComplete: this.hideWait.bind(this)
        });
    },
    
    hideWait: function(transport)
    {
        if (this.displayElement != null)
        {
            this.running --;
            if (this.running <=0) this.displayElement.hide();
        }
    },
    
    fireListeners: function(transport)
    {
        var address = transport.responseText.evalJSON();
        for (var i=0;i<this.listeners.length;i++)
        {
            this.listeners[i](address);
        }
    }
});

 
if(typeof OpenLayers!='undefined')
{
/*
 Class: VisStedet.Map
 An extension to the OpenLayers.Map object to make it understand geoEvents and handle clickable layers implicitly.
 This is a drop-in replacement for the OpenLayers.Map object.
 */
VisStedet.Map = Class.create();
Object.extend(Object.extend(VisStedet.Map.prototype, OpenLayers.Map.prototype), 
{
    clickListeners: [],
    movedListeners: [],
    
    initialize: function (div, options) {
        var newArguments = [];
        newArguments.push(div, options);
        OpenLayers.Map.prototype.initialize.apply(this, newArguments);
        this.events.register("addlayer", this, this.layerAdded.bind(this));
        this.fractionalZoom = false;
    },

    /*
     Method: setCenterPoint
     Essentially a wrapper around the setCenter function, making it accept the coordinates embedded in a GeoEvent, rather than as an OpenLayers.LonLat object.
     This eases the use, since this function can be hooked up directly as a listener to any object producing GeoEvents, making the map react automatically,
     centering on the raised GeoEvent.

     Parameters:
     geoEvent - {GeoEvent} A standard GeoEvent, assumed to have an x and y field.
     */
    setCenterPoint: function(geoEvent)
    {
    	var pConverted = viskort.convertPoint(new OpenLayers.Geometry.Point(geoEvent.x, geoEvent.y));
        this.panTo(new OpenLayers.LonLat(pConverted.x, pConverted.y));
    },
    
    pointChanged: function(geoEvent)
    {
        this.setCenterPoint(geoEvent);
    },
        
    setBox: function(geoEvent)
    {
        this.zoomToExtent(new OpenLayers.Bounds(geoEvent.minX, geoEvent.minY, geoEvent.maxX, geoEvent.maxY));
    	return boundsToGeoEvent(this.getExtent(), "Bounding box");
    },
    
    /*
     Method: addClickListener
     Sets up callback functions to be called when the map generates a point GeoEvent; i.e. when somebody clicks on it.
     
     Parameters:
     listenerCallback - {function} Will be called with a GeoEvent object containing diplayName, x and y.
     */
    addClickListener: function(listenerCallback)
    {
        if (this.clickListeners.length == 0)
        {
            this.events.register("click", this, this.fireClickListeners.bind(this));
        }
        this.clickListeners.push(listenerCallback);
    },
    
    addPointListener: function(listenerCallback)
    {
        this.addClickListener(listenerCallback);
    },
    
    addMovedListener: function(listenerCallback)
    {
    	if(this.movedListeners.length ==  0)
    	{
	        this.events.register("moveend", this, this.fireMovedListeners.bind(this));
    	}
        this.movedListeners.push(listenerCallback);
    },
        
    layerAdded: function(openLayerEvent)
    {
        // This allows the map to automatically recognize that an newly added layer carries a control object, and install and activate it.
    
        // The 2.5 release does not send the layer object as property of the event; only the map itself.
        // This code will only work from 2.6 onwards, with it's more elegant event model.
        if (typeof(openLayerEvent.layer.control) != 'undefined')
        {
            this.addControl(openLayerEvent.layer.control);
            openLayerEvent.layer.control.activate();
        }
    },
    
    fireClickListeners: function(openLayerEvent)
    {
        var lonlat = this.getLonLatFromViewPortPx(openLayerEvent.xy);
        geoEvent =
        {
            displayName: "Point",
            x: lonlat.lon,
            y: lonlat.lat
        }
        for (var i=0;i<this.clickListeners.length;i++)
        {
            this.clickListeners[i](geoEvent);
        }
    },
    
    fireMovedListeners: function(openLayerEvent)
    {
        geoEvent = boundsToGeoEvent(openLayerEvent.object.getExtent());
        for (var i=0;i<this.movedListeners.length;i++)
        {
            this.movedListeners[i](geoEvent);
        }
    }
});

/*
 Class: VisStedet.AtomFeatureControl
 
 This class is handler of interaction with a GeoAtom layer. The GeoAtom layer will automatically install it in the map when shown,
 and as such, it should not be necessary to care about thise class in normal use. It is possible, however, that an application will need
 to override this class to provide it's own, customized behaviour.
 The handler is not strictly speaking tied to an Atom feed, but rather to a "vector popup", but since they are always, in Vis Stedet context, seeded from GeoAtom, the naming here remains.
 */
VisStedet.AtomFeatureControl = OpenLayers.Class(OpenLayers.Control, {
    
    multipleKey: null,
    toggleKey: null,
    multiple: false, 
    clickout: true,
    toggle: false,
    hover: false,
    onSelect: function() {},
    onUnselect: function() {},
    geometryTypes: null,
    layer: null,
    callbacks: null,
    selectStyle: null,
    renderIntent: "select",
    //renderIntent: "temporary",
    handler: null,
    previousHover: null,
    listeners: [],

    initialize: function(layer, options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.layer = layer;
        this.callbacks = OpenLayers.Util.extend({
                                                  click: this.clickFeature,
                                                  clickout: this.clickoutFeature,
                                                  over: this.overFeature,
                                                  out: this.outFeature
                                                }, this.callbacks);
        var handlerOptions = { geometryTypes: this.geometryTypes};
        this.handler = new OpenLayers.Handler.Feature(this, layer,
                                                      this.callbacks,
                                                      handlerOptions);
    },

    unselectAll: function(options) {
        // we'll want an option to supress notification here
        var feature;
        for(var i=this.layer.selectedFeatures.length-1; i>=0; --i) {
            feature = this.layer.selectedFeatures[i];
            if(!options || options.except != feature) {
                this.unselect(feature);
            }
        }
    },

    clickFeature: function(feature) {
        /*if(!this.hover)*/ {
            var selected = (OpenLayers.Util.indexOf(this.layer.selectedFeatures,
                                                    feature) > -1);
            if(selected) {
                if(this.toggleSelect()) {
                    this.unselect(feature);
                } else if(!this.multipleSelect()) {
                    this.unselectAll({except: feature});
                }
            } else {
                if(!this.multipleSelect()) {
                    this.unselectAll({except: feature});
                }
                this.select(feature);
                // Fire the VisStedet-specific listeners, which should only fire on a click-select, not a mouseover-select:
                for (var i=0;i<this.listeners.length;i++)
                {
                    var e = {
                        'displayName' : feature.attributes.title,
                        'title' : feature.attributes.title,
                        'summary' : feature.attributes.summary,
                        'link' : feature.attributes.link,
                        'id' : feature.fid,
                        'x' : ((feature.geometry.bounds.left + feature.geometry.bounds.right)/2),
                        'y' : ((feature.geometry.bounds.top + feature.geometry.bounds.bottom)/2)
                    }
                
                    this.listeners[i](e);
                }
            }
        }
    },

    multipleSelect: function() {
        return this.multiple || this.handler.evt[this.multipleKey];
    },
    
    toggleSelect: function() {
        return this.toggle || this.handler.evt[this.toggleKey];
    },

    clickoutFeature: function(feature) {
        if(/*!this.hover &&*/ this.clickout) {
            this.unselectAll();
        }
    },

    overFeature: function(feature) {
        if (feature != this.previousHover)
        {
            if (this.previousHover)
            {
                if(OpenLayers.Util.indexOf(this.layer.selectedFeatures, this.previousHover) == -1)
                    this.layer.drawFeature(this.previousHover, "default");
                else
                    this.layer.drawFeature(this.previousHover, "select");
            }
        }
        this.layer.drawFeature(feature, 'temporary');
        this.previousHover = feature;
        this.onSelect(feature);
        
        return;
        if(/*this.hover &&*/
           (OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) == -1))
        {
            this.select(feature, 'temporary');
            this.previousHover = feature;
        }
    },

    outFeature: function(feature) {
    /*
    if(OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) > -1)
        this.layer.drawFeature(feature, "select");
    else
        this.layer.drawFeature(feature, "default");
        */
        /*if(this.hover)*/ {
            //this.unselect(feature);
        }
    },
    
    select: function(feature, style) {
        this.layer.selectedFeatures.push(feature);

        var selectStyle = style || this.selectStyle || this.renderIntent;
        
        this.layer.drawFeature(feature, selectStyle);
        this.layer.events.triggerEvent("featureselected", {feature: feature});
        this.onSelect(feature);
    },

    unselect: function(feature) {
        // Store feature style for restoration later
        this.layer.drawFeature(feature, "default");
        OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);
        this.layer.events.triggerEvent("featureunselected", {feature: feature});
        this.onUnselect(feature);
    },

    setMap: function(map) {
        this.handler.setMap(map);
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },

    /*
     Method: addListener
     Sets up callback functions to be called when a feature is clicked. As per this entire class being largely an internal part, it is generally not necessary (nor recommended) to access this function directly.
     The PopupVector layer contains functions to handle the events (which internally uses this function).
     
     Parameters:
     listenerCallback - {function} Will be called with a GeoEvent representing the clicked-on feature, containing displayName, title, summary, link, id, x and y.
     */
    addListener: function(listenerCallback)
    {
        this.listeners.push(listenerCallback);
    }
});

/*
 Extension of the basic vector layer to provide pop-up bubbles. It relies on the VisStedet.AtomFeatureControl handler to track the interaction.
 */
VisStedet.PopupVector = OpenLayers.Class(OpenLayers.Layer.Vector, {

    listeners: [],
    control: null,
    previousFeature: null,
    url: null,
    loaded: false,
    format: null,

    /*
     Constructor: VisStedet.PopupVector
     This laregly reflects the OpenLayers.GML class, in the sense that it's an "external file handler" for the Vector layer class.
     However, it defaults to the GeoAtom format rather than GML, and adds the Vis Stedet event model and pop-ups.
     
     Parameters:
     name - {String} 
     url - {String} URL of an Atom file.
     options - {Object} Hashtable of extra options to tag onto the layer.
     */
     initialize: function(name, url, options) {
        var newArguments = [];
        newArguments.push(name, options);
        OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
        this.url = url;

        this.control = new VisStedet.AtomFeatureControl(this, {onSelect: this.onFeatureSelect, hover: true});
        
        //this.control = new OpenLayers.Control.SelectFeature(this, {onSelect: this.onFeatureSelect, hover: true});
    },
       
    setVisibility: function(visibility, noEvent) {
        OpenLayers.Layer.Vector.prototype.setVisibility.apply(this, arguments);
        if(this.visibility && !this.loaded){
            this.load();
        }
    },
    
    moveTo: function(bounds, zoomChanged, minor) {
        OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
        if(this.visibility && !this.loaded){
            this.events.triggerEvent("loadstart");
            this.load();
        }
    },

    load: function() {
        if (!this.loaded) {
            var results = OpenLayers.loadURL(this.url, null, this, this.requestSuccess, this.requestFailure);
            this.loaded = true;
        }
    }, 

    requestSuccess: function(request) {
        var doc = request.responseXML;
        
        if (!doc || request.fileType!="XML") {
            doc = request.responseText;
        }

        var feed = this.format ? new this.format() : new VisStedet.GeoAtom();
        this.addFeatures(feed.read(doc));
        this.events.triggerEvent("loadend");
    },
    
    requestFailure: function(request) {
        alert("Error in loading feed file "+this.url);
        this.events.triggerEvent("loadend");
    },

    onFeatureSelect: function(feature)
    {
        if (this.previousFeature != null)
        {
            this.map.removePopup(this.previousFeature.popup);
            this.previousFeature.popup.destroy();
            this.previousFeature.popup = null;
            this.previousFeature = null;
        }
        
        var popup = new OpenLayers.Popup.AnchoredBubble("id", 
            feature.geometry.getBounds().getCenterLonLat(),
            null,
            '<b>' + feature.attributes.title + '</b><br/>' + feature.attributes.summary,
            null, true);
        popup.setOpacity(.9);
        feature.popup = popup;
        this.map.addPopup(popup);
        this.previousFeature = feature;
    },
    
    /*
     Method: addListener
     Sets up callback functions to be called when a feature shown in this layer is clicked.
     
     Parameters:
     listenerCallback - {function} Will be called with a GeoEvent representing the clicked-on feature, containing displayName, title, summary, link, id, x and y.
     */
    addListener: function(listenerCallback)
    {
        // Pipe the listener to the actual feature control, which is where we can handle the detailed click differentiation:
        this.control.addListener(listenerCallback);
    }

});

/*
 Class: VisStedet.GeoAtom
 
 A parser for the GeoAtom feed. It is usually not necessary to use this class directly, since it will internally be used by default by the PopupVector layer class.
 */
VisStedet.GeoAtom = OpenLayers.Class(OpenLayers.Format.XML, {
    
    featureTitle: "Untitled",
    
    featureDescription: "No Description",
    
    /*
     * Constructor: VisStedet.GeoAtom
     * Create a new parser for GeoAtom.
     *
     * Parameters:
     * options - {Object} An optional object whose properties will be set on
     *     this instance.
     */
    initialize: function(options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    
    /**
     * createGeometryFromItem
     * Return a geometry from a GeoRSS Item.
     *
     * InternalParameters:
     * item - {DOMElement} A GeoRSS item node or Atom entry
     *
     * InternalReturns:
     * {<OpenLayers.Geometry>} A geometry representing the node.
     */
    createGeometryFromItem: function(item) {
    
        var point = this.getElementsByTagNameNS(item, '*', 'Point');
        var line = this.getElementsByTagNameNS(item, '*', 'LineString');
        var polygon = this.getElementsByTagNameNS(item, '*', 'Polygon');
        
        var coords = this.getElementsByTagNameNS(item, '*', 'posList');
        if (coords.length == 0) coords = this.getElementsByTagNameNS(item, '*', 'coordinates');
        if (coords.length == 0) coords = this.getElementsByTagNameNS(item, '*', 'coordList');
        if (coords.length == 0) coords = this.getElementsByTagNameNS(item, '*', 'pos');

        var coordList = OpenLayers.String.trim(coords[0].firstChild.nodeValue).split(/[\s+,]/);
        
        var components = []; 
        for (var i=0; i < coordList.length; i+=2) {
            var point = new OpenLayers.Geometry.Point(parseFloat(coordList[i]), parseFloat(coordList[i+1]));
            components.push(point);
        }
        
        if (polygon.length > 0) return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);            
        if (line.length > 0) return new OpenLayers.Geometry.LineString(components);            
        if (point.length > 0) return new OpenLayers.Geometry.Point(components[0]);            
        
        alert('No recognized GML element in entry');
        return;        
    },        

    /**
     * createFeatureFromItem
     * Return a feature from a GeoRSS Item.
     *
     * Parameters:
     * item - {DOMElement} A GeoRSS item node.
     *
     * Returns:
     * {<OpenLayers.Feature.Vector>} A feature representing the item.
     */
    createFeatureFromItem: function(item) {
    
        var geometry = this.createGeometryFromItem(item);
        
        /* Provide defaults for title and description */
        var title = this.getChildValue(item, "*", "title", this.featureTitle);
       
        /* First try RSS descriptions, then Atom summaries */
        var summary = this.getChildValue(
            item, "*", "description",
            this.getChildValue(item, "*", "summary", this.featureDescription)
        );

        /* If no link URL is found in the first child node, try the
           href attribute */
        var link = this.getChildValue(item, "*", "link");
        if(!link) {
            try {
                link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href");
            } catch(e) {
                link = null;
            }
        }

        var id = this.getChildValue(item, "*", "id", null);
        var data = {
            "title": title,
            "summary": summary,
            "link": link
        };
        var feature = new OpenLayers.Feature.Vector(geometry, data);
        feature.fid = id;
        return feature;
    },        
    
    /*
     * getChildValue
     *
     * Parameters:
     * node - {DOMElement}
     * nsuri - {String} Child node namespace uri ("*" for any).
     * name - {String} Child node name.
     * def - {String} Optional string default to return if no child found.
     *
     * Returns:
     * {String} The value of the first child with the given tag name.  Returns
     *     default value or empty string if none found.
     */
    getChildValue: function(node, nsuri, name, def) {
        var value;
        try {
            value = this.getElementsByTagNameNS(node, nsuri, name)[0].firstChild.nodeValue;
        } catch(e) {
            value = (def == undefined) ? "" : def;
        }
        return value;
    },
    
    /*
     Method: read
     Return a list of features from a GeoAtom document.
     
     Parameters:
     data - {Element} 
     
     Returns:
     An Array of <OpenLayers.Feature.Vector>s
     */
    read: function(doc) {
        if (typeof doc == "string") { 
            doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
        }

        /* Try Atom entries first, then RSS items */
        var itemlist = null;
        itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');
        if (itemlist.length == 0) {
            itemlist = this.getElementsByTagNameNS(doc, '*', 'item');
        }
        
        var numItems = itemlist.length;
        var features = new Array(numItems);
        for(var i=0; i<numItems; i++) {
            features[i] = this.createFeatureFromItem(itemlist[i]);
        }
        return features;
    }
});
}

/*
 Class: Helper
 */
 /*
 Method: showObject
 A debug helper function to show the fields of an object.
 
 Parameters:
 obj - {Object} The object to display in an alert box.
 */
function showObject(obj)
{
    var result = "";
    for (var prop in obj)
    {
        if (typeof(obj[prop]) != 'function')
            result += typeof obj + "." + prop + " = " + obj[prop] + " (" + typeof(obj[prop])+")\n";
    }
    alert(result);
}

function boundsToGeoEvent(bounds, displayName)
{
		var box = bounds.toArray();
		var center = bounds.getCenterLonLat();
    	return {
    		displayName: displayName,
    		minX: box[0],
    		minY: box[1],
    		maxX: box[2],
    		maxY: box[3],
    		x: center.lon,
    		y: center.lat
    	}
}

/*
 Class: Cookie
 Convenience functions to handle cookies. Why this is not a part of the javascript core itself is a mystery.
 */
var Cookie = {
  set: function(name, value, daysToExpire) {
    var expire = '';
    if (daysToExpire != undefined) {
      var d = new Date();
      d.setTime(d.getTime() + (86400000 * parseFloat(daysToExpire)));
      expire = '; expires=' + d.toGMTString();
    }
    return (document.cookie = escape(name) + '=' + escape(value || '') + expire);
  },
  get: function(name) {
    var cookie = document.cookie.match(new RegExp('(^|;)\\s*' + escape(name) + '=([^;\\s]*)'));
    return (cookie ? unescape(cookie[2]) : null);
  },
  erase: function(name) {
    var cookie = Cookie.get(name) || true;
    Cookie.set(name, '', -1);
    return cookie;
  },
  accept: function() {
    if (typeof navigator.cookieEnabled == 'boolean') {
      return navigator.cookieEnabled;
    }
    Cookie.set('_test', '1');
    return (Cookie.erase('_test') === '1');
  }
};


/*
 Class: Defaults
 
 In principle, utility functions to ease configuration of the "actual" functionality. Practically, the easy access to reasonable defaults is
 a large part of Vis Stedet, and these functions also demonstrates "best practice" to use the components.
 */
 
/*
 Method: getDropdown
 Call this to wire the input and div tags for a place search to actual AJAX functionality.
 This configures the generic JSON dropdown functionality to use the specific JSON service.
 
 Parameters:
 elementID - {String} The DOM ID of the input tag.
                     It is assumed that a corresponding 'elementID_choices' is available in the HTML
                     to show the list of choices.
                     If an 'elementID_indicator' is available in the HTML, it's display property will be toggled
                     on and off as the async AJAX call is running and returning.
 minChars  {Integer} The minimum number of chars to type before the AJAX request is fired. Defaults to 4 if not given.
 
 Returns:
 JSONDropdown - A bound instance of a VisStedet.JSONDropdown, to hook event listener functions into.  
 */
function getDropdown(elementId, url, minChars)
{
	var params = {};
    if (minChars != null )
        params.minChars  = minChars;
    else
        params.minChars  = 4;
	if ($(elementId + '_indicator') != null) params.indicator = elementId + '_indicator';
    return new VisStedet.JSONDropdown(url, 'input', elementId, elementId + '_choices', params);
}

/*
 Method: getPlaceGazetteerDropdown
 This is an internal helper function, preserved for ease of legacy support.
 */
function getPlaceGazetteerDropdown(elementId, minChars)
{
    return getDropdown(elementId, "/Proxy.aspx?path=GetPlaceJSON.aspx", minChars);
}

/*
 Method: getAddressDropdown
 Another internal legacy support.
 */
function getAddressDropdown(elementId, minChars)
{
    return getDropdown(elementId, '/Proxy.aspx?path=GetAddressJSON.aspx&wildcard=true', minChars);
}

function getAWSFind()
{
   return new VisStedet.JSONFind("/Proxy.aspx?path=FindAddressJSON.aspx");
}

/*
 Method: getDefaultMap
 Sets up a default map. This is a conveniencem method to get something on the screen quickly,
 and is quite likely to be replaced by customized initialization methods.
 
 elementID - {String} The DOM ID of the div tag to contain the map.
 showPostDistricts - {boolean} Whether to show the post districts overlay or not.
 
 Return:
 OpenLayers.Map - An instantiated OpenLayers object bound to the given div tag.
 */
function getDefaultMap(elementId, showPostDistricts, login, password)
{
    var map = new VisStedet.Map(
        elementId,
        {
            projection: 'EPSG:25832',
            units: 'Meters',
            maxExtent: new OpenLayers.Bounds(100000,6000000,1000000,6400000),
            resolutions: new Array(.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 500, 1000),
            controls: []
        }
    );

    map.addLayer(
        new OpenLayers.Layer.WMS(
            "DTK/Skærmkort", 
            "http://kortforsyningen.kms.dk/",
            {
                servicename: 'topo_skaermkort',
		        layers: '0,1,2,3,4,5,6,7,8,9,10,11,12,13,14',
                //servicename: 'dtk_skaermkort',
                //layers: 'dtk_skaermkort',
                format: 'image/jpeg',
                ticket: Cookie.get('kfticket')
             },
             {
                isBaseLayer: true,
                buffer: 1,
                displayInLayerSwitcher: true
             }
        )
    );
    
    if(showPostDistricts)
    {
        map.addLayer(
            new OpenLayers.Layer.WMS(
                "Postdistrikter", 
                "http://kortforsyningen.kms.dk/",
                {
                    servicename: 'kms_vector_basic_01',
                    layers: 'POSTDISTRIKT,POSTDISTRIKTNAVN',
                    format: 'image/png',
                    transparent: 'true',
                    ignoreillegallayers: 'true',
                    ticket: Cookie.get('kfticket')
                 },
                 {
                    singleTile: true,
                    buffer: 0
                 }
            )
        );
    }
    
    map.addControl(new OpenLayers.Control.PanZoomBar());
    map.addControl(new OpenLayers.Control.Navigation());
    
    map.fractionalZoom = true;

    if(showPostDistricts) map.addControl(new OpenLayers.Control.LayerSwitcher());

	map.setCenter(new OpenLayers.LonLat(722110, 6178883));
	map.zoomTo(10);
	
	return map;
}



