/**
 * WJComponent
 *
 * Base Component class
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJComponent = Class.create({
	/**
	 * Properties of WJComponent
	 *
	 * string id
	 * string value
	 * boolean disabled
	 * boolean readonly
	 * event focusEvent
	 * event blurEvent
	 * event changeEvent
	 **/

	/**
	 * initialize
	 *
	 * Creates a new WJComponent
	 *
	 * @since 
	 * @access public
	 * @param string id
	 * @return 
	 **/
	initialize: function(id) {
		this.id = id;
		this.value = null;
		this.disabled = false;
		this.readonly = false;
		this.focusEvent = "wj:focus";
		this.blurEvent = "wj:blur";
		this.changeEvent = "wj:change";
		this.componentType = "WJComponent";
		this.htmlelement = null;
		this.label = null;
		this.group = false;
	},

	/**
	 * setHtmlelement
	 *
	 * Sets the html element for this component
	 *
	 * @since Mon Jul 21 2008
	 * @access public
	 * @param htmlelement element
	 * @return void
	 **/
	setHtmlelement: function(element) {
		this.htmlelement = element;
	},

	/**
	 * setLabel
	 *
	 * Sets this component's label
	 *
	 * @since Wed Nov 19 2008
	 * @access public
	 * @param WJLabel label
	 * @return void
	 **/
	setLabel: function(label) {
		this.label = label;
	},

	/**
	 * fireEvent
	 *
	 * Fires the event with the original event wrapped in it
	 *
	 * @since Mon Jul 21 2008
	 * @access public
	 * @param string eventName
	 * @param event e
	 * @return void
	 **/
	fireEvent: function(eventName, e) {
		this.htmlelement.fire(eventName, e);
	},

	/**
	 * initComponent
	 *
	 * Inits this component from a datacollector
	 *
	 * @since Wed Jul 16 2008
	 * @access public
	 * @param WJDatacollector datacollector
	 * @return void
	 **/
	initComponent: function(datacollector) { 
		this.value = datacollector.getValue();
	},

	/**
	 * getValue
	 *
	 * Gets the value of this component
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getValue: function() {
		var usesDijit = (typeof(dijit) != "undefined") ? true : false;
		
		if (this.htmlelement && usesDijit && dijit.byId(this.htmlelement.id) && dijit.byId(this.htmlelement.id).getValue) {
			this.value = dijit.byId(this.htmlelement.id).attr("value"); // IE doesn't support getValue, FF doesn't (fully) support .value
		}
		else if (this.htmlelement && this.htmlelement.value) {
			this.value = this.htmlelement.value;
		}
		return this.value;
	},

	/**
	 * setValue
	 *
	 * Sets the value of this component
	 *
	 * @since Tue Nov 04 2008
	 * @access public
	 * @return void
	 **/
	setValue: function(value) {
		var usesDijit = (typeof(dijit) != "undefined") ? true : false;
		this.value = value;
		if (this.htmlelement && usesDijit && dijit.byId(this.htmlelement.id) && dijit.byId(this.htmlelement.id).setValue) {
			dijit.byId(this.htmlelement.id).setValue(this.value);
		}
		else if (this.htmlelement && this.htmlelement.value) {
			this.htmlelement.value = this.value;
		}
	},

	/**
	 * getId
	 *
	 * Gets this component's id
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getId: function() {
		return this.id;
	}
});

/**
 * WJRestrictedInput
 *
 * Base component for a component with limited input options
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJRestrictedInput = Class.create(WJComponent, {
	/**
	 * Properties of WJRestrictedInput
	 *
	 * WJOptionGroup optiongroup
	 * boolean alternative
	 * WJComponent alternativeComponent
	 **/
	
	/**
	 * initialize
	 *
	 * Initialize this WJRestrictedInput
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJRestrictedInput
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJRestrictedInput";
	},

	/**
	 * initComponent
	 *
	 * Inits this component from a datacollector, includes the possible values in an option group
	 *
	 * @since Mon Jul 21 2008
	 * @access public
	 * @param WJDatacollector datacollector
	 * @return void
	 **/
	initComponent: function($super, datacollector) { 
		$super(datacollector);
		WJDebugger.log(WJDebugger.INFO, "Initing restricted input component", datacollector, datacollector.datatypeSpecifics);
		this.optiongroup = new WJOptionGroup();
		
		if (datacollector.datatypeSpecifics && datacollector.datatypeSpecifics.values) {
			var values = datacollector.datatypeSpecifics.values;
			for (var i = 0; i < values.length; i++) {
				this.optiongroup.addOption(new WJOption(values[i], new WJLabel(this.id + "_label_" + i, values[i]) ) );
			}
		}
	}
});

/**
 * WJSinglevalueInput
 *
 * Base component for input type where only one value may be selected
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJSinglevalueInput = Class.create(WJRestrictedInput, {
	
	/**
	 * initialize
	 *
	 * Initialize this WJSinglevalueInput
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJSinglevalueInput
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJSinglevalueInput";
	},

	/**
	 * initComponent
	 *
	 * Inits the component from a datacollector
	 * Boolean are usually represented with a WJSinglevalueInput; if datacollector is a boolean, add true and false options
	 *
	 * @since Wed Jul 16 2008
	 * @access public
	 * @param WJDatacollector datacollector
	 * @return void
	 **/
	initComponent: function($super, datacollector) {
		$super(datacollector);

		if (datacollector.getDatatype() == "WMBoolean") {
			this.optiongroup.addOption(new WJOption(1, new WJLabel(this.id + "_label_true", "Ja") ) );
			this.optiongroup.addOption(new WJOption(0, new WJLabel(this.id + "_label_false", "Nee") ) );
		}
	}
});

/**
 * WJMultivalueInput
 *
 * Base component for selecting multiple values
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJMultivalueInput = Class.create(WJRestrictedInput, {
	/**
	 * Properties of WJMultivalueInput
	 *
	 * Array value
	 **/
	/**
	 * initialize
	 *
	 * Initialize this WJMultivalueInput
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJMultivalueInput
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJMultivalueInput";
	}
});

/**
 * WJGroup
 *
 * A group of components
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
WJGroup = Class.create(WJComponent, {
	/**
	 * Properties of WJGroup
	 *
	 * Array components
	 **/

	/**
	 * initialize
	 *
	 * Initializes a new WJGroup
	 *
	 * @since Tue Aug 19 2008
	 * @access public
	 * @param string id
	 * @return void
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJGroup";
		this.components = new Array();
		this.region = "center";
		this.hasGroups = false;
		this.group = true;
	},

	/**
	 * setRegion
	 *
	 * Sets the region where to show this group
	 *
	 * @since Wed Nov 12 2008
	 * @access public
	 * @param string region
	 * @return void
	 **/
	setRegion: function(region) {
		this.region = region;
	},

	/**
	 * addComponent
	 *
	 * Adds a component to this component group
	 *
	 * @since Tue Aug 19 2008
	 * @access public
	 * @param WJComponent comp
	 * @return void
	 **/
	addComponent: function(comp) {
		WJDebugger.log(WJDebugger.DEBUG, "Adding component to WJGroup " + this.id, comp);
		this.components.push(comp);
	},

	/**
	 * getComponents
	 *
	 * Gets the components in this group
	 *
	 * @since Tue Aug 19 2008
	 * @access public
	 * @return Array
	 **/
	getComponents: function() {
		return this.components;
	},

	/**
	 * initComponent
	 *
	 * Loads all components in this group into this component group
	 *
	 * @since Tue Aug 19 2008
	 * @access public
	 * @param WJDatacollector datacollector
	 * @param WJGui gui
	 * @return void
	 **/
	initComponent: function($super, datacollector, gui) {
		$super(datacollector, gui);

		WJDebugger.log(WJDebugger.DEBUG, "Initing a " + this.id + " WJGroup", datacollector, gui);

		if (datacollector.value.composite) {
			for (var i = 0; i < datacollector.value.composite.length; i++) {
				if (typeof(datacollector.value.composite[i]) == "object") {
					this.addComponent(gui.createComponents(datacollector.value.composite[i]) );
				}
			}
		}
	}
});

/**
 * WJText
 *
 * Textbox input component
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJText = Class.create(WJComponent, {
	/**
	 * Properties of WJText
	 *
	 * integer maxchars
	 * integer maxlines
	 **/

	/**
	 * initialize
	 *
	 * Initialize this WJText
	 *
	 * @since Mon Jul 28 2008
	 * @access public
	 * @return void
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJText";
	}
});

/**
 * WJLabel
 *
 * A label, the text that is usually found near an input component.
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
WJLabel = Class.create(WJComponent, {
	/**
	 * Properties of WJLabel
	 *
	 * string text
	 * string image
	 * WJComponent component
	 **/

	/**
	 * initialize
	 *
	 * Creates a new Label
	 *
	 * @since 
	 * @access public
	 * @param string id
	 * @param string text (default: null)
	 * @param string image (default: null)
	 * @return 
	 **/
	initialize: function($super, id, text, image) {
		$super(id);
		this.componentType = "WJLabel",
		this.text = text;
		this.image = image;
	}
});

/**
 * WJTab
 *
 * A tab group
 *
 * @since Wed Sep 10 2008 (existed before, but completely reimplemented the class)
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJTab = Class.create(WJComponent, {
	/**
	 * initialize
	 *
	 * Initialize this WJTab
	 *
	 * @since Wed Sep 10 2008
	 * @access public
	 * @return WJTab
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJTab";
	},

	/**
	 * setTitle
	 *
	 * Sets the title of this tab (if represented by a dojo TabContainer / ContentPane)
	 *
	 * @since Mon Sep 15 2008
	 * @access public
	 * @param string title
	 * @return void
	 **/
	setTitle: function(title) {
		if (this.htmlelement && this.htmlelement["class"] == "dijitContentPane") {
			this.htmlelement.controlButton.containerNode.innerHTML = title;
		}
	},

	/**
	 * click
	 *
	 * Fires the tabclick event
	 *
	 * @since Fri Sep 12 2008
	 * @access public
	 * @return mixed
	 **/
	click: function() {
		this.fire("aeroplane:tabclick", {value: this.getValue()});
	}
});

/**
 * WJFile
 *
 * Component to select files (ie browse or interface to filemanager)
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJFile = Class.create(WJComponent, {
	/**
	 * initialize
	 *
	 * Initialize this WJFile
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJFile
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJFile";
	}
});

/**
 * WJImage
 *
 * Component to select an image
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJImage = Class.create(WJFile, {
	/**
	 * initialize
	 *
	 * Initialize this WJImage
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJImage
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJImage";
	},

	/**
	 * selectImage
	 *
	 * Sets the value of this component
	 *
	 * @since Mon Dec 29 2008
	 * @access public
	 * @param string image
	 * @param string path
	 * @param integer section
	 * @return void
	 **/
	selectImage: function(image, path, section) {
		this.value = "/var/mediamanager/images/" + path + "/" + image;
		if ( $(this.htmlelement.id + "_thumbnail") ) {
			var src = $(this.htmlelement.id + "_thumbnail").src;
			var basepath = src.match(/(\/images\/thumbs(\/__[^\/]*)*)\/?/);
			if (basepath) {
				$(this.htmlelement.id + "_thumbnail").src = basepath[1] + this.value;
			}
			else {
				 // make a (small) thumbnail annyway
				$(this.htmlelement.id + "_thumbnail").src = "/images/thumbs/__lw50h50" + this.value;
			}

			$(this.htmlelement.id + "_thumbnail").up().removeClassName("aeroplane_nothumb");
		}
	},

	/**
	 * getValue
	 *
	 * Returns the current image value
	 *
	 * @since Tue Dec 30 2008
	 * @access public
	 * @return string
	 **/
	getValue: function() {
		if (this.htmlelement) {
			if (this.htmlelement.type == "file") {
				this.value = "/var/mediamanager/images/" + document.forms[this.id + "_form"]["path"].value + "/" + this.htmlelement.value;
			}
		}
		return this.value;
	},

	/** 
 	 * uploadSingleFile 
 	 * 
 	 * Uploads one file using an iframe 
 	 * 
 	 * @since Wed Mar 04 2009 
 	 * @access public 
 	 * @param event event 
 	 * @return void 
 	 **/ 
 	uploadSingleFile: function(event) { 
 		document.forms[this.id + "_form"].submit(); 
 	},

	/**
	 * setHtmlelement
	 *
	 * Attaches the open mediamanager event to the wjbutton
	 *
	 * @since Mon Dec 29 2008
	 * @access public
	 * @param element elem
	 * @return void
	 **/
	setHtmlelement: function($super, elem) {
		if (dijit.byId(elem.id) ) {
			$super(elem);
			dijit.byId(elem.id).setObserver(function() {
				aeroplane.openApplication("Mediamanager", this.selectImage.bind(this), {section: 2100, path: "."} );
			}.bind(this) );
		}
		else if (elem.type == "file") {
			$super(elem);
			this.htmlelement.observe("change", this.uploadSingleFile.bindAsEventListener(this) );
		}
		else {
			this.setHtmlelement.bind(this, elem).delay(0.1);
		}
	}
});

