Team:Team:Freiburg software/Code/qooxdoo/Application.js

From 2009.igem.org

/* ************************************************************************

   Copyright: SynBioWave (synbiowave.org)

   License: GPL

   Authors: David Nellessen (mail@davidn.de)
   
   DESCRIPTION: 
   
   This Application is used to create a GUI inside a wave
   gadget from a JSON passed via the wave API (wave.getState()). This
   Application expects  the JSON data to construct the menu
   from in wave.getState('ui.structure') with a special structure we call
   the qooxwave protocol. This protocol is not yet completed. 
   For understanding qooxwave protocol, have a look at the documentation:
   https://2009.igem.org/Team:Freiburg_software/Project/qooxWave-details
   
   TODO:
   - complete qooxwave protocol
   - gadget resizing should respect menus
   - Refreshing UI on every state update is not neccessary 
   - solve absolute url problem (still pointing to sourceforge.net in config.josn)
   - rebuild the model on user input and bind model to store
   - MVC improvement: when changing something like clicking on a checkbox, the model is not updated correctly
      therefore the state has still the old value (no checkbox clicked),
      nothing will happen, eventhough the server might whant to uncheck the checkbox!
      So there must be either
        - a update of the model
        - allways update gui on stateupdate
        - some kind of mixing of the above: not on every state update, but if the model
          might be out of sync
   - modelling is very sensible cerncerning their ids. they cannot contain certain characters, which?
   - check target url for upload widget

************************************************************************ */

/* ************************************************************************

#asset(qooxwaveclient/*)

************************************************************************ */

/**
 * This is the main application class of "qooxwaveClient"
 */
