/******************************************************************************
 *                                                                            *
 *  Copyright (C) 2008-2010 Alexander Barth <barth.alexander@gmail.com>.      *
 *                                                                            *
 *  This program is free software: you can redistribute it and/or modify      *
 *  it under the terms of the GNU Affero General Public License as published  *
 *  by the Free Software Foundation, either version 3 of the License, or      *
 *  (at your option) any later version.                                       *
 *                                                                            *
 *  This program is distributed in the hope that it will be useful,           *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *  GNU Affero General Public License for more details.                       *
 *                                                                            *
 *  You should have received a copy of the GNU Affero General Public License  *
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.     *
 *                                                                            *
 ******************************************************************************/

// TODO:
// rename 'anim' to 'ViewSection'

// disable console login if firebug is not running

if ( typeof console == "undefined" ) {
    var console = {};
    console.log = function() {};
}


var Util = {};


Util.round = function(x,nd) {
    // keep only nd significant digits of x
    var r = Math.pow(10.0,Math.floor(log10(Math.abs(x))+1-nd) );
    x = Math.round(x/r)*r;
    return x;
};


Util.removeChildNodes = function (elem) {
    while (elem.hasChildNodes()) {
        elem.removeChild(elem.lastChild);
    }
};

Util.DocumentInputs = function() {
    this.disabled_inputs = [];
    
    this.disable = function() {
        var inputs = document.getElementsByTagName("input");
        var i;

        for (i=0;i<inputs.length;i++) {
            if (!inputs[i].disabled) {
                this.disabled_inputs.push(inputs[i]);
                inputs[i].disabled = true;
            }
        }


        inputs =  document.getElementsByTagName("select");

	for (i=0;i<inputs.length;i++) {
            if (!inputs[i].disabled) {
                this.disabled_inputs.push(inputs[i]);
                inputs[i].disabled = true;
            }
        }

    };

    this.enable = function() {
        while (this.disabled_inputs.length > 0) {
            this.disabled_inputs.pop().disabled = false;
        }
    };
};