/**
 * WJButtonComponent
 *
 * Not named WJButton as that name was already taken
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJButtonComponent = Class.create(WJComponent, {
	/**
	 * Properties of WJButtonComponent
	 *
	 * event clickEvent
	 **/
	
	/**
	 * initialize
	 *
	 * Initialize this WJButtonComponent
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJButtonComponent
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJButtonComponent";
		this.clickEvent = null;
	},

	/**
	 * disable
	 *
	 * Disables this button
	 *
	 * @since Fri Feb 13 2009
	 * @access public
	 * @return void
	 **/
	disable: function() {
		if (dijit.byId(this.htmlelement.id) ) {
			dijit.byId(this.htmlelement.id).button.disable();
		}
		else {
			this.disable.bind(this).delay(0.25);
		}
	},
	
	/**
	 * enable
	 *
	 * Enables this button
	 *
	 * @since Fri Feb 13 2009
	 * @access public
	 * @return void
	 **/
	enable: function() {
		if (dijit.byId(this.htmlelement.id) ) {
			dijit.byId(this.htmlelement.id).button.enable();
		}
		else {
			this.disable.bind(this).delay(0.25);
		}
	}
});

/**
 * WJFileUploader
 *
 * Component to upload files
 *
 * @since Fri Oct 17 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJFileUploader = Class.create(WJComponent, {
	/**
	 * initialize
	 *
	 * Initialize this WJFileUploader
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @return WJFileUploader
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJFileUploader";
		this._totalSize = 0;
	},

	/**
	 * _initUploaderWidget
	 *
	 * Creates the uploader widget and sets it properties
	 *
	 * @since Fri Oct 17 2008
	 * @access protected
	 * @return void
	 **/
	_initUploaderWidget: function() {
		window.uploaderEvent = this.eventHandler.bind(this);

		this.uploader = new SWFObject("uploaderObject", "/lib/plugin/aeroplane/flash/uploader.swf", "100%", "100%", "transparent", {settings: Object.toJSON({eventHandler: "uploaderEvent", allowMultipleFiles: true, buttonImage: "/lib/wjgui/skins/lucid/images/contentbuttons.png", buttonText: "Bestanden zoeken", buttonTextStyle: {font: "Arial", fontSize: "12", color: "F40D75", fontWeight: "bold"} }) });
		swfloader.loadSWFObject(this.htmlelement, this.uploader);
	},

	/**
	 * eventHandler
	 *
	 * Handles the event coming from the uploader SWF
	 *
	 * @since Thu Mar 19 2009
	 * @access public
	 * @param string reference
	 * @param object event
	 * @return void
	 **/
	eventHandler: function(reference, event) {
		switch (event.type) {
			case "resize":
				this.htmlelement.setStyle({width: event.width, height: event.height});
				break;
			case "fileSelect":
				this.fileSelect(event);
				break;
			case "uploadCompleteData":
				this.markUploadComplete(event);
				break;
			case "uploadCancel":
				this.markUploadCancel(event);
				break;
			case "uploadProgress":
				this.updateUploadProgress(event);
				break;
		}
	},

	/**
	 * setNextButton
	 *
	 * Sets the next button, ie the button that one can click to continue after uploading something, this button should remain inactive until the upload is complete
	 *
	 * @since Fri Feb 13 2009
	 * @access public
	 * @param dijit.widget button
	 * @return void
	 **/
	setNextButton: function(button) {
		this.nextbutton = button;
		this.nextbutton.disable();
	},

	/**
	 * updateUploadProgress
	 *
	 * Updates the progressbars to reflect the current upload progress
	 *
	 * @since Fri Nov 07 2008
	 * @access public
	 * @param event e
	 * @return void
	 **/
	updateUploadProgress: function(e) {
		var pbar = dijit.byId(e.id + "_progress");
		if (pbar) {
			pbar.update({progress: e.bytesLoaded});
		}
	},

	/**
	 * markUploadCancel
	 *
	 * Marks an upload as cancelled
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @param event e
	 * @return void
	 **/
	markUploadCancel: function(e) {
		$(e.id + "_inforow").down(".aeroplane_uploadcancelled").show();
		$(e.id + "_inforow").down(".aeroplane_uploadcancelled").title = "De upload werd geannulleerd";
		$(e.id + "_inforow").down("a.aeroplane_delete").hide();
		this.handleUploadCompleted(e.id);
	},

	/**
	 * handleUploadCompleted
	 *
	 * Handles a completed upload, adds a message when all are finished
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @param string id
	 * @return void
	 **/
	handleUploadCompleted: function(id) {
		delete this.files[id];

		if ($H(this.files).size() == 0) {
			this.componentdiv.down(".aeroplane_fileselector").down("div").hide();
			this.componentdiv.down(".aeroplane_fileselector").insert(new Element("h2").update("De upload is voltooid") );
			this.componentdiv.down("thead").down(".aeroplane_delete").firstDescendant().hide();
			this.uploadbutton.getButton().setStyle({visibility: "hidden"});
			aeroplane.loadingCursor(false);
		}
	},

	/**
	 * markUploadComplete
	 *
	 * Marks an upload as completed
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @param event e
	 * @return void
	 **/
	markUploadComplete: function(e) {
		$(e.id + "_inforow").down("a.aeroplane_delete").hide();
		$(e.id + "_progress").down("div").setStyle({visibility: "hidden"});
		var finfo = e.data.evalJSON()[0];
		WJDebugger.log(WJDebugger.NOTICE, "Marking '%s' as uploaded", e.id, finfo);

		if (finfo.error == 0) {
			$(e.id + "_inforow").down(".aeroplane_complete").show();
			if (finfo.errormessage) {
				$(e.id + "_inforow").down(".aeroplane_complete").title = finfo.errormessage;
			}
			if (this.nextbutton) {
				this.nextbutton.enable();
			}
		}
		else {
			$(e.id + "_inforow").down(".aeroplane_error").show();
			if (finfo.errormessage) {
				$(e.id + "_inforow").down(".aeroplane_error").title = finfo.errormessage;
			}
		}
		this.handleUploadCompleted(e.id);
	},

	/**
	 * fileSelect
	 *
	 * Callback for selecting files
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @param event e
	 * @return void
	 **/
	fileSelect: function(e) {
		this._updateListing(e.fileList);
	},

	/**
	 * _updateListing
	 *
	 * Creates a nice looking listing
	 *
	 * @since Mon Feb 2 2009
	 * @access protected
	 * @param Object files
	 * @return void
	 **/
	_updateListing: function(files) {
		this.componentdiv.down("thead").hide();
		if (this.uploadbutton) {
			this.uploadbutton.getButton().setStyle({visibility: "visible"});
		}
		this.componentdiv.down("tbody").update("");
		this.componentdiv.down(".aeroplane_wjfileuploader_tablewrapper").setStyle({"height": ($(this.componentdiv.parentNode).getHeight() - 100) + "px", "overflow-y": "auto"});
		this.componentdiv.down(".aeroplane_total").update("");
		aeroplane.windowmanager.cleanUpDijitWidgets();
		this._totalSize = 0;
		this.files = files;

		var filescollection = [];
		for (var file in this.files) {
			filescollection.push(file);
		}
		filescollection.reverse().each(function(file) {
			this._handleFileSelect(this.files[file] );
		}.bind(this) );

		if ($H(this.files).size() > 0) {
			this.componentdiv.siblings().invoke("remove");
			this.componentdiv.down(".aeroplane_total").update("Totaal"+ ": <strong>" + this._formatFileSize(this._totalSize) + "</strong>");
			this.componentdiv.down("thead").show();
			$(this.uploader.getAttribute("swfname") ).setTextStyle(Object.toJSON({font: "Arial", fontSize: "12", color: "01A1C1", fontWeight: "normal", textDecoration: "underline", cursor: "pointer"}) );
			$(this.uploader.getAttribute("swfname") ).setText("Bestanden toevoegen");
			$(this.uploader.getAttribute("swfname") ).hideButtonImage();

			this._showUploadButton();
		}
	},

	/**
	 * removeFile
	 *
	 * Removes a file from the uploader and the listing
	 *
	 * @since Mon Feb 2 2009
	 * @access public
	 * @param string fileID
	 * @return void
	 **/
	removeFile: function(fileID) {
		WJDebugger.log(WJDebugger.NOTICE, "Removing file '%s'", fileID);
		var files = {};
		for (var id in this.files) {
			if (id != fileID) {
				files[id] = this.files[id];
			}
		}
		$(this.uploader.getAttribute("swfname") ).removeFile(fileID);
		this._updateListing(files);
	},

	/**
	 * _showUploadButton
	 *
	 * Shows the upload button if there are files selected
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @return void
	 **/
	_showUploadButton: function() {
		if (!this.uploadbutton) {
			this.uploadbutton = new WJButton("Upload starten", this.startUpload.bind(this), false,
			this.componentdiv.down("div.aeroplane_start") );
		}
		this.uploadbutton.getButton().setStyle({visibility: "visible"});
	},

	/**
	 * startUpload
	 *
	 * Uploads all currently selected files
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @return void
	 **/
	startUpload: function() {
		aeroplane.loadingCursor(true);
		var cookie = new WJCookie();
		var sessionid = cookie.get("PHPSESSID", false);
		this.uploadInfo["PHPSESSID"] = sessionid;

		WJDebugger.log(WJDebugger.INFO, "Upload files", this.uploadInfo, this.files);
		$(this.uploader.getAttribute("swfname") ).uploadAll("/index.php", "POST", this.uploadInfo);
	},

	/**
	 * setUploadInformation
	 *
	 * Sets the information the button requires to do the actual upload
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @param object info
	 * @return void
	 **/
	setUploadInformation: function(info) {
		this.uploadInfo = info;
	},

	/**
	 * _handleFileSelect
	 *
	 * Adds a file to the list of selected files
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @param object file
	 * @return void
	 **/
	_handleFileSelect: function(file) {
		WJDebugger.log(WJDebugger.NOTICE, "Selecting file", file);
		if (!this.pastetable) {
			this.pastetable = new Element("table");
		}
		var info = info = {id: file.id, file: file.name, size: this._formatFileSize(file.size) };
		this.pastetable.update(this.rowtemplate.evaluate(info) );
		$A(this.pastetable.rows).each(function(row) {
			this.componentdiv.down("tbody").appendChild(row);
		}, this);

		this.componentdiv.select("a.aeroplane_delete").last().observe("click", function (event, fileID) {
 			this.removeFile(fileID);
		}.bindAsEventListener(this, file.id) );
		dojo.parser.parse(this.componentdiv.select(".aeroplane_progress").last().up() );

		this._totalSize += file.size;

		var pbar = dijit.byId(file.id + "_progress");
		if (pbar) {
			pbar.update({maximum: file.size, progress: 1});
		}
	},

	/**
	 * _formatFileSize
	 *
	 * Formats the filesize into something with Kb, Mb etc.
	 *
	 * @since Fri Oct 17 2008
	 * @access protected
	 * @param integer bytesize
	 * @return string
	 **/
	_formatFileSize: function(bytesize) {
		if (bytesize >= 1073741824) { // 1024 ^ 3
			bytesize = (Math.round( (bytesize * 100) / 1073741824) / 100 ) + " " + "GB";
		}
		else if (bytesize >= 1048576) { // 1024 ^ 2
			bytesize = (Math.round( (bytesize * 100) / 1048576) / 100) + " " + "MB";
		}
		else if (bytesize >= 1024) {
			bytesize = (Math.round( (bytesize * 100) / 1024) / 100) + " " + "KB";
		}
		else {
			bytesize = bytesize + " " + "b";
		}
		return bytesize;
	},

	/**
	 * alternativeUpload
	 *
	 * Performs an alternative upload from the browse button
	 *
	 * @since Wed Mar 18 2009
	 * @access public
	 * @return void
	 **/
	alternativeUpload: function() {
		this.alternativeUploader.form.submit();
		$(this.alternativeUploader.form.target).observe("load", function() {
			this.nextbutton.enable();
			this.componentdiv.down(".aeroplane_fileselector").down("div").hide();
			this.componentdiv.down(".aeroplane_fileselector").insert(new Element("h2").update("De upload is voltooid") );
		}.bind(this) );
	},

	/**
	 * setHtmlelement
	 *
	 * Sets the html element for this component
	 * Inits the YUI uploader component
	 *
	 * @since Fri Oct 17 2008
	 * @access public
	 * @param htmlelement element
	 * @return void
	 **/
	setHtmlelement: function($super, element) {
		$super(element);

		this.componentdiv = this.htmlelement.up(".aeroplane_wjfileuploader")

		var templaterows = this.componentdiv.select("tr.aeroplane_listingtemplate");
		this.rowtemplate = new Template(templaterows.collect(function(row) {
			return "<tr id='" + row.id + "row' class='" + row.className + "'>" + row.innerHTML + "</tr>";
		} ).join("") );
		templaterows.invoke("remove");

		this._updateListing({});
		this._initUploaderWidget();

		if ($("alternative_" + element.id) ) {
			this.alternativeUploader = $("alternative_" + element.id);
			this.alternativeUploader.observe("change", this.alternativeUpload.bind(this) );
			if ($("alternativelink_" + element.id) ) {
				$("alternativelink_" + element.id).observe("click", function() {
					$("alternativediv_" + element.id).setStyle({"display": "block"});
				});
			}
		}
	}
});