qx.Class.define("qooxwaveclient.Application",
{
  extend : qx.application.Standalone,


  	properties : { //this app makes use of the dynamic property feature of qooxdoo. guiModel bind to the model in the store using data binding feature of qooxdoo
		guiModel : {nullable : true, apply : "_applyGuiModel", event : "changeGuiModel"}
	},

  /*
  *****************************************************************************
     MEMBERS
  *****************************************************************************
  */

  members :
  {
    /**
     * This method contains the initial application code and gets called 
     * during startup of the application
     */
    main : function()
    {
      // Call super class
      this.base(arguments);

      // Enable logging in debug variant
      if (qx.core.Variant.isSet("qx.debug", "on"))
      {
        // support native logging capabilities, e.g. Firebug for Firefox
        qx.log.appender.Native;
        // support additional cross-browser console. Press F7 to toggle visibility
        qx.log.appender.Console;
      }

      /*
      -------------------------------------------------------------------------
        Below is actual application code...
      -------------------------------------------------------------------------
      */
      
      /*
       * Define main objects
       */
      
      //general member variables:
      this.namespace = new Object();
      this.namespace.ui = 'ui';
      this.namespace.ui_structure = this.namespace.ui + '.structure';
      
      //set minHeight of the gadget. This is unfortunaltely nedded, because the auto resizing does not respect menus
      this.minHeight = 80;
      
      //Get Wave Object (simulated when outside of wave)
      var wave = new qooxwaveclient.wave.WaveFactory().getWave();
      
      //create a Store for server-client (wave-thisApp) communication
      this.store = new qooxwaveclient.Store(wave, new Array(this.namespace.ui_structure), this.namespace.ui_structure);
      wave.setStateCallback(this.store.processStateUpdate, this.store);
      this.store.bind("model", this, "guiModel");

      //uncomment the following lines to activate the testing ui
      //this.buildTestingUI();
	  //this.minHeight = 400;
      
      //Testing Ui can also be activated with a shortcut (works only outside wave)
      var find = new qx.event.Command("Control-Y");
      find.addListener("execute", function(){
    	  if(qx.ui.core.Widget.contains(this.getRoot(), this.testUi)){
    		  this.testUi.destroy();
    		  this.minHeight = 100;
    	  }
    	  else{
    		  this.buildTestingUI();
    		  this.minHeight = 400;
    	  }
    	  this.refresh();
      }, this);
      
    },
    
	
    
    //build the main Widget from guiModel | destruction suggested first
	buildMainWidget : function (model){
    	  this.__buildRootGadget();
    	  if(!model) return true;
	      if(model.type == "ui" && model.subitem){ //distinguish different implementations of qooxwave
	    	 model = model.subitem; 
	      }
	      for(var itemid in model){
	    	  var widget = this.__loop(itemid, model[itemid]);
	    	  if(widget.qxwidget) this.gadget.add(widget.qxwidget);
	    	  else return false;
	      }
	      return true;
	},
	
    //public method to destruct gadget
	destructMainWidget :function () {
		this.__destructRootGadget();	
	},
	
	//reBuild this.gadget from guiModel
	refresh : function (){
		this.destructMainWidget();
		//alert(this.getGuiModel().toString());
		this.buildMainWidget(this.getGuiModel());
		this.resizeGadget();
		return true;
	},
	
	//resize wave gadget
	resizeGadget : function (){
		var heightDest = this.minHeight; //minimal height
		if(this.gadget.getBounds())	var heightDest = Math.max(heightDest, this.gadget.getBounds().height);
		if(typeof gadgets != "undefined") gadgets.window.adjustHeight(heightDest);
		this.debug("SET HEIGHT: " + heightDest);
	},
	
	//executed when new value of GuiModel applied
	_applyGuiModel : function (value, old){
		if(value == old) this.debug("Apply guiModel. But NO Change detected");
		else {
			this.debug("Apply guiModel: " + value);
			this.refresh();
		}
	},
	
	//loops an object item according to the qooxWave protocol
	__loop : function (itemid, item){
		item.id = itemid;
		if(item.type == "toolbar") item = this.__processToolbar(itemid, item); 
		else if(item.type == "button") item = this.__processButton(itemid, item);
		else if(item.type == "checkbox") item = this.__processCheckbox(itemid, item);
		else if(item.type == "menu") item = this.__processMenu(itemid, item);
		else if(item.type == "label") item = this.__processLabel(itemid, item);
		else if(item.type == "form") item = this.__processForm(itemid, item);
		else if(item.type == "upload") item = this.__processUpload(itemid, item);
		else if(item.type == "chron") item = this.__processChron(itemid, item);
		return item;
	},

	
	//proccesses the toolbar typed items
    __processToolbar : function (itemid, item){
		if(typeof item.qxwidget == "undefined") item.qxwidget = new qx.ui.toolbar.ToolBar(); //could be create earlier
		
		if(typeof item.subitem != "undefined"){ //loop subitems
			for(var subitemid in item.subitem){ 
				if(item.subitem[subitemid].type == "button"){  //if type of subitem is button create a special toolbar button (design issue)
					item.subitem[subitemid].qxwidget = new qx.ui.toolbar.Button();
					item.subitem[subitemid] = this.__loop(subitemid, item.subitem[subitemid]);
					item.qxwidget.add(item.subitem[subitemid].qxwidget);
				}
				else if(item.subitem[subitemid].type == "checkbox"){  //if type of subitem is checkbox, create a special toolbar button (design issue)
					item.subitem[subitemid].qxwidget = new qx.ui.toolbar.CheckBox();
					item.subitem[subitemid] = this.__loop(subitemid, item.subitem[subitemid]);
					item.qxwidget.add(item.subitem[subitemid].qxwidget);
				}
				else if(item.subitem[subitemid].type == "menu"){  //create a special menu (design issue)
					item.subitem[subitemid].qxwidget = new qx.ui.toolbar.MenuButton();
					item.subitem[subitemid] = this.__loop(subitemid, item.subitem[subitemid]);
					item.qxwidget.add(item.subitem[subitemid].qxwidget);
				}
			}
		}
		return item;
	},
	
	//processes the form typed items
	__processForm : function (itemid, item){
		if(typeof item.qxform == "undefined") item.qxform = new qx.ui.form.Form(); //could be create earlier	}
		item.qxform.addGroupHeader(item.label);

		//create widget
	    item.qxwidget = new qx.ui.groupbox.GroupBox(""+item.label, null);
	    item.qxwidget.setLayout(new qx.ui.layout.VBox(5));
	    item.qxwidget.setMaxWidth(300);
	    
	    //A atom that shows loading state
	    var loadingAtom = new qx.ui.basic.Atom("State", "qooxwaveclient/ajax-loader-large.gif");
	    loadingAtom.set({alignX : "center", marginTop : 10});
	    loadingAtom.hide();
	    
	    //create model
		var model = new Object();
		
		//create submit queue 
		var queue = new Array();
		var queueCount = 0;
		
		if(typeof item.subitem != "undefined"){ //loop subitems | most of them are processed directly here, cause they can only be subitems of form
			
			for(var subitemid in item.subitem){
				var skipModelSetup = false;
				if(item.subitem[subitemid].type == "textarea"){
					item.subitem[subitemid].qxwidget = new qx.ui.form.TextArea();
					item.subitem[subitemid].value = item.subitem[subitemid].value+"";
				}
				else if(item.subitem[subitemid].type == "textfield"){
					item.subitem[subitemid].qxwidget = new qx.ui.form.TextField();
					item.subitem[subitemid].qxwidget.setMinWidth(150);
					item.subitem[subitemid].value = item.subitem[subitemid].value+"";
				}
				else if(item.subitem[subitemid].type == "checkbox"){
					item.subitem[subitemid].qxwidget = new qx.ui.form.CheckBox();
					item.subitem[subitemid].value = !!item.subitem[subitemid].value; //parse to boolean
				}
				else if(item.subitem[subitemid].type == "radio"){
					item.subitem[subitemid].qxwidget = new qx.ui.form.RadioButton();
					item.subitem[subitemid].value = !!item.subitem[subitemid].value; //parse to boolean
					//add to radio group
					if(!item.subitem[subitemid].group) continue;
					if(typeof item.radiogroups == "undefined") item.radiogroups = new Object();
					if(typeof item.radiogroups[item.subitem[subitemid].group] == "undefined") item.radiogroups[item.subitem[subitemid].group] = new qx.ui.form.RadioButtonGroup();
					item.radiogroups[item.subitem[subitemid].group].add(item.subitem[subitemid].qxwidget);
				}
				else if(item.subitem[subitemid].type == "download"){  //section copied from == "upload" so the notaion is wrong. TODO: change this
					item.subitem[subitemid].upload = new qooxwaveclient.Download(item.subitem[subitemid].target+"", this.namespace.ui + "." + subitemid, this.store, false, "application/x-www-form-urlencoded");
					item.qxwidget.add(item.subitem[subitemid].upload);
					item.subitem[subitemid].qxwidget = new qx.ui.form.TextField();
					item.subitem[subitemid].qxwidget.setReadOnly(true);
					
					//bind filename upload -> textfield
					item.subitem[subitemid].upload.bind("fileName", item.subitem[subitemid].qxwidget, "value");
					item.subitem[subitemid].value = item.subitem[subitemid].value+"";

					//process submitting
					item.subitem[subitemid].upload.queueCount = queueCount;
					item.subitem[subitemid].upload.go = function(){
						toogelLoadingAtom("downloading ...");
						this.modefieUri(item.serialize("uri"));
						this.submit();
						toogelLoadingAtom("Uploading " + this.getFileName() + "...");
					};
					item.subitem[subitemid].upload.addListener("onload", function(e){
						qx.event.Timer.once(queue[this.queueCount+1].go, queue[this.queueCount+1], 1000);
					},item.subitem[subitemid].upload);
					queue[queueCount] = item.subitem[subitemid].upload;
					queueCount++;
					
					//hide textfield
					item.subitem[subitemid].qxwidget.setEnabled(false);
				}
				else if(item.subitem[subitemid].type == "upload" ){
					item.subitem[subitemid].upload = new qooxwaveclient.Upload(item.subitem[subitemid].target+"", this.namespace.ui + "." + subitemid, this.store, false, "multipart/form-data");
				    item.qxwidget.add(item.subitem[subitemid].upload);
					item.subitem[subitemid].qxwidget = new qx.ui.form.TextField();
					item.subitem[subitemid].qxwidget.setReadOnly(true);
					
					//bind filename upload -> textfield
					item.subitem[subitemid].upload.bind("fileName", item.subitem[subitemid].qxwidget, "value");
					item.subitem[subitemid].value = item.subitem[subitemid].value+"";

					//process submitting
					item.subitem[subitemid].upload.queueCount = queueCount;
					item.subitem[subitemid].upload.go = function(){
						toogelLoadingAtom("Uploading ...");
						this.modefieUri(item.serialize("uri"));
						this.submit();
						toogelLoadingAtom("Uploading " + this.getFileName() + "...");
					};
					item.subitem[subitemid].upload.addListener("onload", function(e){
						qx.event.Timer.once(queue[this.queueCount+1].go, queue[this.queueCount+1], 1000);
					},item.subitem[subitemid].upload);
					item.subitem[subitemid].upload.addListener("error", function(e){
						alert('There was one upload field not specified. Nothing to send.');
						qx.event.Timer.once(queue[this.queueCount+1].go, queue[this.queueCount+1], 1000);
					},item.subitem[subitemid].upload);
					queue[queueCount] = item.subitem[subitemid].upload;
					queueCount++;
					
					//hide textfield
					item.subitem[subitemid].qxwidget.setEnabled(false);
				}
				else continue; //make sure only available subitems are processed
				if(!skipModelSetup){  //TODO ... HMMMM
					model[subitemid] = item.subitem[subitemid].value; //fill the model
					item.subitem[subitemid].qxwidget.setValue(item.subitem[subitemid].value);
				}
				item.subitem[subitemid].qxwidget.setWidth(180); //TODO
				item.qxform.add(item.subitem[subitemid].qxwidget, item.subitem[subitemid].label, null, subitemid);
			}
		}
		
		//form model and data binding
		model = qx.data.marshal.Json.createModel(model);
	    var formController = new qx.data.controller.Form(model, item.qxform);
	    item.serialize = function(method){
	    	if(method == "json"){
		    	var modelJson = qx.util.Serializer.toJson(model);
		    	var value = modelJson+"";
		    	value = value.replace(/"/g, "'");
	    	}
	    	else if(method == "uri"){
	    		var modelUri = qx.util.Serializer.toUriParameter(model, function(object){return ""+object;});
	    		var value = modelUri+"";
		    	value = value.replace(/"/g, "'");
	    	}
	    	else value = null;
	    	return value;
	    };
	    
	    //toggle loading icon
	    toogelLoadingAtom = function (label){
	    	//if(qx.ui.core.contains(item.qxwidget, this.))
	    	if(!label) loadingAtom.hide();
	    	else {
	    		loadingAtom.setLabel(""+label);
	    		loadingAtom.show();
	    	}
	    };
	    	    
		// send button
	    var sendButton = new qx.ui.form.Button("Send", "icon/16/actions/dialog-ok.png");
	    queue[queueCount] = new Object();
	    queue[queueCount].deltaid = this.namespace.ui + "." + itemid;
	    queue[queueCount].store = this.store;
	    queue[queueCount].gadget = this.gadget;
	    queue[queueCount].widget = item.qxwidget;
	    queue[queueCount].go = function(){
	    	toogelLoadingAtom("validating form");
	    	//TODO: validation
	    	var delta = new Object();
	    	value = item.serialize("json");
	    	delta[this.deltaid] = "{'event' : 'changeValue', 'value' : " + value + "}";
			this.store.submitEventDelta(delta);

	    	toogelLoadingAtom(null);
			if(qx.ui.core.Widget.contains(this.gadget, this.widget)) this.gadget.remove(this.widget); //remove form
			
	    };
	   
	    //submit form on pressing the send button
	    sendButton.addListener("execute", function(e){
	    	queue[0].go();
	    }, this);
	    item.qxform.addButton(sendButton);

	    // reset button
	    var resetButton = new qx.ui.form.Button("Reset", "icon/16/actions/dialog-cancel.png");
	    resetButton.addListener("execute", function() {
	    	item.qxform.reset();
	    }, this);
	    item.qxform.addButton(resetButton);
	    
	    //add formwidget to item.qxwidget
	    item.qxwidget.view = item.qxform.createView();
	    item.qxwidget.add(item.qxwidget.view);
	    
	    //add loadingAtom
	    item.qxwidget.add(loadingAtom);
	    
		return item;
	},
	
	
	//processes the upload typed items
	__processUpload : function (itemid, item){
		item.qxwidget = new qooxwaveclient.Upload(item.target+"", this.namespace.ui + "." + item.id, this.store, true, "multipart/form-data");
		return item;
	},
	
	
	//processes the button typed items
	__processButton : function (itemid, item){
		if(typeof item.qxwidget == "undefined") item.qxwidget = new qx.ui.form.Button(); //could be create earlier	}
		item.qxwidget.setLabel(item.label);
		var childrenToToogle = new Array();
		
		if(typeof item.subitem != "undefined"){ //loop subitems
			for(var subitemid in item.subitem){ 
				if(item.subitem[subitemid].type == "form"){  //create a form
					item.subitem[subitemid] = this.__loop(subitemid, item.subitem[subitemid]);
					//for toogling on and off the forms onClick (so some lines below...)
					childrenToToogle.push(item.subitem[subitemid].qxwidget);
				}
				if(item.subitem[subitemid].type == "upload"){  //create a upload form
					item.subitem[subitemid] = this.__loop(subitemid, item.subitem[subitemid]);
					//for toogling on and off the forms onClick (so some lines below...)
					childrenToToogle.push(item.subitem[subitemid].qxwidget);
				}
				else continue;  // make sure only allowed subitems are added
				
			}
		}
		
		//attach onClick event
		item.qxwidget.addListener("execute", function (event) {
				
			//toogle forms on and off
			for(var i = 0; i<childrenToToogle.length; i++){
				//alert(i);
				if(qx.ui.core.Widget.contains(this.gadget, childrenToToogle[i])){
					this.gadget.remove(childrenToToogle[i]);
				}
				else{
					this.gadget.add(childrenToToogle[i]);
				}
			}
			if(typeof item.subitem == "undefined") { //only fire if there are no subitems
				var delta = new Object();
				var id = this.namespace.ui + "." + item.id;
				delta[id] = "{'event' : 'execute'}";
				//delta[id]['status'] = "fired";
				this.store.submitEventDelta(delta);
			}
		}, this);

		return item;
	},

	//processes the checkbox typed items
	__processCheckbox : function (itemid, item){
		if(typeof item.qxwidget == "undefined") item.qxwidget = new qx.ui.form.CheckBox(); //could be create earlier	}
		item.qxwidget.setLabel(item.label);
		item.qxwidget.setValue(!!item.value);
		
		//attach onClick event
		item.qxwidget.addListener("changeValue", function (event) {
			var delta = new Object();
			var id = this.namespace.ui + "." + item.id;
			delta[id] = "{\"event\" : \"changeValue\", \"value\" : \"" + event.getValue() + "\"}";
			this.store.submitEventDelta(delta);
		}, this);
		
		return item;
	},
	
	//processes the chron typed items. This item fires automated events.
	__processChron : function (itemid, item){
		item.qxwidget = new qx.ui.groupbox.GroupBox("", null);
	    item.qxwidget.setLayout(new qx.ui.layout.VBox(5));
	    item.qxwidget.setMaxWidth(300);
	    
	    var loadingAtom = new qx.ui.basic.Atom(""+item.label, "qooxwaveclient/ajax-loader-large.gif");
	    loadingAtom.set({alignX : "center"});
	    item.qxwidget.add(loadingAtom);
	    
	    var count = 1;
	    
	    item.chron = function(){
	    	var delta = new Object();
			var id = this.namespace.ui + "." + item.id;
			//loadingAtom.setLabel(""+count);
			count++;
			delta[id] = "{'event' : 'execute'}";
			//delta[id]['status'] = "fired";
			this.store.submitEventDelta(delta);
	    };
	    
	    //create timer to regularily fire events
	    var timer = new qx.event.Timer(parseInt(item.value)+10);
	    timer.addListener("interval", item.chron, this);
	    timer.start();
	    
		return item;
	},
	
	//processes the label typed items
	__processLabel : function (itemid, item){
		if(typeof item.qxwidget == "undefined") item.qxwidget = new qx.ui.basic.Label(item.label); //could be create earlier	}
		return item;
	},
	
	//processes the menu typed items
	__processMenu : function (itemid, item){
		if(typeof item.qxwidget == "undefined") item.qxwidget = new qx.ui.menu.Button(); //could be create earlier	}
		item.qxwidget.setLabel(item.label);
		
		var menu = new qx.ui.menu.Menu(); //create a new menucontainer
		item.qxwidget.setMenu(menu); //and set it
		if(typeof item.subitem != "undefined"){ //loop subitems
			for(var subitemid in item.subitem){ 
				if(item.subitem[subitemid].type == "button"){ //if type of subitem is button, create a special menu button (design issue)
					item.subitem[subitemid].qxwidget = new qx.ui.menu.Button();
					item.subitem[subitemid] = this.__loop(subitemid, item.subitem[subitemid]);
					menu.add(item.subitem[subitemid].qxwidget);
				}
				else if(item.subitem[subitemid].type == "checkbox"){ //if type of subitem is checkbox, create a special menu checkbox (design issue)
					item.subitem[subitemid].qxwidget = new qx.ui.menu.CheckBox();
					item.subitem[subitemid] = this.__loop(subitemid, item.subitem[subitemid]);
					menu.add(item.subitem[subitemid].qxwidget);
				}
				else if(item.subitem[subitemid].type == "menu"){   //create a special menu (design issue)
					item.subitem[subitemid].qxwidget = new qx.ui.menu.Button();
					item.subitem[subitemid] = this.__loop(subitemid, item.subitem[subitemid]);
					menu.add(item.subitem[subitemid].qxwidget);
				}
			}
		}
		
		return item;
	},
	
	//Destructs this.gadget, the root gadget of the application
	__destructRootGadget : function (){
		if(typeof this.gadget != "undefined" && !this.gadget.isDisposed()){
			this.getRoot().remove(this.gadget);
			this.gadget.dispose();
		}
	},
	
	//builds the root geadget (this,gadget) of the application. Any other gadget is added to this. this.gadget != this.getRoot() 
	__buildRootGadget : function (){
		this.gadget = new qx.ui.container.Composite();  //set an individual 'root' object called gadget. could be changed later on to calculate the correct height of the widget. Is also used to use a different layout than canvas
	      
	      //Set the layut of the root.gadget container
	      var layout = new qx.ui.layout.VBox();
	      layout.setSpacing(4); // apply spacing
	      this.gadget.setLayout(layout);
	      this.getRoot().add(this.gadget, {left:0, top:0, right:0});
	      this.gadget.addListener("resize", function(){
	    	  this.debug("HEIGHT: " + this.gadget.getBounds().height);
	    	  this.resizeGadget();
	      }, this);
	},
	
	
	//returns a test string in qooxwave protocol form
	__getTestString : function (){
		
		var submenu = "'submenu' : {" +
			"'type' : 'menu' ," +
				"'label' : 'Click Me' ," +
				"'subitem' : {'subsubmenu' : {" +
					"'type' : 'button' ," +
					"'label' : 'or click me'" +
				"}}" +
			"}";
		var upload = 
			"'datei1' : {" +
		  		"'label' : 'Datei 1' ," +
		  		"'type' : 'upload'," +
		  		"'target' : 'http://www.davidn.de/index.php'" +
		  	"}";
		var upload2 = 
			"'datei2' : {" +
		  		"'label' : 'Datei 2' ," +
		  		"'type' : 'upload'," +
		  		"'target' : 'http://www.davidn.de/index.php'" +
		  	"}";
		
		var formular = "'id0000' : {" +
  		"'label' : 'Upload File' ," +
	  		"'type' : 'form'," +
	  		"'subitem' : {" +
	  			"'form_bemerkung' : {" +
	  				"'type' : 'textfield'," +
	  				"'label' : 'Bemerkung'," +
	  				"'value' : ''" +
	  			"}," +
	  			"'form_checkbox1' : {" +
	  				"'type' : 'checkbox'," +
	  				"'label' : 'Aut. Erkennung'," +
	  				"'value' : true" +
	  			"}," +
	  			"'form_format1' : {" +
	  				"'type' : 'radio'," +
	  				"'label' : 'Format 1'," +
	  				"'group' : 'formatgruppe'," + 
	  				"'value' : 'abc'" +
	  			"}," +
	  			"'form_format2' : {" +
	  				"'type' : 'radio'," +
	  				"'label' : 'Format 3'," +
	  				"'group' : 'formatgruppe'," +
	  				"'value' : false" +
	  			"}," +
	  			upload + "," + upload2 + 
	  		"} " +
	  	"}";
		
		return "{" +

//			formular + "," +
				"'rootid' : {" +
	  		"'type' : 'toolbar' ," +
	  		"'label' : 'root element' ," +
	  		"'subitem' : {" +
		  /*		"'subid1' : {" +
	  			"'type' : 'button' ," +
	  			"'label' : 'Form'," +
	  			"'subitem' : " +
		  			"{"  + 
						formular +
		  			"}" +
	  			"}," +*/
		  		"'subid2' : {" +
		  			"'type' : 'button' ," +
		  			"'label' : 'Upload a file'," +
		  			"'subitem' : {" +
		  				upload +
		  			"}" +
	  			"}," +
	  			" 'subid3' : {" +
	  			"'type' : 'menu' ," +
	  			"'label' : 'Menu' ," +
	  			"'subitem' : {" +
		  			submenu +
	  			"}" +
	  		"}}" +
	  	"} " + 
	  "}"; 
	},
	
	//builds the testing ui for debuggin. It shows state data and allows to create ui from user input strings
	buildTestingUI : function () {
		var container = new qx.ui.container.Composite();
		this.testUi = container;
		var layout = new qx.ui.layout.VBox();
		layout.setSpacing(10);
		container.setLayout(layout);
		this.getRoot().add(container, {right: 0, top: 40});

		var button = new qx.ui.form.Button("Refresh GUI (state update causes this)");
		button.addListener("execute", this.refresh, this);

		container.add(new qx.ui.core.Spacer(50));
		container.add(new qx.ui.basic.Label("Submit a JSON-String as Delta with key " + this.namespace.ui_structure + ":"));
		var input = new qx.ui.form.TextArea();
		input.app = this;
		container.add(input);

		var button = new qx.ui.form.Button("Submit the above input as structure");
		container.add(button);
		button.addListener("execute", function (e){
			var delta = new Object();
			delta[this.app.namespace.ui_structure] = this.getValue(); 
			this.app.store.submitEventDelta(delta);
		}, input);
		
		var button = new qx.ui.form.Button("TEST ME! (Example JSON)");
		container.add(button);
		button.addListener("execute", function (e){
			var delta = new Object();
			delta[this.app.namespace.ui_structure] = this.app.__getTestString();
			this.app.store.submitEventDelta(delta);
			this.setValue(delta[this.app.namespace.ui_structure]);
		}, input);
		
		container.add(new qx.ui.core.Spacer(50));
		var info = new qx.ui.form.TextArea();
		info.setReadOnly(true);
		container.add(new qx.ui.basic.Label("Submit Delta Info:"));
		container.add(info);
		this.store.bind("modelDebug", info, "value");
	}
	
  }
});