Util.query = function(s) {

    this.param = {};

    var vars = s.split("&");
    for (var i=0;i<vars.length;i++) {
        var pair = vars[i].split("=");
        //alert(pair[0]);
        this.param[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
    }

    this.get = function(kw,def) {
        if (kw in this.param) {
            return this.param[kw];
        }
        else {
            return def;
        }
    };

    this.has = function(kw) {
        return (kw in this.param);
    };
};


// return GET parameter string from the hash table param

Util.encodeparam = function(param) {                
    var parameters = "";

    for (var name in param) {
        parameters += (parameters.length !==0 ? "&" : "") + name + "=" + encodeURIComponent(param[name]);        
    }

    return parameters;
};


Util.append_param = function(url,vars) {

    if (typeof vars == 'object') {
	vars = Util.encodeparam(vars);
    }

    if (url.indexOf('?') == -1) {
	return url + '?' + vars;
    }
    else {
	return url + '&' + vars;
    }

};

Util.ajax = function(url, vars, callbackFunction,options) {
    options = options || {};
    var req_type = options.request || 'GET';
        

    // Provide the XMLHttpRequest class for IE 5.x-6.x:
    if( typeof XMLHttpRequest == "undefined" ) {
        XMLHttpRequest = function() {
            try { 
		return new ActiveXObject("Msxml2.XMLHTTP.6.0"); 
	    } 
	    catch(e) {
		try { 
		    return new ActiveXObject("Msxml2.XMLHTTP.3.0"); 
		} 
		catch(e) {
		    try { 
			return new ActiveXObject("Microsoft.XMLHTTP"); 
		    } 
		    catch(e) {
			alert( "This browser does not support XMLHttpRequest." );
		    }
		}
	    }
            return 0;
        };
    }

    var request =  new XMLHttpRequest();


    if (req_type == 'POST') {
	request.open("POST", url, true);
    }
    else {
	url = Util.append_param(url,vars);
	request.open("GET", url, true);
    }

    request.onreadystatechange = function() {
        var done = 4, ok = 200;

        if (request.readyState == done && request.status == ok) {
            if (request.responseXML) {
                callbackFunction(request.responseXML);
            }
        }
    };

    if (req_type == 'POST') {
	request.setRequestHeader("Content-Type",
				 "application/x-www-form-urlencoded");

	if (typeof vars == 'object') {
	    vars = Util.encodeparam(vars);
	}

	request.send(vars);
    }
    else {
	request.send(null);
    }
};

Util.proxy_ajax = function(url, params, obj, callback_method, options) {

    var fun = function (xmldoc) {
	console.log('obj ',obj);
	callback_method.call(obj,xmldoc);
    };

    if (options.proxy) {
	params._proxy_url=url;
	Util.ajax(options.proxy, params, fun, options);
    }
    else {
	Util.ajax(url, params, fun, options);
    }

};


Util.loadXMLDoc = function(dname) {
    var xhttp;

    if (window.XMLHttpRequest)  {
	xhttp=new XMLHttpRequest();
    }
    else {
	xhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xhttp.open("GET",dname,false);
    xhttp.send("");
    return xhttp.responseXML;
};


Util.XSLTransform = function(xml,xsl,node) {
    // code for IE
    if (window.ActiveXObject)  {
	ex=xml.transformNode(xsl);
	node.innerHTML=ex;
    }
    // code for Mozilla, Firefox, Opera, etc.
    else if (document.implementation && document.implementation.createDocument) {
	xsltProcessor=new XSLTProcessor();
	xsltProcessor.importStylesheet(xsl);
	resultDocument = xsltProcessor.transformToFragment(xml,document);
	node.appendChild(resultDocument);
    }
};


// get elements by tags. Looks also in parent node

Util.get_tags = function(elem,name,list) {
    // if list argument is not given start with empty list
    list = list || [];

    var item = elem.firstChild;
    
    while (item !== null) {
        if (item.tagName == name) {
	    list.push(item);
	}

  	item = item.nextSibling;         
    }

    // look in parent node
    if (elem.parentNode !== null) {
	list = Util.get_tags(elem.parentNode,name,list);
    }

    return list;
};


// reverse a list

Util.reverse = function(l) {
    var r = [];

    for (var i=0;i<l.length;i++) {
	r[l.length-i-1] = l[i];
    }

    return r;
};

// intersect of two lists

Util.intersect = function(l1,l2) {
    var r = [];

    for (var i=0;i<l1.length;i++) {
	for (var j=0;j<l2.length;j++) {
	    if (l1[i] === l2[j]) {
		r[r.length] = l1[i];
	    }
	}
    }

    return r;
};


Util.mjd = function(str) {
    var r = str.split('-');
    var year  = parseFloat(r[0]);
    var month = parseFloat(r[1]);
    var day   = parseFloat(r[2]);
    var t0 = Date.UTC(1858,11-1,17,0,0,0);
    var mjd = (Date.UTC(year,month-1,day,0,0,0) - t0)/(24*60*60*1000);
    return mjd;
};


Util.gregd = function(t) {
    var t0 = Date.UTC(1858,11-1,17,0,0,0);
    var d1 = new Date();
    d1.setTime(24*60*60*1000 * t + t0 );

    var str = 
	(d1.getFullYear()).toString() + "-" +
	(d1.getMonth()+101).toString().substr(1,2) + "-" +
	(d1.getDate()+100).toString().substr(1,2);

    return str;
};


Util.encodesec = function(slon,slat) {
    var section = "";
    for (var i=0; i < slon.length; i++) {
	section += slon[i] + "," + slat[i] + "|";
    }

    section = section.substring(0,section.length-1);
    return section;
};

Util.deencodesec = function(url_sec) {
    var coord = url_sec.split('%7C');

    var slon = [];
    var slat = [];

    for (i = 0; i < coord.length; i++) {
	var v = coord[i].split('%2C');
	slon.push(parseFloat(v[0]));
	slat.push(parseFloat(v[1]));
    }
    return {lon: slon, lat: slat};
};

// anim class

// derived class must provide 
// get_wms_layer()
// analysis_wms

function anim(mapid,colorbar_container,anim_button,select_time) {
    this.mapid = mapid;
    this.colorbar_container = colorbar_container;
    this.animation_running = false;
    this.animation_delay = 2000;
    //this.animation_smooth_transition = true;
    this.animation_smooth_transition = false;
    //alert('in anim' + p);

    this.anim_button = anim_button;
    this.select_time = select_time;
    this.analysis_wms_new = {};
    this._loadend = {};

    // hash table
    // each element is one layer
    this.analysis_wms = {};
    this.figprops = {};
    this.Layers = {};
    this.fields = {};
    this.ncolorbar = 0;
    this.layer_num = 0;
    
    this.downloadFormats = ['image/png', 'application/pdf', 'image/eps', 'image/svg+xml'];

}

// class methods

anim.prototype.addLayer = function(Layer) {
    var id = Layer.id;    
    this.Layers[id] = Layer;
    this.fields[id] = {};
    var obj = this;

    // default: no download button
    var download_callback = null;
    var metadata_url = Layer.metadata_url;

    if (Layer.WMS_extensions == 'OceanBrowser') {
	this.fields[id].downloadDialog = new OceanBrowser.UI.LayerDownload('download_' + id,Layer,
									   function() { 
									       return obj.getCurrentExtent();
									   },
									   this.downloadFormats);

	this.fields[id].downloadDialog.events.register('download', null, function(param) {
		console.log(param);
		obj.download(id,param);		    
	    });

	download_callback = function() {
	    obj.fields[id].downloadDialog.open();
	};
    }
    else if (Layer.WMS_extensions == 'box-select') {
	download_callback = function() {
	    // redefine call-back later
	};
    }

    if (Layer.WMS_extensions == 'box-select-wfs') {
	metadata_url = 'dummy';
    }

    var remove_callback = function() {
	// will trigger remove_layer(Layer);
	this.Layer.cap.deselectLayer(this.Layer);
    };

    // style of layer
    // add only style button if any styles are defined
    var s = Layer.get_styles();
    var add_style_button = s.length > 0;

    

    this.figprops[id] = new FigureProperties(this.mapid,this.mapid + this.layer_num,
					     Layer.wms_url,
					     //function() { obj.update(); },
					     function() { obj.update_layer(id); },
       {colorbarclass: 'colorbar2',
        colorbar_container: this.colorbar_container,
        title: Layer.title,
	Layer: Layer,
	abstract_info: Layer.abstract_info,
        metadata_url: metadata_url,
        add_style_button: add_style_button,
	download_callback: download_callback,
	colorbar_height: 50,
	colorbar_width: 150,
	//getCurrentExtent: function() { return obj.getCurrentExtent(); },
        remove_callback: remove_callback});

    if (Layer.WMS_extensions != 'OceanBrowser') {
	this.figprops[id].set_styles(s);
    }

    this.ncolorbar++;
    this.layer_num++;
};

anim.prototype.removeLayer = function(Layer) {
    var id = Layer.id;    

    this.figprops[id].remove();
    delete this.figprops[id];
    delete this.Layers[id];
    
    this.ncolorbar--;
};


anim.prototype.get_bounds = function() {
    var bounds = null;

    for (var id in this.Layers) {
	if (!bounds) {
	    bounds = this.Layers[id].get_bounds();
	}
	else {
	    bounds.extend(this.Layers[id].get_bounds());
	}
    }

    return bounds;
};

anim.prototype.getCurrentExtent = function() {
    return this.map.getExtent();
};


anim.prototype.layer_info_width = function() {
    var w = 0;
    var i = 0;

    for (var id in this.Layers) {
	w += $(this.figprops[id].layer_info_div).outerWidth(true);
        i += 1;

	// put only two-colorbars side-by-side
        if (i == 2) {
	    break;
	}
    }

    //return w;

    // width of scroll-bar
    w += 20;

    //w = Math.min(w,370);

    $(this.colorbar_container).width(w);

    return w;
};



// AnimLayer class

// derived class must provide 
// get_wms_layer()
// analysis_wms

function AnimLayer(anim_button,select_time,viewer,id) {
    this.viewer = viewer;
    this.id = id;

    this.animation_running = false;
    this.animation_delay = 2000;
    this.animation_smooth_transition = true;
    //this.animation_smooth_transition = false;
    //alert('in anim' + p);

    this.anim_button = anim_button;
    this.select_time = select_time;
    this.analysis_wms_new = null;


    var obj = this;
    this.anim_button.onclick = function() { obj.animate(); };
}


AnimLayer.prototype.animate_transition = function() { 
    opacity = this.analysis_wms_new.opacity;
	
    var opacity_inc = 0.1;
    
    if (this.nextframe === 0) {
	// hurry up!
	opacity = 1;
    } 
    else {
	// ok, we have time to make a nice transition
	opacity += opacity_inc;
    }

    this.analysis_wms_new.setOpacity(opacity);

    var obj = this;
    //console.log("op " + opacity);    
    

    if (opacity > 1-opacity_inc/2) {
	// remove previous layer
	// calling destroy instead of removeLayer helps 
	// to avoid memory leaks

	var zindex = this.viewer.analysis_wms[this.id].getZIndex();
	this.viewer.analysis_wms[this.id].destroy();


	// switch WMS layer
	this.viewer.analysis_wms[this.id] = this.analysis_wms_new;
	this.viewer.figprops[this.id].layerWMS = this.analysis_wms_new;
	this.viewer.analysis_wms[this.id].setZIndex(zindex);

	this.animate_next();
    }
    else {
        timeout = setTimeout(function () { obj.animate_transition(); },50);
    }
};


AnimLayer.prototype.animate_loadend = function() { 
    // remove event handler
    this.analysis_wms_new.events.unregister("loadend",this,this.animate_loadend);

    console.log("load end " + this.id);

    if (this.animation_running) {

	/*
    
	var num = 0;
	for (var i=0; i < this.map.layers.length; i++) {
	    num += this.map.layers[i].numLoadingTiles;
	}

	// do nothing if all maps are not ready
	if (num > 0) {
	    return;
	}

	console.log("load end all loaded " + this.id);
	*/
    
        // increment time index
        this.select_time.selectedIndex = (this.select_time.selectedIndex+1)  % this.select_time.options.length;

        // determine time needed to load this frame
        var d = new Date();                 
        // loadtime time to load frame in ms
        var loadtime = d.getTime() - this.animation_time;
        // time we have before we should show next frame
        this.nextframe = Math.max(this.animation_delay-loadtime,0);


        if (this.animation_smooth_transition) {
            this.animate_transition();
        }
        else {

	    // make visible
	    this.analysis_wms_new.setOpacity(1);
		// remove previous layer
		// calling destroy instead of removeLayer helps 
		// to avoid memory leaks
		//this.map.removeLayer(this.analysis_wms);
	    console.log("remove layer " + this.id);

	    this.viewer.analysis_wms[this.id].destroy();

		// switch WMS layer
	    this.viewer.analysis_wms[this.id] = this.analysis_wms_new;
	    this.viewer.figprops[this.id].layerWMS = this.analysis_wms_new;
            
	    //console.log("Load time " + loadtime + "; next frame at: " + this.nextframe);

	    // schedule next frame refresh
	    var obj = this;
	    timeout = setTimeout(function () { obj.animate_next(); },this.nextframe);
	    //timeout = setTimeout(function () { obj.animate_prepare_next2(); },this.nextframe);
        }
    }
    else {
        // clean-up
        this.viewer.map.removeLayer(this.analysis_wms_new);
    }
};

AnimLayer.prototype.animate_next = function() {

    if (this.animation_running) {
        var d = new Date(); 
        this.animation_time = d.getTime();

        var time_index = (this.select_time.selectedIndex+1)  % this.select_time.options.length ;
	var time = this.select_time.options[time_index].value;
	var obj = this;

	var zindex = this.viewer.analysis_wms[this.id].getZIndex();

	this.analysis_wms_new = this.viewer.get_wms_layer(this.id,{time: time});
	this.analysis_wms_new.setZIndex(zindex);

	this.analysis_wms_new.events.register("loadend",this,this.animate_loadend);

	// make invisible but will still load when added to map
	this.analysis_wms_new.setOpacity(0);
	//this.analysis_wms_new.setOpacity(1);
	this.viewer.map.addLayer(this.analysis_wms_new);


	this.analysis_wms_new.setZIndex(zindex);



        // schedule next event
        //var obj = this;
        //timeout = setTimeout(function () { obj.animate_next() },this.animation_delay);

    }
};



AnimLayer.prototype.animate = function() {
    //alert('anim');

    this.animation_running = !this.animation_running;
    
    if (this.animation_running) {
        this.anim_button.value = "Stop";
        this.animate_next();
    }
    else {
        this.anim_button.value = "Animate";
    }


};


// progress bar for OpenLayers

function MapProgressbar(id,map) {
    this.id = id;    
    this.tiles_to_load = 0;
    this.map = map;

    map.events.register("addlayer",this,this.addlayer);
    map.events.register("removelayer",this,this.removelayer);
}

// register event handler
MapProgressbar.prototype.addlayer = function(event) {
    event.layer.events.register("tileloaded",this,this.update);
};

// unregister event handler
MapProgressbar.prototype.removelayer = function(event) {
    event.layer.events.unregister("tileloaded",this,this.update);
};

// count number to tiles yet to be loaded and set progressbar accordingly
MapProgressbar.prototype.update = function() {
    var num = 0;
    var tmp;
    for (var i=0; i < this.map.layers.length; i++) {
	tmp = this.map.layers[i].numLoadingTiles;
	if (tmp !== undefined) {
	    num += tmp;
	}
    }
    console.log('tiles_to_load ',num,this.tiles_to_load);

    if (num > this.tiles_to_load) {
        this.tiles_to_load = num;
        
        // initialize progressbar
        $("#" + this.id).progressbar({ value: 0 });

    }
    else  {
        $("#" + this.id).progressbar('option','value', 100*(1-num/this.tiles_to_load) );

        if (num === 0) {
            // finish
            this.tiles_to_load = 0;
            $("#" + this.id).progressbar('destroy');
        }
    }

    //log(100*(1-num/this.tiles_to_load));
    //log(num + '/' + this.tiles_to_load);
};



// options used:
// add_style_button
// colorbar_container
// Layer

function FigureProperties(mapid,namespace,wms_url,onclose,options) {
    this.wms_url = wms_url;
    this.namespace = namespace;
    this.mapid = mapid;
    this.info = null;

    // options might be overwritten by options
    this.colorbarclass = "colorbar";
    this.add_style_button = false;
    this.colorbar_container = document.getElementById(mapid);
    this.title = null;
    this.abstract_info = null;
    this.layer_info_class = 'layer_info ui-widget-content';
    this.dimensions = {};
    this.download_callback = null;
    this.remove_callback = null;
    this.colorbar_height = 150;
    this.colorbar_width = 80;
    this.Layer = null;

    // overwrite default options
    if (options) {
	for (var p in options) {
	    this[p] = options[p];
	}
    }

    var obj = this;

    var ext = 'OceanBrowser';
    if (this.Layer) {
	ext = this.Layer.cap.WMS_extensions;
    }

    var oLabel;
    
    var dom = OceanBrowser.UI.makedom;

    this.layer_info_div = document.createElement("div");
    this.layer_info_div.className = this.layer_info_class;

    if (this.title) {
	this.layer_info_div.appendChild(dom('h3',{},[this.title]));
    }

    // add colorbar
    this.colorbar_div = dom('div',{'id': namespace + "_colorbar",
				   'class': this.colorbarclass});
    this.layer_info_div.appendChild(this.colorbar_div);


    if (this.add_style_button) {
	var button_open = dom('div',{'class': "FigurePropertiesConfig",
				     'title': "Change style of this layer",
				     'id': namespace + "config_button"});

	this.layer_info_div.appendChild(button_open);

        button_open.onclick = function () { 
          obj.open(); 
	};
    }

    // info/metadata button
    if (this.Layer || this.metadata_url) {
      this.InfoDialog = new OceanBrowser.UI.InfoLayer(this.namespace,this.Layer);
      var button_metadata = dom("div",{'class': "FigurePropertiesInfo",
				   'title': "Information about this layer",
				   'id': namespace + "config_info"});

      // metadata_url might change
      button_metadata.onclick = function () {
	  if (this.metadata_url) {
	      window.open(obj.metadata_url,'Metadata');
	  }
	  else {
	      obj.InfoDialog.open(); 
	      
	  }
      };      

      this.layer_info_div.appendChild(button_metadata);	
      this.button_metadata = button_metadata;
    }


    // download button
    if (this.download_callback) {
	var button_download = dom("div",{'class': "FigurePropertiesDownload",
					 'title': "Download this layer",
					 'id':  namespace + "config_download"});
	// this.download_callback might change
	button_download.onclick = function() { obj.download_callback(); };
	this.layer_info_div.appendChild(button_download);	
	this.button_download = button_download;
    }


    // remove button
    if (this.remove_callback) {
	var button_remove = dom('div',{'class': "FigurePropertiesRemove",
				       'title': 'Remove this layer',
				       'id':  namespace + "config_remove"});
      // this.remove_callback might change
      button_remove.onclick = function() { obj.remove_callback(); };
      this.layer_info_div.appendChild(button_remove);	
      this.button_remove = button_remove;
    }


    if (ext == 'box-select' || ext == 'box-select-wfs') {
	this.layer_info_div.appendChild(document.createElement("br"));
	this.box_select=document.createElement("input");
	this.box_select.id = namespace + "_box_select";
	this.box_select.type="checkbox";
	// IE requires to set first type then to add into DOM
	// https://connect.microsoft.com/IE/feedback/details/332453/cannot-change-an-inputs-type-after-youve-placed-it-in-the-page
	this.layer_info_div.appendChild(this.box_select);

	oLabel=this.layer_info_div.appendChild(document.createElement("label"));
	oLabel.htmlFor= namespace + "_box_select";
	oLabel.appendChild (document.createTextNode("select domain"));                	
    }

    this.colorbar_container.appendChild(this.layer_info_div);

    // Properties Dialog

    this.dialog_element = document.createElement("div");
    this.dialog_element.id = namespace + "config_dialog";
    this.dialog_element.title="Configuration";
    var oP = this.dialog_element.appendChild(document.createElement("p"));
    this.oP = oP;

    var button_reset = document.createElement("input");
    button_reset.type="button";
    button_reset.value="Reset to default";
    button_reset.id = namespace + "config_button_reset";
    this.dialog_element.appendChild(button_reset);
    button_reset.onclick = function () { 
        obj.reset();
    };


    // input element cannot be changed in IE after added to the DOM
    var button_close = document.createElement("input");
    button_close.type="button";
    button_close.value="Close";
    button_close.id = namespace + "config_button_close";
    this.dialog_element.appendChild(button_close);


    // add to DOM    
    document.body.appendChild(this.dialog_element);

    // create dialog
    $(this.dialog_element).dialog({ autoOpen: false, width: 400, height: 350, zIndex: 3999 }); 

    // events

    this.colorbar_div.onclick = function () { 
        obj.open(); 
    };

    button_close.onclick = function () { 
        $(obj.dialog_element).dialog('close'); 
        onclose();
    };

}

FigureProperties.prototype.open = function() { 
    $(this.dialog_element).dialog('open');
};

FigureProperties.prototype.set_styles = function(styles) { 
    // style
    this.styles = styles;

    //var oP = this.dialog_element.appendChild(document.createElement("p"));
    var oP = this.oP;
    var oLabel, oSelect;

    // plotting method

    oP.appendChild(document.createElement("br"));
    oLabel=oP.appendChild(document.createElement("label"));
    oLabel.htmlFor = this.namespace + "select_style";
        
    oLabel.appendChild (document.createTextNode("Style: "));
    oSelect=oP.appendChild(document.createElement("select"));
    oSelect.id = this.namespace + "select_style";
        
    oOption=oSelect.appendChild(document.createElement("option"));
    oP.appendChild(document.createElement("br"));


    var sel_style = oSelect;

    while (sel_style.hasChildNodes()) {
        sel_style.removeChild(sel_style.lastChild);
    }
                
    for (var j=0; j < styles.length; j++) {
        var option = document.createElement('option');

        option.value = styles[j].name;
        option.appendChild(document.createTextNode(styles[j].title));
        
        sel_style.appendChild(option);      
    }

};

FigureProperties.prototype.create_prop_ui = function(xmldoc) { 

    if (this.xmldoc) {
	// ignore multiple call to this function
	return;
    }

    this.xmldoc = xmldoc;
    this.style_param_id = [];
    this.style_param_id_modif = {};
    this.style_params = [];

    var params = xmldoc.getElementsByTagName("param");
    var oLabel, elem;
    var oOption;
    var obj = this;

    var oP = this.oP;
    //this.dialog_element.appendChild(oP);

    for (var i=0; i<params.length; i++) {
	var type = params[i].getAttribute('type');
	var id = params[i].getAttribute('id');
	var def = params[i].getAttribute('default');
	var label = params[i].getAttribute('label');
	var description = params[i].getAttribute('description');
	var opt = params[i].getElementsByTagName("option");

	var html_id = this.namespace + "_style_param_" + id;
	this.style_param_id.push(id);

	oLabel = document.createElement("label");
	oLabel.htmlFor = html_id;
	oLabel.appendChild(document.createTextNode(label +": "));

	if (description) {
	    oLabel.title = description;
	}

	oP.appendChild(oLabel);

	if (opt.length > 0) {
	    // user can select among different options
	    elem = oP.appendChild(document.createElement("select"));

	    for (var j = 0; j < opt.length; j++) {
		oOption = elem.appendChild(document.createElement("option"));
		oOption.value = opt[j].getAttribute('value');

		if (type == 'imageselector') {
		    oOption.setAttribute('rel','<img src="' + opt[j].firstChild.data + '" />');
		}
		else {
		    oOption.appendChild(document.createTextNode(opt[j].firstChild.data));
		}

		// should be in reset
		//oOption.selected = def == oOption.value;
		//oOption.selected = (def == oOption.value ? 'selected' : 'unselected');
		if (def == oOption.value) {
		    oOption.selected = true;
		}

	    }


	    if (type == 'imageselector') {		
		// id must be defined before jListbox is initiated
		elem.id = html_id;
		$(elem).jListbox();
	    }

	}
	else {
	    if (type == 'boolean') {
		elem = document.createElement("input");
		elem.type="checkbox";
	    }

	    else if (type == 'float' || type == 'string' || type == 'int') {
		elem = document.createElement("input");
		elem.type='text';
		elem.size='7';
	    }
	}


	elem.id = html_id;
	elem.name = html_id;
	oP.appendChild(elem);	

	$(elem).change(function() {
		obj.style_param_id_modif[this.id] = true;
		this.style.background = '#eef';                                                                                                 	    });
    
	oP.appendChild(document.createElement("br"));
    }

};


// reset the values in the style form except the protected onces
// typically modified by the user

FigureProperties.prototype.reset = function(protect) { 
    
    protect = protect || {};

    var xmldoc = this.xmldoc;

    var params = xmldoc.getElementsByTagName("param");
    var elem;

    for (var i=0; i<params.length; i++) {
	var type = params[i].getAttribute('type');
	var id = params[i].getAttribute('id');
	var def = params[i].getAttribute('default');

	var html_id = this.namespace + "_style_param_" + id;

	elem = document.getElementById(html_id);

	// reset only not projected fields
	if (! (html_id in protect )) {
	    elem.style.background = 'white';

	    var opt = elem.getElementsByTagName("option");

	    if (opt.length > 0) {
		// user can select among different options

	    
		// does not work with jListbox

		if (type != 'imageselector') {
		    for (var j = 0; j < opt.length; j++) {
			opt[j].selected = def == opt[j].value;
		    }
		}
	    }
	    else {
		if (elem.type == "checkbox") {
		    elem.checked = def == 'True';
		}
		else if (elem.type == "text") {
		    elem.value = "" + def;
		}
	    }

	}
	
    }
};

FigureProperties.prototype.set_stat = function(xmldoc) { 
    if (!this.xmldoc) {
	this.create_prop_ui(xmldoc);
    }

    this.xmldoc = xmldoc;
    this.reset(this.style_param_id_modif);

    var warning = xmldoc.getElementsByTagName("warning");
    if (warning.length !== 0) {
	alert("Warning: " + warning[0].firstChild.nodeValue);
    }

};


FigureProperties.prototype.get_style = function() { 
    var ext = 'OceanBrowser';
    // encode style
    var s = "";
    var url = null;

    if (this.Layer) {
	if (this.Layer.cap) {
	    ext = this.Layer.cap.WMS_extensions;
	}
    }


    if (ext == 'OceanBrowser') {
	s = '';
	var val, id;
	for (var i in this.style_param_id) {
	    id = this.style_param_id[i];
	    val = this.get_style_param(id);

	    if (val !== undefined) {
		s +=  id + ':' + val + '+';
	    }
	    else {
		console.log('id ',id,' undefined');
	    }

	}


	// remove last plus
	s = s.slice(0,s.length-1);
    }
    else {
	var elem = document.getElementById(this.namespace + "select_style");

	if (elem) {
	    s = elem.value;
	}

	if (this.styles) {
	    for (var j=0; j < this.styles.length; j++) {
		if (this.styles[j].name == s) {
		    url = this.styles[j].legend_url;
		    break;
		}
	    }
	}
    }

    console.log('style ',s);


    // if url is not already defined from the GetCapabilities information
    // construct url link for color-bar using GetLegendGraphic request
    
    if (!url) {
	
	var params = 
	{request: "GetLegendGraphic",
	 /*       width: this.colorbar_div.offsetWidth,
		  height: this.colorbar_div.offsetHeight,*/	 
	 width: this.colorbar_width,
	 height: this.colorbar_height,
	 transparent: true,
	 decorated: true,
	 style: s,
	 color: '#000000',
	 format: 'image/png'
	};


	if (this.Layer) {
	    params.layer = decodeURIComponent(this.Layer.name);
	}
    
	url = Util.append_param(this.wms_url,params);
    }



    // once image is loaded set size of div
    var obj = this;
    var img = new Image();
    img.onload = function() {
	$(obj.colorbar_div).width(img.width);
	$(obj.colorbar_div).height(img.height);
    };
    img.src = url;

    var colorbar_style = "url(" + url + ")";
    this.colorbar_div.style.backgroundImage = colorbar_style;
    
    return s;
};


FigureProperties.prototype.plotting_range = function() { 

    return {ca0: parseFloat(document.getElementById(this.namespace + "_style_param_vmin").value),
	    ca1: parseFloat(document.getElementById(this.namespace + "_style_param_vmax").value)};
    
};


FigureProperties.prototype.get_style_param = function(id) { 

    var elem = document.getElementById(this.namespace + "_style_param_" + id);

    if (!elem) {
	return null;
    }

    if (elem.type === 'checkbox') {
	// for checkbox input
	val = elem.checked;
    }
    else {
	val = elem.value;
    }

    return val;

};


FigureProperties.prototype.set_data_range = function(vmin,vmax) { 
    console.log('call to set_data_range',vmin,vmax);

    var s = '';

    s += '<?xml version="1.0"?>';
    s += '<stats>';
    s += '<param id="cmap" type="imageselector" default="jet" label="Colormap" >';
    s += '<option value="jet">img/mini_colormap_jet.png</option>';
    s += '<option value="hsv">img/mini_colormap_hsv.png</option>';
    s += '<option value="gray">img/mini_colormap_gray.png</option>';
    s += '<option value="RdBu">img/mini_colormap_RdBu.png</option>';
    s += '<option value="Paired">img/mini_colormap_Paired.png</option>';
    s += '</param>';
    s += '<param id="inverted" type="boolean" default="False" label="Invert colormap" >';
  s += '</param>';
    s += '<param id="method" type="string" default="pcolor_flat" label="Plotting style"  description="Flat shading will produce interpolated colors">';
    s += '<option value="pcolor_flat">Flat shading</option>';
    s += '<option value="contourf">Filled contours</option>';
    s += '<option value="contour">Contours</option>';
    s += '</param>';
    s += '<param id="vmin" type="float" default="' + vmin + '" label="Minimum color-bar range" >';
    s += '</param>';
    s += '<param id="vmax" type="float" default="' + vmax + '" label="Maximum color-bar range" >';
    s += '</param>';
    s += '<param id="ncontours" type="int" default="40" label="Number of contour-lines" >';
    s += '</param>';
    s += '</stats>';

    var xmldoc = OpenLayers.parseXMLString(s);
    
    this.set_stat(xmldoc);
};



FigureProperties.prototype.set_dimension = function(name,title,opt) { 
    var sel,
        range = opt.range,
        units = opt.units,
        default_value = opt.default_value;


    if (!(name in this.dimensions)) {
	this.dimensions[name] = {};
	this.dimensions[name].name = title;

	this.layer_info_div.appendChild(document.createElement("br"));
	var label = document.createElement("label");
	label.htmlFor = this.namespace + "_select_" + name;
	label.appendChild(document.createTextNode(title + ": "));
	//this.layer_info_div.appendChild(label);

	sel = this.layer_info_div.appendChild(document.createElement("select"));
	sel.id = this.namespace + "_select_" + name;
	sel.title = title;

	this.dimensions[name].sel = sel;
	this.dimensions[name].label = label;
    }


    if (name == 'time') {
	this.layer_info_div.appendChild(document.createElement("br"));
	var button_anim = document.createElement("input");
	button_anim.type = "button";
	button_anim.value = "Animate";
	button_anim.id = this.namespace + "config_button";
	this.layer_info_div.appendChild(button_anim);	
	this.button_anim = button_anim;
    }


    var option; 
    sel = this.dimensions[name].sel;
       
    while (sel.hasChildNodes()) {
	sel.removeChild(sel.lastChild);
    }

    this.dimensions[name].label.firstChild.data = this.dimensions[name].name + '[' + units + '] :';
    this.dimensions[name].sel.title = this.dimensions[name].name + ' [' + units + ']';
		
    for (j=0; j < range.length; j++) {
	option = document.createElement('option');
	    
	option.value = "" + range[j];

	if (range[j] == default_value) {
	    option.selected = range[j] == default_value;
	}

	option.appendChild(document.createTextNode("" + range[j]));
	    
	sel.appendChild(option);      
    }

};


FigureProperties.prototype.get_dimension = function(name) { 
    if (name in this.dimensions) {
	return this.dimensions[name].sel.value;
    }

    return '';
};


// display feature info
FigureProperties.prototype.set_info = function(str) {
    if (!this.info) {
	this.info = document.createElement("div");
	this.info.appendChild (document.createTextNode(""));
	this.layer_info_div.appendChild(this.info);
    }

    this.info.firstChild.data = str;
};

FigureProperties.prototype.get_layer_param = function() {
    //console.log('get_layer_param',id);
    var analysis_params = {};

    var depth = this.get_dimension('elevation');
    var time = this.get_dimension('time');
    var style = this.get_style();

    if (depth !== "") {
	analysis_params.elevation = depth;
    }

    if (time !== "") {
	analysis_params.time = time;
    }

    console.log('param + style',analysis_params,style);
    
    return {param: analysis_params, style: style};
};



FigureProperties.prototype.remove = function() {
    var root = document.body;
    //root.removeChild(this.dialog_element);
    this.colorbar_container.removeChild(this.layer_info_div);
};