/**
 * WJRichtextLabel
 *
 * A label, the text that is usually found near an input component, with rich markup
 *
 * @since Thu Mar 5 2009
 * @revision $Revision$
 * @author Giso Stallenberg
 * @package Windmill.Javascript.Component
 **/
WJRichtextLabel = Class.create(WJLabel, {
	/**
	 * Properties of WJLabel
	 *
	 * string html
	 * WJComponent component
	 **/

	/**
	 * initialize
	 *
	 * Creates a new WJRichtextLabel
	 *
	 * @since Thu Mar 5 2009
	 * @access public
	 * @param string id
	 * @param string html (default: null)
	 * @return void
	 **/
	initialize: function($super, id, html) {
		$super(id);
		this.componentType = "WJRichtextLabel",
		this.html = html;
	},

	/**
	 * setLabel
	 *
	 * Setting a label on a label is unwanted in most cases, so this is overwritten to do nothing expect when really wanted 
	 *
	 * @since Wed Apr 01 2009
	 * @access public
	 * @param WJLabel label
	 * @param boolean force
	 * @return void
	 **/
	setLabel: function($super, label, force) {
		var force = force || false;
		if (force) {
			$super(label);
		}
	}
});

/**
 * WJFlashVideo
 *
 * Component to select a flv
 *
 * @since Thu Mar 12 2009
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJFlashVideo = Class.create(WJFile, {
	/**
	 * initialize
	 *
	 * Initialize this WJFlashVideo
	 *
	 * @since Thu Mar 12 2009
	 * @access public
	 * @return WJFlashVideo
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJFlashVideo";
	},

	/**
	 * selectVideo
	 *
	 * Sets the value of this component
	 *
	 * @since Thu Mar 12 2009
	 * @access public
	 * @param string image
	 * @param string path
	 * @param integer section
	 * @return void
	 **/
	selectVideo: function(image, path, section) {
		this.value = "/var/mediamanager/videos/" + path + "/" + image;
		if ( $(this.htmlelement.id + "_thumbnail") ) {
			var src = $(this.htmlelement.id + "_thumbnail").src;
			var basepath = src.match(/(\/images\/thumbs(\/__[^\/]*)*)\/?/);
			if (basepath) {
				$(this.htmlelement.id + "_thumbnail").src = basepath[1] + this.value + ".png";
			}
			else {
				 // make a (small) thumbnail annyway
				$(this.htmlelement.id + "_thumbnail").src = "/images/thumbs/__lw50h50" + this.value + ".png";
			}

			$(this.htmlelement.id + "_thumbnail").up().removeClassName("aeroplane_nothumb");
		}
	},

	/**
	 * getValue
	 *
	 * Returns the current video value
	 *
	 * @since Thu Mar 12 2009
	 * @access public
	 * @return string
	 **/
	getValue: function() {
		return this.value;
	},

	/**
	 * setHtmlelement
	 *
	 * Attaches the open mediamanager event to the wjbutton
	 *
	 * @since Mon Dec 29 2008
	 * @access public
	 * @param element elem
	 * @return void
	 **/
	setHtmlelement: function($super, elem) {
		if (dijit.byId(elem.id) ) {
			$super(elem);
			dijit.byId(elem.id).setObserver(function() {
				aeroplane.openApplication("Mediamanager", this.selectVideo.bind(this), {section: 2300, path: "."} );
			}.bind(this) );
		}
		else {
			this.setHtmlelement.bind(this, elem).delay(0.1);
		}
	}
});

/**
 * WJAccordion
 *
 * A accordion group
 *
 * @since Mon Oct 13 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 * @todo change baseclass to something that both WJTab and WJAccordion extend from
 **/
var WJAccordion = Class.create(WJTab, {
	/**
	 * initialize
	 *
	 * Initialize this WJAccordion
	 *
	 * @since Wed Sep 10 2008
	 * @access public
	 * @return WJTab
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJAccordion";
	},

	/**
	 * click
	 *
	 * Fires the accordionclick event
	 *
	 * @since Mon Oct 13 2008
	 * @access public
	 * @return mixed
	 **/
	click: function() {
		this.fire("aeroplane:accordionclick", {value: this.getValue()});
	}
});

/**
 * WJCheckbox
 *
 * Represents a group of checkboxes
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJCheckbox = Class.create(WJMultivalueInput, {
	/**
	 * initialize
	 *
	 * Initialize this WJCheckbox
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJCheckbox
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJCheckbox";
	},
	
	/**
	 * getValue
	 *
	 * Returns the value if the htmlelement is checked
	 *
	 * @since Fri Jan 02 2009
	 * @access public
	 * @return string
	 **/
	getValue: function($super) {
		if (this.htmlelement.checked) {
			return $super();
		}
	}
});


/**
 * WJGrid
 *
 * A grid component, ie excel-like component
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaer
 * @package Windmill.Javascript.Component
 **/
var WJGrid = Class.create(WJGroup, {
	/**
	 * Properties of WJGrid
	 *
	 * Array rows
	 **/
	
	/**
	 * initialize
	 *
	 * Initialize this WJGrid
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJGrid
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJGrid";
	},

	/**
	 * addRow
	 *
	 * Adds a row to the grid
	 *
	 * @since 
	 * @access public
	 * @param Array data
	 * @return 
	 **/
	addRow: function(data) {
		
	},

	/**
	 * getRows
	 *
	 * Gets all rows in this grid
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getRows: function() {
		
	}
});

/**
 * WJLink
 *
 * A link component
 *
 * @since Mon Oct 13 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJLink = Class.create(WJGroup, {
	/**
	 * initialize
	 *
	 * Initialize this WJLink
	 *
	 * @since Mon Oct 13 2008
	 * @access public
	 * @return WJLink
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJLink";
		this.region = "top";
		this.group = 0; // WJLink has a lot of group properties but it isn't really a group. This makes sure it isn't treated as a group on the XSL level.
	},

	/**
	 * click
	 *
	 * Fires the click event
	 *
	 * @since Mon Oct 13 2008
	 * @access public
	 * @return mixed
	 **/
	click: function() {
		this.fire("aeroplane:click", {value: this.getValue()});
	}
});

/**
 * WJNormalButton
 *
 * Button component that should be displayed as an ordinary <button>
 *
 * @since Mon Dec 29 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJNormalButton = Class.create(WJButtonComponent, {
	/**
	 * initialize
	 *
	 * Initialize this WJNormalButton
	 *
	 * @since Mon Dec 29 2008
	 * @access public
	 * @return WJNormalButton
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJNormalButton";
	}
});

/**
 * WJRichtext
 *
 * Rich text component
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJRichtext = Class.create(WJText, {
	/**
	 * Properties of WJRichtext
	 *
	 * Array allowed
	 **/

	/**
	 * initialize
	 *
	 * Initialize this WJRichtext
	 *
	 * @since Mon Jul 28 2008
	 * @access public
	 * @return void
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJRichtext";
	},
	
	/**
	 * getValue
	 *
	 * Gets the value of this component
	 *
	 * @since Tue Dec 16 2008
	 * @access public
	 * @return string
	 **/
	getValue: function($super) {
		var usesTinyMCE = (typeof(tinyMCE) != "undefined") ? true : false;
		
		if (this.htmlelement && usesTinyMCE && tinymce.EditorManager.get(this.htmlelement.id) && tinymce.EditorManager.get(this.htmlelement.id).getContent) {
			this.value = tinymce.EditorManager.get(this.htmlelement.id).getContent();
		}
		else {
			this.value = $super();
		}
		return this.value;
	}
});

/**
 * WJTabGroup
 *
 * A group of components to display as tabs
 *
 * @since Wed Nov 19 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
WJTabGroup = Class.create(WJGroup, {
	/**
	 * initialize
	 *
	 * Initializes a new WJTabGroup
	 *
	 * @since Wed Nov 19 2008
	 * @access public
	 * @param string id
	 * @return void
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJTabGroup";
	}
});

/**
 * WJThumbnail
 *
 * Component to show a thumbnail
 *
 * @since Tue Oct 21 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJThumbnail = Class.create(WJImage, {
	/**
	 * initialize
	 *
	 * Initialize this WJThumbnail
	 *
	 * @since Tue Oct 21 2008
	 * @access public
	 * @return WJThumbnail
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJThumbnail";
	}
});

/**
 * WJTime
 *
 * Time select component
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJTime = Class.create(WJComponent, {
	
	/**
	 * initialize
	 *
	 * Initialize this WJTime
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJTime
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJTime";
	},

	/**
	 * getHour
	 *
	 * Gets the hour part of value
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getHour: function() {
		
	},

	/**
	 * getMinute
	 *
	 * Gets the minute part of value
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getMinute: function() {
		
	},

	/**
	 * getSecond
	 *
	 * Gets the second part of value
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getSecond: function() {
		
	}
});

/**
 * WJCloseableTab is a WJTab the can be closed 
 *
 * @since Fri Sep 12 2008
 * @author Ron Rademaker
 **/

var WJCloseableTab = Class.create(WJTab, {
	/**
	 * initialize
	 *
	 * Initialize this WJCloseableTab
	 *
	 * @since Fri Sep 12 2008
	 * @access public
	 * @return WJCloseableTab
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJCloseableTab";
	},
	
	/**
	 * click
	 *
	 * Fires the tabclick event, adding a memo for close
	 *
	 * @since Fri Sep 12 2008
	 * @access public
	 * @return mixed
	 **/
	click: function() {
		this.fire("aeroplane:tabclick", {value: this.getValue(), close: false});
	},
	
	/**
	 * closeClick
	 *
	 * Fires the tabclick event, adding a memo for close
	 *
	 * @since Fri Sep 12 2008
	 * @access public
	 * @return mixed
	 **/
	closeClick: function() {
		this.fire("aeroplane:tabclick", {value: this.getValue(), close: true});
	},

	/**
	 * setHtmlelement
	 *
	 * Sets the html element for this component, registers the onClose
	 *
	 * @since Fri Sep 12 2008
	 * @access public
	 * @param htmlelement element
	 * @return void
	 **/
	setHtmlelement: function($super, element) {
		$super(element);
	}
});

/**
 * WJDate
 *
 * Component to enter dates
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJDate = Class.create(WJComponent, {

	/**
	 * initialize
	 *
	 * Initializes this WJDate
	 *
	 * @since Tue Jul 29 2008
	 * @access public
	 * @param integer id
	 * @return void
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJDate";
	},

	/**
	 * getDate
	 *
	 * Get the day of the month
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getDate: function() {
		
	},

	/**
	 * getMonth
	 *
	 * Gets the month of the entered data (1-jan to 12-dec)
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getMonth: function() {
		
	},

	/**
	 * getYear
	 *
	 * Gets the year
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getYear: function() {
		
	},

	/**
	 * getValue
	 *
	 * Gets the value as a string instead of Date object (which is the dijit widget's default)
	 *
	 * @since Tue Aug 05 2008
	 * @access public
	 * @return void
	 **/
	getValue: function($super) {
		var value = $super();
		if (value) {
			this.value = value.toString();
		}
		else {
			this.value = "";
		}
		return this.value;
	}
});

/**
 * WJFlash
 *
 * Component to select a swf (from the mediamanager)
 *
 * @since Wed Feb 18 2009
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJFlash = Class.create(WJFile, {
	/**
	 * initialize
	 *
	 * Initialize this WJFlash
	 *
	 * @since Wed Feb 18 2009
	 * @access public
	 * @return WJImage
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJFlash";
	},

	/**
	 * selectFlash
	 *
	 * Sets the value of this component
	 *
	 * @since Wed Feb 18 2009
	 * @access public
	 * @param string image
	 * @param string path
	 * @param integer section
	 * @return void
	 **/
	selectFlash: function(swf, path, section) {
		this.value = "/var/mediamanager/flash/" + path + "/" + swf;
		if ( $(this.htmlelement.id + "_filename") ) {
			$(this.htmlelement.id + "_filename").update(this.value);
		}
	},

	/**
	 * getValue
	 *
	 * Returns the current flash value
	 *
	 * @since Wed Feb 18 2009
	 * @access public
	 * @return string
	 **/
	getValue: function() {
		return this.value;
	},

	/**
	 * setHtmlelement
	 *
	 * Attaches the open mediamanager event to the wjbutton
	 *
	 * @since Wed Feb 18 2009
	 * @access public
	 * @param element elem
	 * @return void
	 **/
	setHtmlelement: function($super, elem) {
		if (dijit.byId(elem.id) ) {
			$super(elem);
			dijit.byId(elem.id).setObserver(function() {
				aeroplane.openApplication("Mediamanager", this.selectFlash.bind(this), {section: 2500, path: "."} );
			}.bind(this) );
		}
		else {
			this.setHtmlelement.bind(this, elem).delay(0.1);
		}
	}
});

/**
 * WJHtml
 *
 * A html component, used to be able to use contentblocks with components without contentpanes
 *
 * @since Mon Jan 19 2009
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJHtml = Class.create(WJGroup, {
	/**
	 * initialize
	 *
	 * Initialize this WJHtml
	 *
	 * @since Mon Jan 19 2009
	 * @access public
	 * @return WJHtml
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJHtml";
		this.group = 0; // WJHtml has a lot of group properties but it isn't really a group (like WJLink). This makes sure it isn't treated as a group on the XSL level.
	}
});

/**
 * WJLinkButton
 *
 * Button component that should be displayed as a link
 *
 * @since Tue Dec 23 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJLinkButton = Class.create(WJButtonComponent, {
	/**
	 * initialize
	 *
	 * Initialize this WJLinkButton
	 *
	 * @since Tue Dec 23 2008
	 * @access public
	 * @return WJLinkButton
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJLinkButton";
	}
});

/**
 * WJListbox
 *
 * A listbox is component to select multiple options from an option group. (A list where you use CTRL+click to add items to your selection)
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJListbox = Class.create(WJMultivalueInput, {
	
	/**
	 * initialize
	 *
	 * Initialize this WJListbox
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJListbox
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJListbox";
	}
});

/**
 * WJList
 *
 * Javascript component to sort data with (so it's an ordered list)
 *
 * @since Mon Sep 01 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJList = Class.create(WJComponent, {
	/**
	 * initialize
	 *
	 * Initialize this WJList
	 *
	 * @since Mon Sep 01 2008
	 * @access public
	 * @return void
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJList";
	}
});

/**
 * WJOptionGroup
 *
 * Collection of options
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJOptionGroup = Class.create({
	/**
	 * Properties of WJOptionGroup
	 *
	 * Array options
	 **/
	
	/**
	 * initialize
	 *
	 * Creates a new WJOptionGroup
	 *
	 * @since Mon Jul 21 2008
	 * @access public
	 * @return void
	 **/
	initialize: function() { 
		this.options = new Array();
		this.componentType = "WJOptionGroup";
	},

	/**
	 * getOptions
	 *
	 * Gets the options in this group
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	getOptions: function() {
		return this.options;
	},

	/**
	 * addOption
	 *
	 * Adds a WJOption
	 *
	 * @since 
	 * @access public
	 * @param WJOption option
	 * @return 
	 **/
	addOption: function(option) {
		this.options.push(option);
	}
});

/**
 * WJOption
 *
 * An option (for restricted input)
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJOption = Class.create({
	/**
	 * Properties of WJOption
	 *
	 * WJLabel label
	 * string value
	 **/

	/**
	 * initialize
	 *
	 * Creates a new WJOption
	 *
	 * @since Mon Jul 21 2008
	 * @access public
	 * @param string value
	 * @param WJLabel label
	 * @return WJOption
	 **/
	initialize: function(value, label) {
		this.label = label;
		this.value = value;
		this.componentType = "WJOption";
	}
});

/**
 * WJPassword
 *
 * Textbox for password input
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJPassword = Class.create(WJText, {
	/**
	 * initialize
	 *
	 * Initialize this WJPassword
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return void
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJPassword";
	}
});

/**
 * WJProgressbar
 *
 * A progressbar component
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJProgressbar = Class.create(WJComponent, {
	/**
	 * Properties of WJProgressbar
	 *
	 * float start
	 * float end
	 * float interval
	 * WJOptionGroup optiongroup
	 **/
	
	/**
	 * initialize
	 *
	 * Initialize this WJProgressbar
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJProgressbar
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJProgressbar";
	},

	/**
	 * setOptiongroup
	 *
	 * Sets the option group.
	 *
	 * @since 
	 * @access public
	 * @param WJOptionGroup optiongroup
	 * @return 
	 **/
	setOptiongroup: function(optiongroup) {
		
	},

	/**
	 * step
	 *
	 * Progresses the progressbar with one step
	 *
	 * @since 
	 * @access public
	 * @return 
	 **/
	step: function() {
		
	},

	/**
	 * setProgress
	 *
	 * Sets the progressbar to percentage completed
	 *
	 * @since 
	 * @access public
	 * @param integer percentage
	 * @return 
	 **/
	setProgress: function(percentage) {
		
	}
});

/**
 * WJRadio
 *
 * Represents a group of radio buttons
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJRadio = Class.create(WJSinglevalueInput, {
	/**
	 * initialize
	 *
	 * Initialize this WJRadio
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJRadio
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJRadio";
		this.htmlelement = [];
	},

	/**
	 * setHtmlelement
	 *
	 * Sets the html element for this component, radio components are likely to have multiple elements
	 *
	 * @since Thu Oct 16 2008
	 * @access public
	 * @param htmlelement element
	 * @return void
	 **/
	setHtmlelement: function(element) {
		this.htmlelement.push(element);
	},
	
	/**
	 * getValue
	 *
	 * Gets the value of this component
	 *
	 * @since Thu Oct 16 2008
	 * @access public
	 * @return string
	 **/
	getValue: function() {
		for (var i = 0; i < this.htmlelement.length; i++) {
			if (this.htmlelement[i] && dijit.byId(this.htmlelement[i].id) && dijit.byId(this.htmlelement[i].id).getValue) {
				if (dijit.byId(this.htmlelement[i].id).checked) {
					this.value = dijit.byId(this.htmlelement[i].id).getValue();
				}
			}
		}
		return this.value;
	}
});

/**
 * WJSelect
 *
 * Select, dropdown, combobox component
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJSelect = Class.create(WJSinglevalueInput, {
	/**
	 * initialize
	 *
	 * Initialize this WJSelect
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJSelect
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJSelect";
	}
});

/**
 * WJSlider
 *
 * Slider component to select a value from an option group
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJSlider = Class.create(WJSinglevalueInput, {
	
	/**
	 * initialize
	 *
	 * Initialize this WJSlider
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJSlider
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJSlider";
	}
});

/**
 * WJTagCloud
 *
 * Tagcloud input component
 *
 * @since Tue Oct 21 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJTagCloud = Class.create(WJText, {
	/**
	 * initialize
	 *
	 * Initialize this WJTagCloud
	 *
	 * @since Tue Oct 21 2008
	 * @access public
	 * @return void
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJTagCloud";
	}
});

/**
 * WJTree is a tree component. Used to organize other components in an explicit tree (they already are in a implicit tree!)
 *
 * @since Tue Aug 12 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJTree = Class.create(WJLabel, {
	/**
	 * initialize
	 *
	 * Creates a new WJTree
	 *
	 * @since 
	 * @access public
	 * @param string id
	 * @return 
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJTree";
	}
});

/**
 * WJUrltext
 *
 * Textbox for URL / link selection 
 *
 * @since Wed Jul 16 2008
 * @revision $Revision$
 * @author Ron Rademaker
 * @package Windmill.Javascript.Component
 **/
var WJUrltext = Class.create(WJText, {
	
	/**
	 * initialize
	 *
	 * Initialize this WJUrltext
	 *
	 * @since Fri Aug 22 2008
	 * @access public
	 * @return WJUrltext
	 **/
	initialize: function($super, id) {
		$super(id);
		this.componentType = "WJUrltext";
	}
});

/**
 * WJCBGooglemaps
 *
 * WJCBGooglemaps handles a google map CB
 *
 * @since Tue Dec 30 2008
 * @revision $Revision$
 * @author Reyo Stallenberg
 * @package Windmill.Javascript.Contentblock
 **/
var WJCBGooglemaps = Class.create({
	/**
	 * Properties of WJCBGooglemaps
	 *
	 * Hash _overlays
	 * Hash _forms
	 **/
	_overlays: new Hash(),
	_forms: new Hash(),
	_selectobservers: new Hash(),
	_loader: null,

	/**
	 * initialize
	 *
	 * Creates a new WJCBGooglemaps
	 *
	 * @since Tue Dec 30 2008
	 * @access public
	 * @param string id
	 * @return new WJCBGooglemaps
	 **/
	initialize: function(elementid, latitude, longitude, zoom, sitename) {
		this._mapelement = $(elementid);
		this._mapelement.style.backgroundImage = "";

		this._center = new GLatLng(latitude, longitude);
		this._zoom = zoom;
		this._sitename = sitename;

		this._zoomtobounds = false;
		this._bounds = new GLatLngBounds();

		this._addpositioncontrol = true;
		this._positioncontrol = (this._mapelement.getHeight() < 300) ? new GSmallMapControl : new GLargeMapControl();

		this._map = new GMap2(this._mapelement);

		this._map.addMapType(G_PHYSICAL_MAP);
		var hierarchy = new GHierarchicalMapTypeControl();
		hierarchy.addRelationship(G_SATELLITE_MAP, G_HYBRID_MAP, null, true);

		this._addtypecontrol = true;
		this._typecontrol = hierarchy;

		this._enabledoubleclickzoom = true;
		this._enablecontinuouszoom = true;

		this.currentrequests = 0;
	},

	/**
	 * setLoader
	 *
	 * Gives possibility to add a loader
	 *
	 * @since Tue Apr 7 2009
	 * @access public
	 * @param element
	 * @return void
	 **/
	setLoader: function(element) {
		this._loader = element;
	},


	/**
	 * getLoader
	 *
	 * Get the loader
	 *
	 * @since Tue Apr 7 2009
	 * @access public
	 * @return element
	 **/
	getLoader: function() {
		return this._loader;
	},

	/**
	 * loaderShowHide
	 *
	 * Checks if it should show or hide the loader (only adds classname)
	 *
	 * @since Tue Apr 7 2009
	 * @access public
	 * @return void
	 **/
	 loaderShowHide: function() {
		if (this.getLoader() && Object.isElement(this.getLoader() ) ) {
			if (this.currentrequests > 0) {
				this.getLoader().addClassName("maploadershow");
			}
			else {
				this.getLoader().removeClassName.bind(this.getLoader() ).defer("maploadershow");
			}
		}
	 },

	/**
	 * changeSettings
	 *
	 * Gives possibility to overwrite some settings
	 *
	 * @since Tue Feb 10 2009
	 * @access
	 * @param
	 * @return
	 **/
	changeSettings: function(settings) {
		for (key in settings) {
			this[key] = settings[key];
		}
	},

	/**
	 * applySettings
	 *
	 * Applies the settings for the map
	 *
	 * @since Tue Feb 10 2009
	 * @access public
	 * @param
	 * @return
	 **/
	applySettings: function() {
		this._map.setCenter(this._center, this._zoom);

		if (this._enabledoubleclickzoom) {
			this._map.enableDoubleClickZoom();
		}
		if (this._enablecontinuouszoom) {
			this._map.enableContinuousZoom();
		}
		if (this._addpositioncontrol) {
			this._map.addControl(this._positioncontrol);
		}
		if (this._addtypecontrol) {
			this._map.addControl(this._typecontrol);
		}

		this._addZoomListener();
	},

	/**
	 * addZoomListener
	 *
	 * Adds a listener to be able to reposition the custom infowindows
	 *
	 * @since Tue Feb 10 2009
	 * @access protected
	 * @return void
	 **/
	_addZoomListener: function() {
		GEvent.addListener(this._map, "zoomend", function() {
			this._overlays.findAll(function(overlay) {
				return (typeof(overlay.value.infowindow) != "undefined" && overlay.value.infowindow.isVisible() );
			} ).each(function(overlay) {
				this.positionInfowindowRelativeToMarker(overlay.value);
			}.bind(this) );
		}.bind(this) );
	},

	/**
	 * pushOverlay
	 *
	 * Stacks the settings of an overlay in this._overlays
	 *
	 * @since Tue Dec 30 2008
	 * @access public
	 * @param String key
	 * @param String overlay
	 * @param Boolean active
	 * @param Boolean filterable
	 * @return void
	 **/
	pushOverlay: function(key, overlay, active, parser, filterable) {
		var active = (active == 1) ? true : false;
		this.setOverlay(key, {
			"baseurl": overlay,
			"url": overlay,
			"active": active,
			"parser": parser,
			"filterable": filterable
		});
	},

	/**
	 * setOverlay
	 *
	 * Adds an item to this._overlays
	 *
	 * @since Fri Jan 2 2009
	 * @access public
	 * @param string key
	 * @param object value
	 * @return void
	 **/
	setOverlay: function(key, value) {
		this._overlays.set(key, value);
	},

	/**
	 * getOverlay
	 *
	 * Gets an item from this._overlays
	 *
	 * @since Fri Jan 2 2009
	 * @access public
	 * @param string key
	 * @return object
	 **/
	getOverlay: function(key) {
		return this._overlays.get(key);
	},

	/**
	 * addOverlays
	 *
	 * Executes addOverlay for the active overlays on this map
	 *
	 * @since Tue Dec 30 2008
	 * @access public
	 * @return void
	 **/
	addOverlays: function() {
		this._fillFormsValues();
		this._overlays.each(function(overlay) {
			if (typeof(overlay.value.geoxml) == "undefined") {
				this.getNewGeoXml(overlay);
			}
			else if (overlay.value.active) {
				this.addOverlay(overlay)
			}
		}.bind(this) );
	},

	/**
	 * addOverlay
	 *
	 * Adds the parsed overlay to the map
	 *
	 * @since Fri Jan 30 2009
	 * @access public
	 * @param Hash overlay
	 * @return void
	 **/
	addOverlay: function(overlay) {
		this._map.addOverlay(overlay.value.geoxml);
		overlay.value.lasturl = overlay.value.url;
	},

	/**
	 * getNewGeoXml
	 *
	 * Adds the overlay with the correct parser
	 *
	 * @since Thu Jan 29 2009
	 * @access public
	 * @param Hash overlay
	 * @return void
	 **/
	getNewGeoXml: function(overlay) {
		if (overlay.value.active) {
			if (overlay.value.parser == "GGeoXml") {
				overlay.value.geoxml = new GGeoXml(overlay.value.url);
				this.addOverlay(overlay);
			}
			else {
				this.parseOverlay(overlay);
				var handle = GEvent.addListener(overlay.value.geoxml, "parsed", function (overlay) {
					if (overlay.value.active) {
						this.addOverlay(overlay);
					}
					this.currentrequests--;
					this.loaderShowHide();
					GEvent.removeListener(handle);
				}.bind(this, overlay) );
			}
		}
	},

	/**
	 * hideInfoWindows
	 *
	 * Hides all visible infowindows
	 *
	 * @since Mon Feb 9 2009
	 * @access public
	 * @return void
	 **/
	hideInfoWindows: function() {
		this._overlays.findAll(function(overlay) {
			return (typeof(overlay.value.infowindow) != "undefined");
		} ).each(function(overlay) {
			overlay.value.infowindow.hide();
		});
	},

	/**
	 * createInfoWindow
	 *
	 * Creates an infowindow for the overlay on map
	 *
	 * @since Mon Feb 2 2009
	 * @access public
	 * @return WJWindowGooglemaps
	 **/
	createInfoWindow: function(overlay, map) {
		var callback = function(wjwindow, event, answer) {
			wjwindow.hide();
			event.stop();
		};

		var infowindow = new WJWindowGooglemaps(new WJWindow(callback, $(map.getPane(G_MAP_FLOAT_PANE) ) ), overlay.key, this._sitename);

		var infowindowelement = infowindow.getWindowElement();

		var stopInfowindowEvents = function(event) {
			Event.stop(event);
		}
		GEvent.bindDom(infowindowelement, "mousedown", this, stopInfowindowEvents);
		GEvent.bindDom(infowindowelement, "dblclick", this, stopInfowindowEvents);
		return infowindow;
	},

	/**
	 * removeOverlays
	 *
	 * Executes removeOverlay for the non active overlays on this map
	 *
	 * @since Fri Jan 2 2009
	 * @access public
	 * @return void
	 **/
	removeOverlays: function() {
		this._overlays.each(function(overlay) {
			if (typeof(overlay.value.geoxml) != "undefined" && overlay.value.filterable) {
				if (typeof(overlay.value.infowindow) != "undefined") {
					overlay.value.infowindow.hide();
				}
				this._map.removeOverlay(overlay.value.geoxml);
			}
			if (overlay.value.lasturl != overlay.value.url && overlay.value.filterable) {
				this.getNewGeoXml(overlay);
			}
		}.bind(this) );
	},

	/**
	 * removeOverlay
	 *
	 * Removes a single overlay
	 *
	 * @since Fri Jan 2 2009
	 * @access public
	 * @param string key
	 * @return void
	 **/
	removeOverlay: function(key) {
		var overlay = this._overlays.get(key);
		if (overlay.active) {
			overlay.active = false;
			this._map.removeOverlay(overlay.geoxml);
		}
	},

	/**
	 * addMainFormObserver
	 *
	 * Adds a form observer for the element retrieved by elementid
	 *
	 * @since Fri Jan 2 2009
	 * @access public
	 * @param string elementid
	 * @return void
	 **/
	addMainFormObserver: function(elementid) {
		var mainform = $(elementid);
		mainform.getInputs().each(function(element) {
			$(element).observe("click", function(event) {
				this._handleMainFormChange(event);
			}.bind(this) );
		}.bind(this) );
//		Event.observe(mainform, "change", this._handleMainFormChange.bind(this) );
		this._mainform = mainform;
	},

	/**
	 * addFormObserver
	 *
	 * Adds a form observer for the given form identified by name
	 *
	 * @since Tue Jan 13 2009
	 * @access public
	 * @param string formname
	 * @return void
	 **/
	addFormObserver: function(formname) {
		var additionalform = $(document[formname] );
		if (Object.isElement(additionalform) ) {
			additionalform.getElements().each(function(element) {
				if (element.tagName.toLowerCase() == "select") {
					this._selectobservers.set(element.name, Form.Element.getValue(element) );
				}
				$(element).observe("click", function(event) {
					var eventelement = Event.findElement(event, "select");
					if (eventelement != document) {
						var value =  Form.Element.getValue(eventelement);
						if (value != this._selectobservers.get(eventelement.name) ) {
							this._selectobservers.set(eventelement.name, value);
							this._handleChange(event);
						}
					}
					else {
						this._handleChange(event);
					}
				}.bind(this) );
			}.bind(this) );
// 			Event.observe(additionalform, "change", this._handleChange.bind(this) );
			Event.observe(additionalform, "submit", this._handleSubmit.bind(this) );
			this.setForm(additionalform.name, additionalform);
		}
	},

	/**
	 * _handleSubmit
	 *
	 * Handles a form submit
	 *
	 * @since Thu Jan 15 2009
	 * @access protected
	 * @param Event event
	 * @return void
	 **/
	_handleSubmit: function(event) {
		this._handleChange(event);
		Event.stop(event);
	},

	/**
	 * _handleChange
	 *
	 * Handles the change to the form
	 *
	 * @since Fri Jan 2 2009
	 * @access protected
	 * @param Event event
	 * @return void
	 **/
	_handleChange: function(event) {
		this._fillFormsValues();
		this.redrawOverlays();
	},

	/**
	 * _fillFormsValues
	 *
	 * Changes the urls of the overlays depending on the this._forms values
	 *
	 * @since Wed Jan 21 2009
	 * @access protected
	 * @return void
	 **/
	_fillFormsValues: function() {
		var filter = this._forms.values().invoke("serialize").join("&");
		this._overlays.values().each(function(filter, overlay) {
			if (overlay.filterable && filter != "") {
				overlay.url = overlay.baseurl + ( (overlay.baseurl.indexOf("?") == -1) ? "?" : "&") + filter;
			}
			else {
				overlay.url = overlay.baseurl;
			}
		}.bind(this, filter) );
	},

	/**
	 * redrawOverlays
	 *
	 * Removes and adds all overlays
	 *
	 * @since Thu Jan 15 2009
	 * @access public
	 * @return void
	 **/
	redrawOverlays: function() {
		this.removeOverlays();
		this.addOverlays();
	},

	/**
	 * _handleMainFormChange
	 *
	 * Handles the change to the main form
	 *
	 * @since Thu Jan 15 2009
	 * @access protected
	 * @param Event event
	 * @return void
	 **/
	_handleMainFormChange: function(event) {
		var checkbox = Event.element(event);
		this.hideInfoWindows();

		this._overlays.each(function(checkbox, overlay) {
			if (checkbox.name == overlay.key) {
				if (checkbox.checked) {
					overlay.value.active = true;
					if (typeof(overlay.value.geoxml) == "undefined") {
						this.getNewGeoXml(overlay);
					}
					else if ( (overlay.value.url != overlay.value.lasturl) && overlay.value.filterable) {
						this.removeOverlay(checkbox.name);
						this.getNewGeoXml(overlay);
					}
					else {
						this.addOverlay(overlay);
					}
				}
				else {
					this.removeOverlay(checkbox.name);
					overlay.value.active = false;
				}
			}
		}.bind(this, checkbox) );
	},

	/**
	 * setForm
	 *
	 * Adds an item to this._forms
	 *
	 * @since Fri Jan 2 2009
	 * @access public
	 * @param string key
	 * @param object value
	 * @return void
	 **/
	setForm: function(key, value) {
		this._forms.set(key, value);
	},

	/**
	 * getForm
	 *
	 * Gets an item from this._forms
	 *
	 * @since Fri Jan 2 2009
	 * @access public
	 * @param string key
	 * @return element
	 **/
	getForm: function(key) {
		return this._forms.get(key);
	},

	/**
	 * parseOverlay
	 *
	 * Creates a new GOverlay and downloads kml
	 *
	 * @since Mon Feb 9 2009
	 * @access public
	 * @param Hash overlay
	 * @return GOverlay
	 **/
	parseOverlay: function(overlay) {
		overlay.value.data = {};
		overlay.value.styles = {};
		overlay.value.geoxml = {};
		overlay.value.geoxml.prototype = new GOverlay();
		overlay.value.geoxml.initialize = this.addOverlayToMap.bind(this, overlay);
		overlay.value.geoxml.remove = this.removeOverlayFromMap.bind(this, overlay);
		overlay.value.geoxml.redraw = this.redrawOverlayOnMap.bind(this, overlay);
		overlay.value.geoxml.copy = this.copyOverlayOfMap.bind(this, overlay);
		this.currentrequests++;
		this.loaderShowHide();
		GDownloadUrl(overlay.value.url, this.handleKMLRequest.bind(this, overlay) );
		return overlay.value.geoxml;
	},

	/**
	 * showInfowindow
	 *
	 * Handles the showing of a custom infowindow
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param GMarker m
	 * @param Object overlayvalue
	 * @param String name
	 * @param Element desc
	 * @param String url
	 * @return void
	 **/
	showInfowindow: function(m, overlayvalue, name, desc, url) {
		overlayvalue.infowindow.setContent(desc);
		overlayvalue.lastmarker = m;
		if (typeof(url) != "undefined") {
			var spincomplete = function(overlayvalue, content) {
				overlayvalue.infowindow.setContent(content);
				overlayvalue.infowindow.setLoading(false);
				overlayvalue.infowindow.setHeight(450);
				var point = this.positionInfowindowRelativeToMarker(overlayvalue);
				this._map.panTo(this._map.fromDivPixelToLatLng(point) );
			};
			var spin = new WJSpin();
			overlayvalue.infowindow.setLoading(true);
			spin.content(url, [spincomplete.bind(this, overlayvalue)]);
		}
		overlayvalue.infowindow.setHeight("auto");
		overlayvalue.infowindow.show();
		var point = this.positionInfowindowRelativeToMarker(overlayvalue);
		if (typeof(url) == "undefined") {
			this._map.panTo(this._map.fromDivPixelToLatLng(point) );
		}
	},

	/**
	 * addOverlayToMap
	 *
	 * Creates items to put on the map
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param Hash overlay
	 * @param GMap2 map
	 * @return void
	 **/
	addOverlayToMap: function(overlay, map) {
		var data = overlay.value.data;

		if (typeof(overlay.value.infowindow) == "undefined") {
			overlay.value.infowindow = this.createInfoWindow(overlay, map);
		}

		// This shouldn't be needed, check why the overlays get duplicated
		this.removeOverlayFromMap(overlay);

		data.gpolygons = [];
		data.gpolylines = [];
		data.gmarkers = [];

		if (typeof(data.polylines) != "undefined") {
			data.polylines.each(function(map, overlay, polyline, key){
				this.createPolyline(polyline, overlay);
			}.bind(this, map, overlay) );
		}

		if (typeof(data.polygons) != "undefined") {
			data.polygons.each(function(map, overlay, polygon, key){
				this.createPolygon(polygon, overlay);
			}.bind(this, map, overlay) );
		}

		if (typeof(data.markers) != "undefined") {
			data.markers.each(function(map, overlay, marker, key){
				this.createMarker(marker, overlay);
			}.bind(this, map, overlay) );
		}

		if (this._zoomtobounds) {
			this._map.setZoom(this._map.getBoundsZoomLevel(this._bounds) );
			this._map.setCenter(this._bounds.getCenter() );
		}
	},

	/**
	 * removeOverlayFromMap
	 *
	 * Removes items of overlay from the map
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param Hash overlay
	 * @return void
	 **/
	removeOverlayFromMap: function(overlay) {
		var data = overlay.value.data;

		if (typeof(data.gpolylines) != "undefined") {
			data.gpolylines.each(function(polyline, key){
				this._map.removeOverlay(polyline);
			}.bind(this) );
		}

		if (typeof(data.gpolygons) != "undefined") {
			data.gpolygons.each(function(polygon, key){
				this._map.removeOverlay(polygon);
			}.bind(this) );
		}

		if (typeof(data.gmarkers) != "undefined") {
			data.gmarkers.each(function(marker, key){
				this._map.removeOverlay(marker);
			}.bind(this) );
		}
	},

	/**
	 * redrawOverlayOnMap
	 *
	 * Redraws the overlay on the map (not implemented, but required in GOverlay, so therefore here)
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param Hash overlay
	 * @param Boolean force
	 * @return void
	 * @see WJCBGooglemaps.parseOverlay
	 **/
	redrawOverlayOnMap: function(overlay, force) {
	},

	/**
	 * copyOverlayOfMap
	 *
	 * Copies an empty version of the overlay (not implemented, but required in GOverlay, so therefore here)
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @return void
	 * @see WJCBGooglemaps.parseOverlay
	 **/
	copyOverlayOfMap: function () {
	},

	/**
	 * createMarker
	 *
	 * Creates a single marker
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param Object data
	 * @param Hash overlay
	 * @return void
	 **/
	createMarker: function(data, overlay) {
		var overlayvalue = overlay.value;
		var point = data.point;
		var name = data.name;
		var desc = data.desc;
		var style = data.styleInfo;
		var url;
		if (data.spin != "") {
			url = new WJUrl(data.spin.toQueryParams() , "/index.php");
		}

		var icon = overlayvalue.styles[style];

		var marker = new GMarker(point, icon);

		if (name != "" && desc != "") {
			GEvent.addListener(marker, "click", function(m, overlayvalue, name, desc, url) {
				if (m == overlayvalue.lastmarker) {
					overlayvalue.infowindow.show();
					var point = this.positionInfowindowRelativeToMarker(overlayvalue);
					this._map.panTo(this._map.fromDivPixelToLatLng(point) );
				}
				else {
					this.hideInfoWindows();
					overlayvalue.infowindow.setBaseTitle("");
					overlayvalue.infowindow.setContent("");

					overlayvalue.infowindow.setBaseTitle(name);

					var wrapper = new Element("div").update(desc);
					var contentimages = wrapper.select("img");
					overlayvalue.infowindow.contentimagescounter = contentimages.size();
					if (overlayvalue.infowindow.contentimagescounter > 0) {
						var countdown = function(event, m, html, iwoptions, name, desc, url) {
							overlayvalue.infowindow.contentimagescounter--;
							if (overlayvalue.infowindow.contentimagescounter == 0) {
								this.showInfowindow(m, overlayvalue, name, wrapper, url);
							}
						}.bindAsEventListener(this, m, overlayvalue, name, wrapper, url);

						contentimages.invoke("observe", "load", countdown).invoke("observe", "error", countdown);
					}
					else {
						this.showInfowindow(m, overlayvalue, name, desc, url);
					}
				}
			}.bind(this, marker, overlayvalue, name, desc, url) );
		}
		this._map.addOverlay(marker);
		overlayvalue.data.gmarkers.push(marker);
	},

	/**
	 * createPolyline
	 *
	 * Creates a single polyline
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param Object data
	 * @param Hash overlay
	 * @return void
	 **/
	createPolyline: function(data, overlay) {
		var overlayvalue = overlay.value;
		var points = data.points;
		var color = data.color;
		var width = data.width;
		var opacity = data.opacity;
		var pbounds = data.pbounds;
		var name = data.name;
		var desc = data.desc;
		var p = new GPolyline(points, color, width, opacity);
		this._map.addOverlay(p);
		overlayvalue.data.gpolylines.push(p);
	},

	/**
	 * createPolygon
	 *
	 * Creates a single polygon
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param Object data
	 * @param Hash overlay
	 * @return void
	 **/
	createPolygon: function(data, overlay) {
		var overlayvalue = overlay.value;
		var pbounds = data.pbounds;
		var name = data.name;
		var desc = data.desc;

		var p = new GPolygon(data.points, data.color, data.width, data.opacity, data.fillcolor, data.fillopacity);

		if (name != "" && desc != "") {
			GEvent.addListener(p, "click", function(p, pbounds, overlayvalue, name, desc, clicked) {
				this.hideInfoWindows();
				overlayvalue.infowindow.setBaseTitle("");
				overlayvalue.infowindow.setContent("");

				overlayvalue.infowindow.setBaseTitle(name);

				var wrapper = new Element("div").update(desc);
				var contentimages = wrapper.select("img");
				overlayvalue.infowindow.contentimagescounter = contentimages.size();
				if (overlayvalue.infowindow.contentimagescounter > 0) {
					var countdown = function(event, p, pbounds, overlayvalue, name, desc) {
						overlayvalue.infowindow.contentimagescounter--;
						if (overlayvalue.infowindow.contentimagescounter == 0) {
							this.positionInfowindowRelativeToPoint(overlayvalue, clicked);
							overlayvalue.infowindow.setContent(wrapper);
							overlayvalue.infowindow.show();
							this._map.panTo(clicked);
						}
					}.bindAsEventListener(this, p, pbounds, overlayvalue, name, wrapper);

					contentimages.invoke("observe", "load", countdown).invoke("observe", "error", countdown);
				}
				else {
					this.positionInfowindowRelativeToPoint(overlayvalue, clicked);
					overlayvalue.infowindow.setContent(wrapper);
					overlayvalue.infowindow.show();
					this._map.panTo(clicked);
				}
			}.bind(this, p, pbounds, overlayvalue, name, desc) );
		}

		overlayvalue.data.gpolygons.push(p);
		this._map.addOverlay(p);
	},

	/**
	 * handleKMLRequest
	 *
	 * Handles the response of GDownloadUrl
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param Hash overlay
	 * @param String doc
	 * @param Integer responseCode
	 * @return void
	 **/
	handleKMLRequest: function(overlay, doc, responseCode) {
		if(responseCode == 200) {
			this.processKML(GXml.parse(doc), overlay);
		} else if(responseCode == -1) {
			WJDebugger.log(WJDebugger.ERROR, "Data request timed out. Please try later.");
		} else {
			WJDebugger.log(WJDebugger.ERROR, "Request resulted in error. Check XML file is retrievable.");
		}
	},

	/**
	 * processKML
	 *
	 * Processes the downloaded kml
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param Document xmlDoc
	 * @param Hash overlay
	 * @return void
	 **/
	processKML: function(xmlDoc, overlay) {
		if (xmlDoc.text != "") {
			overlay.value.styles = this.readStyles(xmlDoc.documentElement.getElementsByTagName("Style") );
			overlay.value.data = this.readData(xmlDoc.documentElement.getElementsByTagName("Placemark"), overlay.value.styles);
			overlay.value.groundoverlays = this.readGroundOverlays(xmlDoc.documentElement.getElementsByTagName("GroundOverlay") );
			GEvent.trigger(overlay.value.geoxml, "parsed");
		}
	},

	/**
	 * readData
	 *
	 * Reads out placemarks (polygons, polylines and markers)
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param DomNode placemarks
	 * @param Object styles
	 * @return Object
	 * @todo Refactoring and extraction
	 **/
	readData: function(placemarks, styles) {
		var data = {};
		data.polylines = [];
		data.polygons = [];
		data.markers = [];

		// Read through the Placemarks
		for (var i = 0; i < placemarks.length; i++) {
			var name = this.readKMLValue(placemarks[i].getElementsByTagName("name")[0] );
			var desc = this.readKMLValue(placemarks[i].getElementsByTagName("description")[0] );
			if (typeof(placemarks[i].getElementsByTagNameNS ) != "undefined") {
				var spin = this.readKMLValue(placemarks[i].getElementsByTagNameNS("http://www.connectholland.nl/kml/", "spin")[0] );
			}
			else {
				var spin = this.readKMLValue(placemarks[i].getElementsByTagName("chkml:spin").item(0) );
			}
			var style = this.readKMLValue(placemarks[i].getElementsByTagName("styleUrl")[0] );
			var coords = GXml.value(placemarks[i].getElementsByTagName("coordinates")[0]);
			coords = coords.replace(/\s+/g," "); // tidy the whitespace
			coords = coords.replace(/^ /,"");    // remove possible leading whitespace
			coords = coords.replace(/ $/,"");    // remove possible trailing whitespace
			coords = coords.replace(/, /,",");   // tidy the commas

			var path = coords.split(" ");
			// Is this a polyline/polygon?
			if (path.length > 1) {
				// Build the list of points
				var points = [];
				var pbounds = new GLatLngBounds();
				for (var p = 0; p < path.length - 1; p++) {
					var bits = path[p].split(",");
					if (bits[1] != "") {
						var point = new GLatLng(parseFloat(bits[1] ), parseFloat(bits[0] ) );
						points.push(point);
						this._bounds.extend(point);
						pbounds.extend(point);
					}
				}

				var linestring = placemarks[i].getElementsByTagName("LineString");
				if (linestring.length) {
					// it's a polyline grab the info from the style
					if (!!styles[style]) {
						var width = styles[style].width;
						var color = styles[style].color;
						var opacity = styles[style].opacity;
					} else {
						var width = 5;
						var color = "#0000ff";
						var opacity = 0.45;
					}
					data.polylines.push( {
						points: points,
						width: width,
						color: color,
						opacity: opacity,
						pbounds: pbounds,
						name: name,
	 					desc: desc
					} );
				}

				var polygons = placemarks[i].getElementsByTagName("Polygon");
				if (polygons.length) {
					// it's a polygon grab the info from the style
					if (!!styles[style]) {
						var width = styles[style].width;
						var color = styles[style].color;
						var opacity = styles[style].opacity;
						var fillopacity = styles[style].fillopacity;
						var fillcolor = styles[style].fillcolor;
					} else {
						var width = 5;
						var color = "#0000ff";
						var opacity = 0.45;
						var fillopacity = 0.25;
						var fillcolor = "#0055ff";
					}

					data.polygons.push( {
						points: points,
						color: color,
						width: width,
						opacity: opacity,
						fillcolor: fillcolor,
						fillopacity: fillopacity,
						pbounds: pbounds,
						name: name,
						desc: desc
					} );
				}
			} else {
				// It's not a poly, so I guess it must be a marker
				var bits = path[0].split(",");

				if (bits[1] != "" && typeof(bits[1]) != "undefined") {
					var point = new GLatLng(parseFloat(bits[1]),parseFloat(bits[0]));
					this._bounds.extend(point);
					data.markers.push( {
						point: point,
						name: name,
						desc: desc,
						styleInfo: style,
						spin: spin
					} );
				}
			}
		}
		return data;
	},


	/**
	 * readData
	 *
	 * Reads out groundoverlays and adds them to the map
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param DomNode grounds
	 * @return Array
	 * @todo Refactoring
	 **/
	readGroundOverlays: function(grounds) {
		var parsedGroundOverlays = [];
		// Scan through the Ground Overlays
		for (var i = 0; i < grounds.length; i++) {
			var url = this.readKMLValue(grounds[i].getElementsByTagName("href")[0] );
			var north = parseFloat(this.readKMLValue(grounds[i].getElementsByTagName("north")[0] ) );
			var south = parseFloat(this.readKMLValue(grounds[i].getElementsByTagName("south")[0] ) );
			var east = parseFloat(this.readKMLValue(grounds[i].getElementsByTagName("east")[0] ) );
			var west = parseFloat(this.readKMLValue(grounds[i].getElementsByTagName("west")[0] ) );
			var sw = new GLatLng(south, west);
			var ne = new GLatLng(north, east);
			var ground = new GGroundOverlay(url, new GLatLngBounds(sw, ne) );
			this._bounds.extend(sw);
			this._bounds.extend(ne);
			parsedGroundOverlays.push(ground);
		}
		return parsedGroundOverlays;
	},

	/**
	 * readStyles
	 *
	 * Reads out styles
	 *
	 * @since Mon Feb 09 2009
	 * @access public
	 * @param DomNode styles
	 * @return Object
	 * @todo Refactoring and extraction
	 **/
	readStyles: function(styles) {
		var parsedStyles = {};
		for (var i = 0; i <styles.length; i++) {
			var styleID = styles[i].getAttribute("id");
			var iconstyle = styles[i].getElementsByTagName("IconStyle");
			var iconsizeX = 32;
			var iconsizeY = 32;
			var shadowWidth = 59;
			var scale = 1;

			if (iconstyle.length > 0) {
				var scalestyle = iconstyle[0].getElementsByTagName("scale");
				if (scalestyle.length > 0) {
					var scale = this.readKMLValue(scalestyle[0] );
					var iconsizeX = iconsizeX * scale;
					var iconsizeY = iconsizeY * scale;
					var shadowWidth = shadowWidth * scale;
				}

				var anchorX = iconsizeX * 0.5;
				var anchorY = iconsizeY;
				var iwanchorX = iconsizeX * 0.5;
				var iwanchorY = 0;

				var hotspot = iconstyle[0].getElementsByTagName("hotSpot");
				if (hotspot.length > 0) {
					var hotspotpos = this.calculateHotSpot(hotspot[0], iconsizeX, iconsizeY, anchorX, anchorY);
					anchorX = hotspotpos.x;
					anchorY = hotspotpos.y;
				}

				if (typeof(iconstyle[0].getElementsByTagNameNS ) != "undefined") {
					var iwhotspot = iconstyle[0].getElementsByTagNameNS("http://www.connectholland.nl/kml/", "infowindowHotSpot");
					var imagemapnode = iconstyle[0].getElementsByTagNameNS("http://www.connectholland.nl/kml/", "area");
				}
				else {
					var iwhotspot = iconstyle[0].getElementsByTagName("chkml:infowindowHotSpot");
					//image map is not working in that program
				}

				if (iwhotspot.length > 0) {
					var iwhotspotpos = this.calculateHotSpot(iwhotspot[0], iconsizeX, iconsizeY, iwanchorX, iwanchorY);
					iwanchorX = iwhotspotpos.x;
					iwanchorY = iwhotspotpos.y;
				}

				if ( (typeof(imagemapnode) != "undefined") && (imagemapnode.length > 0) ) {
					var imageMapType = imagemapnode.item(0).getAttribute("shape");
					var imageMap = imagemapnode.item(0).getAttribute("coords").split(",");
				}
			}

			var icons = styles[i].getElementsByTagName("Icon");
			// This might not be an icon style
			if (icons.length > 0) {
				var href = this.readKMLValue(icons[0].getElementsByTagName("href")[0]);
				if (!!href) {
					var basehref = href.replace(/.png/i, "");

					parsedStyles["#" + styleID] = new GIcon(G_DEFAULT_ICON, href);
					parsedStyles["#" + styleID].iconSize = new GSize(iconsizeX, iconsizeY);
					parsedStyles["#" + styleID].shadowSize = new GSize(shadowWidth, iconsizeY);
					parsedStyles["#" + styleID].iconAnchor = new GPoint(anchorX,anchorY);
					parsedStyles["#" + styleID].dragCrossAnchor = new GPoint(2,8);
					parsedStyles["#" + styleID].infoWindowAnchor = new GPoint(iwanchorX,iwanchorY);
					/*parsedStyles["#" + styleID].shadow = basehref + "-shadow.png";*/
					parsedStyles["#" + styleID].shadow = "";
					parsedStyles["#" + styleID].printImage = basehref + ".gif";
					parsedStyles["#" + styleID].mozPrintImage = basehref + ".gif";
					if (typeof(imagemapnode) != "undefined") {
						parsedStyles["#" + styleID].imageMapType = imageMapType;
						parsedStyles["#" + styleID].imageMap = imageMap;
					}
				}
			}

			// is it a LineStyle ?
			var linestyles = styles[i].getElementsByTagName("LineStyle");
			if (linestyles.length > 0) {
				var width = parseInt(this.readKMLValue(linestyles[0].getElementsByTagName("width")[0] ) );
				if (width < 1) {
					width = 5;
				}
				var color = this.readKMLValue(linestyles[0].getElementsByTagName("color")[0] );
				var opacity = parseInt(color.substr(0,2), 16) / 256;
				color = "#" + color.substr(6,2) + color.substr(4,2) + color.substr(2,2);
				if (!parsedStyles["#" + styleID]) {
					parsedStyles["#" + styleID] = {};
				}
				parsedStyles["#" + styleID].color = color;
				parsedStyles["#" + styleID].width = width;
				parsedStyles["#" + styleID].opacity = opacity;
			}

			// is it a PolyStyle ?
			var polystyles=styles[i].getElementsByTagName("PolyStyle");
			if (polystyles.length > 0) {
				if (!parsedStyles["#" + styleID]) {
					parsedStyles["#" + styleID] = {};
				}

				var fill = parseInt(GXml.value(polystyles[0].getElementsByTagName("fill")[0] ) );
				if (polystyles[0].getElementsByTagName("fill").length == 0) {
					fill = 1;
				}

				var outline = parseInt(GXml.value(polystyles[0].getElementsByTagName("outline")[0] ) );
				if (polystyles[0].getElementsByTagName("outline").length == 0) {
					outline = 1;
				}

				var color = this.readKMLValue(polystyles[0].getElementsByTagName("color")[0] );
				var opacity = parseInt(color.substr(0,2), 16 ) / 256;
				color = "#" + color.substr(6,2) + color.substr(4,2) + color.substr(2,2);

				parsedStyles["#" + styleID].fillcolor = color;
				parsedStyles["#" + styleID].fillopacity = opacity;
				if (!fill) {
					parsedStyles["#" + styleID].fillopacity = 0;
				}
				if (!outline) {
					parsedStyles["#" + styleID].opacity = 0;
				}
			}
		}
		return parsedStyles;
	},

	/**
	 * calculateHotSpot
	 *
	 * Calculates the x and y of a hotspot
	 *
	 * @since Mon Feb 9 2009
	 * @access public
	 * @param DomNode hotspot
	 * @param Integer iconsizeX
	 * @param Integer iconsizeY
	 * @param Integer anchorX
	 * @param Integer iconsizeY
	 * @return Object
	 **/
	calculateHotSpot: function(hotspot, iconsizeX, iconsizeY, anchorX, anchorY) {
		var xunits = hotspot.getAttribute("xunits");
		var yunits = hotspot.getAttribute("yunits");
		var xval = hotspot.getAttribute("x");
		var yval = hotspot.getAttribute("y");

		var anchorX = (xunits == "fraction") ? (iconsizeX * xval) : anchorX;
		var anchorX = (xunits == "pixels") ? xval : anchorX;
		var anchorX = (xunits == "insetPixels") ? (iconsizeX - xval) : anchorX;

		var anchorY = (yunits == "fraction") ? (iconsizeY * yval) : anchorY;
		var anchorY = (yunits == "pixels") ? yval : anchorY;
		var anchorY = (yunits == "insetPixels") ? (iconsizeY - yval) : anchorY;
		return {x: anchorX, y: anchorY};
	},

	/**
	 * readKMLValue
	 *
	 * Reads string value of node (uses GXml.value, then removes leading and trailing whitespace)
	 *
	 * @since Mon Feb 9 2009
	 * @access public
	 * @param DomNode node
	 * @return String
	 **/
	readKMLValue: function(node) {
		return GXml.value(node).replace(/^\s*/,"").replace(/\s*$/,"");
	},

	/**
	 * positionInfowindowRelativeToMarker
	 *
	 * Positions the window relative to the last clicked marker
	 *
	 * @since Mon Feb 2 2009
	 * @access public
	 * @param Object overlayvalue
	 * @return GPoint
	 **/
	positionInfowindowRelativeToMarker: function(overlayvalue) {
		if (typeof(overlayvalue.lastmarker) == "undefined") {
			return;
		}
		var fullheight = overlayvalue.infowindow.getFullHeight();
		if (fullheight > this._map.getContainer().getHeight() ) {
			var newheight = overlayvalue.infowindow.getContentHeight() - (fullheight - this._map.getContainer().getHeight() );
			fullheight = overlayvalue.infowindow.setHeight(newheight, null, false).getFullHeight();
		}

		var p = this._map.fromLatLngToDivPixel(overlayvalue.lastmarker.getLatLng() );
		var icon = overlayvalue.lastmarker.getIcon();
		var halfwidth = (overlayvalue.infowindow.getWidth() / 2);
		var offsety = (icon.iconAnchor.y - icon.infoWindowAnchor.y);

		p.y -= offsety;
		p.x -= (icon.iconAnchor.x - icon.infoWindowAnchor.x);
		p.y -= fullheight;
		p.x -= halfwidth;
		overlayvalue.infowindow.setX(p.x);
		overlayvalue.infowindow.setY(p.y);

		overlayvalue.infowindow.getPushpinWindowConnectorElement().setStyle( {"left": ( (halfwidth + overlayvalue.infowindow.getStyleSetting("pushpinwindowconnector-left") ) - offsety) + "px" } );

		var newy = p.y + ( (overlayvalue.infowindow.getFullHeight() + icon.iconSize.height) / 2);
		var newx = p.x + halfwidth;
		return new GPoint(newx, newy);
	},

	/**
	 * positionInfowindowRelativeToPoint
	 *
	 * Positions the window relative to the given point
	 *
	 * @since Mon Feb 2 2009
	 * @access public
	 * @param Object overlayvalue
	 * @return GPoint
	 **/
	positionInfowindowRelativeToPoint: function(overlayvalue, point) {
		var p = this._map.fromLatLngToDivPixel(point);
		var halfwidth = (overlayvalue.infowindow.getWidth() / 2);
		p.y -= overlayvalue.infowindow.getFullHeight();
		p.x -= halfwidth;
		overlayvalue.infowindow.setX(p.x);
		overlayvalue.infowindow.setY(p.y);

		overlayvalue.infowindow.getPushpinWindowConnectorElement().setStyle( {"left": (halfwidth + overlayvalue.infowindow.getStyleSetting("pushpinwindowconnector-left") ) + "px" } );

		var newy = p.y;
		var newx = p.x + halfwidth;
		return new GPoint(newx, newy);
	}
});
/**
 * WJImageBannerCB can swap image src's
 *
 * @since Fri Mar 27 2009
 * @author Giso Stallenberg
 **/
var WJImageBannerCB = Class.create({
	/**
	 * initialize
	 *
	 * Creates a new WJImageBannerCB
	 *
	 * @since Fri Mar 27 2009
	 * @access public
	 * @param string chapid
	 * @param Element chaptarget
	 * @return void
	 **/
	initialize: function(img, newsrc) {
		img = $(img);
		if (img.tagName.toLowerCase() != "img") {
			if (img.down("img") ) {
				img = img.down("img");
			}
			else {
				img = false;
			}
		}
		if (!img) {
			return; // appearently nothing to swap
		}
		
		this._img = img;
		var src = this._img.src;
		this._basepath = "";
		var basepath = src.match(/(\/images\/thumbs(\/__[^\/]*)+)\/?/);
		if (basepath) {
			this._basepath = basepath[1];
			src = src.replace(/(^[^\/]+\/\/[^\/]+\/images\/thumbs(\/__[^\/]*)+)\/?/, "/");
		}
		this._src = src;
		this._newsrc = newsrc;
	
		this._img.observe("mouseover", this.swap.bindAsEventListener(this, this._newsrc) );
		this._img.observe("mouseout", this.swap.bindAsEventListener(this, this._src) );
	},
	
	/**
	 * swap
	 *
	 * Actually swaps the src
	 *
	 * @since Fri Mar 27 2009
	 * @access public
	 * @param Event event
	 * @param string src
	 * @return void
	 **/
	swap: function(event, src) {
		this._img.src = this._basepath + src;
	}
 });
/**
 * Review specific functionality
 *
 * @since Thu Jan 29 2009
 * @author Ron Rademaker
 **/
var WJCBFunctionalityreview = Class.create({
	/**
	 * initialize
	 *
	 * Creates a new review specific cb functionality
	 *
	 * @since Thu Jan 29 2009
	 * @access public
	 * @param WJCBFunctionality func
	 * @return void
	 **/
	initialize: function(func) {
		this.func = func;
	},

	/**
	 * handleNotAcceptable
	 *
	 * Shows the info on the fact fields are missing, NOTE: does not show which fields are missing
	 *
	 * @since Tue Mar 17 2009
	 * @access public
	 * @return void
	 **/
	handleNotAcceptable: function() {
		if ($(this.func.id + "_missing") ) {
			$(this.func.id + "_missing").setStyle({"display": "block"});
		}
	}
});

/**
 * WJCBAction describes a CB functionality action
 *
 * @since Mon Dec 15 2008
 * @author Ron Rademaker
 **/
var WJCBAction = Class.create({
	/**
	 * initialize
	 *
	 * Creates a new WJCBAction
	 *
	 * @since Mon Dec 15 2008
	 * @access public
	 * @param string name
	 * @return void
	 **/
	initialize: function(name, func) {
		this.name = name;
		this.cbfunc = func;
		this.components = [];
	},

	/**
	 * execute
	 *
	 * Collects all information from the action's components and does the right ajax call to perform the action
	 *
	 * @since Tue Dec 16 2008
	 * @access public
	 * @return void
	 **/
	execute: function() {
		var spin = new WJSpin();
		var url = new WJUrl({ct: "wmdynamic", dt: "wmcontentblock", module: "Wmcb", id: this.cbfunc.getId(), action: this.name});
		this.components.each(function(s) {
			this.addParameter("args[" + s.getId() + "]", s.getValue() );
		}.bind(url) );
		var elem = this.cbfunc.getElement();
		elem.ajaxUpdateType = "replaceElement";
		document.body.setStyle({"cursor": "wait"});
		spin.content(url, [elem, function() {document.body.setStyle({"cursor": "default"}); }], {"406": this.handleNotAcceptable.bind(this)});
	},

	/**
	 * handleNotAcceptable
	 *
	 * Handles a Not Acceptable response, usually required fields will be missing
	 *
	 * @since Thu Jan 29 2009
	 * @access public
	 * @return void
	 **/
	handleNotAcceptable: function() {
		$(document.body).setStyle({"cursor": "auto"});
		if (this.cbfunc.specifics) {
			this.cbfunc.specifics.handleNotAcceptable();
		}
	},

	/**
	 * addComponent
	 *
	 * Adds a component to this action
	 *
	 * @since Mon Dec 15 2008
	 * @access public
	 * @param string compname
	 * @param string comptype
	 * @param string compgroup
	 * @return void
	 **/
	addComponent: function(comptype, compname, compgroup) {
		comptype = comptype.substr(0, 3).toUpperCase() + comptype.substr(3);
		if (!window[comptype]) {
			comptype = "WJComponent";
		}

		var id = compname;
		if (!$(id) ) {
			id = compname + "_" + this.cbfunc.getId();
		}
		if (!$(id) && compgroup != undefined) {
			id = compgroup + "_" + compname + "_" + this.cbfunc.getId();
			compname = compgroup + "_" + compname;
		}

		if ($(id) ) {
			var comp = new window[comptype](compname);
			comp.setHtmlelement($(id) );
			this.components.push(comp);
		}
	}
});


/**
 * Form specific functionality (copied from WJCBFunctionalityreview)
 *
 * @since Fri Feb 13 2009
 * @author Niels Nijens
 **/
var WJCBFunctionalityform = Class.create({
	
	/**
	 * initialize
	 *
	 * Creates a new review specific cb functionality
	 *
	 * @since initial
	 * @access public
	 * @param WJCBFunctionality func
	 * @return void
	 **/
	initialize: function(func) {
		this.func = func;
	},
	
	/**
	 * handleNotAcceptable
	 *
	 * Handles Not Acceptable error
	 *
	 * @since initial
	 * @access public
	 * @return void
	 **/
	handleNotAcceptable: function() {
		if ($(this.func.id + "_missing") ) {
			$(this.func.id + "_missing").setStyle({"display": "block"});
		}
	}
});

/**
 * WJCBFunctionality is the javascript class that implements the 'glue' that keeps everything concerning CB Functionality together
 *
 * @since Mon Dec 15 2008
 * @author Ron Rademaker
 **/
var WJCBFunctionality = Class.create({
	/**
	 * initialize
	 *
	 * Creates a new WJCBFunctionality
	 *
	 * @since Mon Dec 15 2008
	 * @access public
	 * @param integer id
	 * @param string cb
	 * @param string htmlid
	 * @param object cbdata
	 * @return void
	 **/
	initialize: function(id, cb, htmlid, cbdata) {
		this.id = id;
		this.element = $(htmlid);
		this.cb = cb;
		this.actions = {};
		this.cbdata = cbdata;

		if (window["WJCBFunctionality" + this.cb]) {
			this.specifics = new window["WJCBFunctionality" + this.cb](this);
		}
	},

	/**
	 * getData
	 *
	 * Gets some data from the cbdata
	 *
	 * @since Tue Dec 16 2008
	 * @access public
	 * @param string key
	 * @return mixed
	 **/
	getData: function(key) {
		return this.cbdata[key];
	},

	/**
	 * setData
	 *
	 * Sets data in cbdata
	 * NOTE: this does not save ANY data
	 *
	 * @since Tue Dec 16 2008
	 * @access public
	 * @param string key
	 * @param mixed value
	 * @return void
	 **/
	setData: function(key, value) {
		this.cbdata[key] = value;
	},

	/**
	 * callAction
	 *
	 * Finds the requested action and calls its execute function
	 *
	 * @since Tue Dec 16 2008
	 * @access public
	 * @param string action
	 * @return void
	 **/
	callAction: function(action) {
		if (this.actions[action]) {
			this.actions[action].execute();
		}
	},

	/**
	 * getId
	 *
	 * Getter for the id
	 *
	 * @since Mon Dec 15 2008
	 * @access public
	 * @return integer
	 **/
	getId: function() {
		return this.id;
	},
	
	/**
	 * getElement
	 *
	 * Getter for the HTML element
	 *
	 * @since Tue Dec 16 2008
	 * @access public
	 * @return integer
	 **/
	getElement: function() {
		return this.element;
	},

	/**
	 * addAction
	 *
	 * Adds an action to the CB functionality
	 *
	 * @since Mon Dec 15 2008
	 * @access public
	 * @param WJCBAction action
	 * @return void
	 **/
	addAction: function(name, action) {
		this.actions[name] = action;
	}
});

/**
 * Rating specific functionality
 *
 * @since Tue Dec 16 2008
 * @author Ron Rademaker
 **/
var WJCBFunctionalityrating = Class.create({
	/**
	 * initialize
	 *
	 * Creates a new rating specific cb functionality
	 *
	 * @since Tue Dec 16 2008
	 * @access public
	 * @param WJCBFunctionality func
	 * @return void
	 **/
	initialize: function(func) {
		this.func = func;
		if (this.func.getElement() ) {
			this._attachObservers();
			this._setDefaults();
		}
	},

	/**
	 * _attachObservers
	 * 
	 * Attaches observers to the rating blok to allow voting
	 * Note: this function is written for direct rating voting, ie. clicking on the fourth star immediately votes 4. Other requirements require overriding this function by reimplementing WJCBFunctionalityrating, just make sure the javascript parser meets it later that this class and it'll work (you might even be able to extend this class).
	 *
	 * @since Tue Dec 16 2008
	 * @access protected
	 * @return void
	 **/
	_attachObservers: function() {
		var fullbar = this._getBar("fullbar")
		this._voteObserver = this.vote.bindAsEventListener(this);
		fullbar.observe("click", this._voteObserver);
		this._moveObserver = this.mouseMove.bindAsEventListener(this);
		fullbar.observe("mousemove", this._moveObserver);
		this._outObserver = this.restoreWidth.bindAsEventListener(this);
		fullbar.observe("mouseout", this._outObserver);
	},

	/**
	 * _getBar
	 *
	 * Returns a bar identified by className
	 *
	 * @since Mon Jan 12 2009
	 * @access protected
	 * @param string className
	 * @return Element
	 **/
	_getBar: function(className) {
		return this.func.getElement().select("." + className).first();
	},

	/**
	 * mouseMove
	 *
	 * Handles the mousemove event
	 *
	 * @since Mon Jan 12 2009
	 * @access public
	 * @param Event e
	 * @return void
	 **/
	mouseMove: function(e) {
		var bar = this._getBar("percentage");
		if (bar) {
			if (this._originalWidth == null) {
				this._originalWidth = bar.getStyle("width");
			}
			var elemloc = bar.cumulativeOffset();
			var relmouseloc = {x: e.pointerX() - elemloc[0], y: e.pointerY() - elemloc[1]};
			if (relmouseloc.x < this._getBar("fullbar").getWidth() ) {
				bar.setStyle({width: relmouseloc.x + "px"});
			}
		}
	},

	/**
	 * restoreWidth
	 *
	 * Restores the original width of the bar
	 *
	 * @since Mon Jan 12 2009
	 * @access public
	 * @param Event e
	 * @return void
	 **/
	restoreWidth: function(e) {
		var bar = this._getBar("percentage");
		if (bar && this._originalWidth != null) {
			bar.setStyle({width: this._originalWidth});
		}
	},

	/**
	 * _setDefaults
	 *
	 * Sets some default data in func (this will allow you to override defaults by using the func's setData function from the uft)
	 *
	 * @since Tue Dec 16 2008
	 * @access protected
	 * @return void
	 **/
	_setDefaults: function() {
		this.func.setData("boundaries", {x: 0, y: 0, width: this._getBar("fullbar").getWidth(), height: this._getBar("fullbar").getHeight()});
	},

	/**
	 * vote
	 *
	 * Handles a vote click
	 *
	 * @since Tue Dec 16 2008
	 * @access public
	 * @param event e
	 * @return void
	 **/
	vote: function(e) {
		var elemloc = this._getBar("fullbar").cumulativeOffset();
		var relclickloc = {x: e.pointerX() - elemloc[0], y: e.pointerY() - elemloc[1]};
		var boundaries = this.func.getData("boundaries");
		if ( (relclickloc.x > boundaries.x) && (relclickloc.x < (boundaries.x + boundaries.width) )
			&&	(relclickloc.y > boundaries.y) && (relclickloc.y < (boundaries.y + boundaries.height) )  ) {
			var limits = {min: parseInt(this.func.getData("min") ), max: parseInt(this.func.getData("max") )};
			var stepwidth = boundaries.width / (1 + limits.max - limits.min);
			var vote = Math.ceil( (relclickloc.x - boundaries.x) / stepwidth);
			$("vote_" + this.func.getId() ).value = vote;
			this.func.callAction("vote");
			this._getBar("fullbar").stopObserving("click", this._voteObserver);
			this._getBar("fullbar").stopObserving("mousemove", this._moveObserver);
			this._getBar("fullbar").stopObserving("mouseout", this._outObserver);
		}
	}
});

/**
 * WJComponent
 *
 * Base Component class
 *
 * @since Tue Feb 17 2009
 * @revision $Revision$
 * @author Alwin van den Hoven
 * @package Windmill.Javascript.Contentblock  
 **/
var WJSwapper = Class.create({

	/**
	 * initialize
	 *
	 * Initializes the global function
	 *
	 * @since Tue Feb 17 2009
	 * @access public
	 **/
	initialize: function(element, basepath) {
		this.baseimage = $(element);
		this.basepath = basepath;
	},
	
	/**
	 * registerElement
	 *
	 * Registers al the elements for the swap array
	 *
	 * @since Tue Feb 17 2009
	 * @access public
	 **/
	registerElement: function(element, path) {

		var element = new Image().src = $(element);
		element.observe("mouseover", this.mouseOver.bindAsEventListener(this, path) );
		element.observe("mouseout", this.mouseOut.bindAsEventListener(this) );
	},

	/**
	 * mouseOver
	 *
	 * on mouseOver get new path
	 *
	 * @since Tue Feb 17 2009
	 * @access public
	 **/
	mouseOver: function(event, path) {
		this.baseimage.src = path;
	},

	/**
	 * mouseOut
	 *
	 * on mouseOut restore original path
	 *
	 * @since Tue Feb 17 2009
	 * @access public
	 **/
	mouseOut: function(event) {
		this.baseimage.src = this.basepath;
	}
});
/**
 * WJChapCB loads a Chap Contentblock
 *
 * @since Tue Dec 30 2008
 * @author Ron Rademaker
 **/
var WJChapCB = Class.create({
	/**
	 * initialize
	 *
	 * Creates a new WJChapCB
	 *
	 * @since Tue Dec 30 2008
	 * @access public
	 * @param string chapid
	 * @param Element chaptarget
	 * @return void
	 **/
	initialize: function(chapid, chaptarget) {
		this._chapid = chapid;
		this._target = $(chaptarget);
		this._load();
	},
	
	/**
	 * load
	 *
	 * Actualy loads the chap blocks data
	 *
	 * @since Tue Dec 30 2008
	 * @access protected
	 * @return void
	 **/
	_load: function() {
		var spin = new WJSpin();
		// NOTE: The dt is determined by Wmchapblock, it will do this automatically because the rssfeed argument is unknown
		var url = new WJUrl({
			"ct": "wmdynamic",
			"blockid": this._chapid,
			"module": "Wmchapblock",
			"wmtrigger[]": ["requestufts"]
		});
		spin.content(url, [this._target, this.updateBlock.bind(this)]);
	},
	
	/**
	 * updateBlock
	 *
	 * Updates the title and icon of the block
	 *
	 * @since Tue Dec 30 2008
	 * @access public
	 * @param 
	 * @return void
	 **/
	updateBlock: function(response) {
		if (this._target) {
			var title = this._target.select(".chap_rsstitle").first();
			if (title) {
				this._target.up(".chap_block").select(".chap_title .chap_content h1").first().update(title.innerHTML);
			}
			var icon = this._target.select(".chap_rssicon").first();
			if (icon) {
				this._target.up(".chap_block").select(".chap_title .chap_content .chap_icon").first().update(icon.innerHTML);
			}
		}
	}
});


