// Object Interface object. By John Haggerty (Slime).
// http://www.slimeland.com/
// Created January 21, 2003

// This include file is not ready to be used by others. 
// It contains much code which is specific to the JavaScript Raytracer, and it's extremely difficult to use and
// integrate with an outside page. It is not complete and there are many things that I intend to change.
// I hope to update it in the future so that other people can easily use it with their own scripts.
// But for now, please don't.

function ObjectProperty(objecttypename, javascriptname, displayname, defaultval)
{
	this.objecttypename = objecttypename;
	this.javascriptname = javascriptname;
	this.displayname = displayname;
	this.defaultval = defaultval;
}

function ObjectType(type, propertylist, constructionfunc, canbesaved)
{
	this.type = type;
	this.propertylist = propertylist;
	this.constructionfunc = constructionfunc;
	if (typeof(canbesaved) == 'undefined')
		this.canbesaved = true;
	else
		this.canbesaved = canbesaved;
	// for bools, scalars, vectors, and colors, there is no propertylist.
	// for objects, it's a list of properties in the form of objectproperties.
	// for optionlists, it's a list of option strings.
	// for arrays, it's a list of accepted elements in the form of objectproperties with no javascriptname.
	// for objectchoices, it's a list of choices.
}
// object type constants:
ObjectType.TYPE_BOOL = 0;
ObjectType.TYPE_SCALAR = 1;
ObjectType.TYPE_STRING = 2;
ObjectType.TYPE_MULTILINE = 3;
ObjectType.TYPE_VECTOR = 4;
ObjectType.TYPE_COLOR = 5;
ObjectType.TYPE_CHOICE = 6;
ObjectType.TYPE_PROPERTYLIST = 7;
ObjectType.TYPE_OBJECTCHOICE = 8;
ObjectType.TYPE_OBJECT = 9;
ObjectType.TYPE_ARRAY = 10;

function ObjectInterface(parentscorrespondingprop, objinstance,id, toplevel,
						isarrayelem, // optional, assumed false
						isobjectchoice, parentinterface, trueparentscorrespondingprop // optional, assumed false
						)
{
	this.parentscorrespondingprop = parentscorrespondingprop;
	this.objecttype = ObjectInterface.objecttypes[parentscorrespondingprop.objecttypename];
	this.instance = objinstance;
	this.id = id;
	this.toplevel = toplevel;
	
	this.completing = this.switchingoptionchoice = false; // (flags)

	this.isobject = (this.objecttype.type == ObjectType.TYPE_ARRAY || 
	                this.objecttype.type == ObjectType.TYPE_OBJECTCHOICE || 
	                this.objecttype.type == ObjectType.TYPE_OBJECT);

	this.isobjectchoice = isobjectchoice;
	this.objectchoicenum = 0;
	this.parentinterface = parentinterface;
	this.trueparentscorrespondingprop = trueparentscorrespondingprop;
	this.isarrayelem = isarrayelem;
	this.toplevel = toplevel;

	this.createfieldset();
}
ObjectInterface.prototype.createfieldset = function()
{
	// sadly, many nested fieldsets are extremely slow, so we have to use semantically incorrect markup =(
	this.fieldset = document.createElement('div'); // fieldset
		this.fieldset.className = 'fieldset';
	var legend = document.createElement('div'); // legend
		legend.className = 'legend';
	if (this.isobjectchoice)
		legend.appendChild(document.createTextNode(this.trueparentscorrespondingprop.displayname + ' (' + this.parentscorrespondingprop.displayname + ')'));
	else
		legend.appendChild(document.createTextNode(this.parentscorrespondingprop.displayname));
	this.fieldset.appendChild(legend);

	this.childinterfaces = new Array();
	
	if (this.isobjectchoice)
	{
		var theprop = this.trueparentscorrespondingprop;
		var thelabel = document.createElement('label'), theselect = document.createElement('select');
		thelabel.appendChild(document.createTextNode('Change to:'));
		thelabel.setAttribute('for',(theselect.id = 'obj' + theprop.javascriptname + 'changeselect' + this.id));

		theselect.shouldbeoptions = new Array();
		for (a=0; a < ObjectInterface.objecttypes[theprop.objecttypename].propertylist.length; a++)
		{
			if (ObjectInterface.objecttypes[theprop.objecttypename].propertylist[a] == this.parentscorrespondingprop)
				this.objectchoicenum = a;
			theselect.shouldbeoptions[a] = new Option(ObjectInterface.objecttypes[theprop.objecttypename].propertylist[a].displayname,a,a==this.objectchoicenum,a==this.objectchoicenum);
		}
		theselect.selectedIndex = this.objectchoicenum;
		this.fieldset.appendChild(thelabel); this.fieldset.appendChild(document.createTextNode(' ')); this.fieldset.appendChild(theselect); this.fieldset.appendChild(document.createTextNode(' '));
		
		var thebtn = ObjectInterface.createbutton('Change', 'obj' + theprop.javascriptname + 'changebtn' + this.id);
		this.fieldset.appendChild(thebtn);
		ObjectInterface.appendnewline(this.fieldset);

		thebtn.associatedinterface = this;
		thebtn.associatedlist = theselect;
		thebtn.associatedparentinterface = this.parentinterface;
		thebtn.associatedproperty = theprop;
		thebtn.onclick = function() {
			this.associatedparentinterface.switchingoptionchoice = true; // bit of a hack, i guess
			this.associatedinterface.cancel();
			this.associatedparentinterface.showchildinterface(this.associatedproperty, this.associatedinterface.parentsbuttonelem, null, this.associatedlist.selectedIndex);
			this.associatedparentinterface.switchingoptionchoice = false;
		}
	}
	
	this.properties = new Array();
	this.propnum = 0;
	if (this.toplevel)
		this.appendProperty(new ObjectProperty('string','___name','Name',''), true);
	if (this.objecttype.type == ObjectType.TYPE_OBJECT)
	{
		// add inputs
		for (var a=0; a < this.objecttype.propertylist.length; a++)
			this.appendProperty(this.objecttype.propertylist[a]);
	}
	else if (this.objecttype.type == ObjectType.TYPE_ARRAY)
	{
		for (var a=0; a < this.objecttype.propertylist.length; a++)
			this.appendArrayProperty(this.objecttype.propertylist[a]);
		
		var selectelem, table,tbody,tr,td;
		table = document.createElement('table');
		tbody = document.createElement('tbody');
		table.appendChild(tbody);
		tr = document.createElement('tr');
		tbody.appendChild(tr);
		td = document.createElement('td'); td.style.verticalAlign='top';
		tr.appendChild(td);
		if (this.properties.length > 1)
		{
			selectelem = document.createElement('select');
			selectelem.id = 'nonpropobjselect' + this.id;
			selectelem.shouldbeoptions = new Array();
			for (var a=0; a < this.properties.length; a++)
				selectelem.shouldbeoptions[a] = new Option(this.properties[a].displayname, ObjectInterface.objecttypes[this.properties[a].objecttypename].type, false, true);
			selectelem.setAttribute('size',Math.min(5,this.properties.length));
			td.appendChild(selectelem);
			ObjectInterface.appendnewline(td);
			selectelem.selectedIndex = 0;
		}
		var buttontext = 'Create';
		if (this.properties.length == 1)
			buttontext = 'Create ' + this.properties[0].displayname;
		this.addbtn = ObjectInterface.createbutton(buttontext, 'addbtn' + this.id);
		this.addbtn.associatedinterface = this;
		this.addbtn.uselist = (this.properties.length > 1);
		if (this.properties.length > 1)
			this.addbtn.associatedlist = selectelem;
		this.addbtn.onclick = function() {
			var editing = (this.associatedinterface.editingindex >= 0)?'editing':'creating'
			if (this.associatedinterface.editingindex != -1) {if (confirm('Proceeding will cancel the object you are currently ' + editing + '.')) {this.associatedinterface.childinterfaces[0].cancel();} else return;}
			if (this.uselist && this.associatedlist.selectedIndex == -1) {alert("Please first select an item from the list."); return;}
			else if (this.uselist) this.associatedinterface.showchildinterface(this.associatedinterface.properties[this.associatedlist.selectedIndex], this);
			else this.associatedinterface.showchildinterface(this.associatedinterface.properties[0], this);
			this.associatedinterface.editingindex = -2;
		}
		td.appendChild(this.addbtn);

		td = document.createElement('td'); td.style.verticalAlign='top';
		tr.appendChild(td);
		this.listelem = document.createElement('select');
		this.listelem.id = 'nonpropobjlist' + this.id;
		this.listelem.setAttribute('size',5);
		td.appendChild(this.listelem);
		td = document.createElement('td'); td.style.verticalAlign='top';
		tr.appendChild(td);
		var curbtn;
		td.appendChild(curbtn = ObjectInterface.createbutton('Edit','editbtn'+this.id));
		curbtn.associatedlist = this.listelem; curbtn.associatedinterface = this;
		curbtn.onclick = function() {
			var editing = (this.associatedinterface.editingindex >= 0)?'editing':'creating'
			if (this.associatedinterface.editingindex != -1) {if (confirm('Proceeding will cancel the object you are currently ' + editing + '.')) {this.associatedinterface.childinterfaces[0].cancel();} else return;}
			if (this.associatedlist.selectedIndex == -1) {alert("Please first select an item from the list."); return;}
			var indexnum = this.associatedlist.selectedIndex;
			this.associatedinterface.showchildinterface(this.associatedinterface.instance[indexnum].parentsprop, this.associatedinterface.addbtn, this.associatedinterface.instance[indexnum].object);
			this.associatedinterface.editingindex = indexnum;
		}
		td.appendChild(curbtn = ObjectInterface.createbutton('Remove','removebtn'+this.id));
		curbtn.associatedlist = this.listelem; curbtn.associatedinterface = this;
		curbtn.onclick = function() {
			if (this.associatedlist.selectedIndex == -1) {alert("Please first select an item from the list."); return;}
			if (!confirm('Remove this item from the list?')) return;
			if (this.associatedinterface.editingindex == this.associatedlist.selectedIndex) this.associatedinterface.childinterfaces[0].cancel();
			var indexnum = this.associatedlist.selectedIndex;
			if (indexnum != this.associatedlist.options.length-1) this.associatedlist.selectedIndex++;
			else this.associatedlist.selectedIndex--; 
			this.associatedlist.options[indexnum] = null;
			for (var a=indexnum; a < this.associatedinterface.instance.length-1; a++)
				this.associatedinterface.instance[a] = this.associatedinterface.instance[a+1];
			this.associatedinterface.instance.length--;
		}
		td.appendChild(curbtn = ObjectInterface.createbutton('Duplicate','duplicatebtn'+this.id));
		curbtn.associatedlist = this.listelem; curbtn.associatedinterface = this;
		curbtn.onclick = function() {
			if (this.associatedlist.selectedIndex == -1) {alert("Please first select an item from the list."); return;}
			var indexnum = this.associatedlist.selectedIndex;
			var newobj = this.associatedinterface.instance[this.associatedinterface.instance.length] = new Object();
			newobj.object = ObjectInterface.copyobj(this.associatedinterface.instance[indexnum].object,this.associatedinterface.instance[indexnum].parentsprop.objecttypename);
			newobj.parentsprop = this.associatedinterface.instance[indexnum].parentsprop;
			this.associatedlist.options[this.associatedlist.options.length] = new Option(this.associatedinterface.instance[indexnum].parentsprop.displayname,indexnum,false,true);
		}
		ObjectInterface.appendnewline(td);
		td.appendChild(curbtn = ObjectInterface.createbutton('Move Up','moveupbtn'+this.id));
		curbtn.associatedlist = this.listelem; curbtn.associatedinterface = this;
		curbtn.onclick = function() {
			if (this.associatedinterface.editingindex >= 0) {alert('You cannot change the order of the list while editing an object in it.'); return;}
			if (this.associatedlist.selectedIndex == -1) {alert("Please first select an item from the list."); return;}
			var indexnum = this.associatedlist.selectedIndex;
			if (indexnum == 0) {alert('The selected item is already at the top of the list.'); return;}
			var temp = this.associatedinterface.instance[indexnum-1];
			this.associatedinterface.instance[indexnum-1] = this.associatedinterface.instance[indexnum];
			this.associatedinterface.instance[indexnum] = temp;
			temp = this.associatedlist.options[indexnum-1].text;
			this.associatedlist.options[indexnum-1].text = this.associatedlist.options[indexnum].text;
			this.associatedlist.options[indexnum].text = temp;
			this.associatedlist.selectedIndex--;
		}
		td.appendChild(curbtn = ObjectInterface.createbutton('Move Down','movedownbtn'+this.id));
		curbtn.associatedlist = this.listelem; curbtn.associatedinterface = this;
		curbtn.onclick = function() {
			if (this.associatedinterface.editingindex >= 0) {alert('You cannot change the order of the list while editing an object in it.'); return;}
			if (this.associatedlist.selectedIndex == -1) {alert("Please first select an item from the list."); return;}
			var indexnum = this.associatedlist.selectedIndex;
			if (indexnum == this.associatedlist.options.length-1) {alert('The selected item is already at the bottom of the list.'); return;}
			var temp = this.associatedinterface.instance[indexnum+1];
			this.associatedinterface.instance[indexnum+1] = this.associatedinterface.instance[indexnum];
			this.associatedinterface.instance[indexnum] = temp;
			temp = this.associatedlist.options[indexnum+1].text;
			this.associatedlist.options[indexnum+1].text = this.associatedlist.options[indexnum].text;
			this.associatedlist.options[indexnum].text = temp;
			this.associatedlist.selectedIndex++;
		}
		
		this.fieldset.appendChild(table);

		this.editingindex = -1;
		this.insertchildrenbefore = table; // will be set to its next sibling later on
	}
	else if (!this.isobject) {
		this.instance[this.parentscorrespondingprop.javascriptname] = this.instance;
		this.appendProperty(this.parentscorrespondingprop);
	}
	
	// add ok and cancel buttons
	this.okbtn = ObjectInterface.createbutton('OK', 'okbtn' + this.id); this.fieldset.appendChild(this.okbtn);
	this.cancelbtn = ObjectInterface.createbutton('Cancel', 'cancelbtn' + this.id); this.fieldset.appendChild(this.cancelbtn);
	this.cancelbtn.associatedinterface = this.okbtn.associatedinterface = this;
	this.cancelbtn.onclick = function(){this.associatedinterface.cancel();};
	this.okbtn.onclick = function(){this.associatedinterface.complete();};
	
	if (!this.toplevel && this.isobject && this.objecttype.canbesaved)
	{
		if (ObjectInterface.createvariable)
		{
			this.savebtn = ObjectInterface.createbutton('Save Copy', 'btnsave'+this.id);
			this.fieldset.appendChild(this.savebtn);
			this.savebtn.associatedinterface = this;
			this.savebtn.onclick = function() {
				ObjectInterface.createvariable(this.associatedinterface);
			}
		}
		if (ObjectInterface.retrievevariable)
		{
			this.loadbtn = ObjectInterface.createbutton('Copy from Saved', 'btnload'+this.id);
			this.fieldset.appendChild(this.loadbtn);
			this.loadbtn.associatedinterface = this;
			this.loadbtn.onclick = function() {
				ObjectInterface.retrievevariable(this.associatedinterface);
			}
		}
	}
	
	if (this.objecttype.type == ObjectType.TYPE_ARRAY)
	{
		this.insertchildrenbefore = this.insertchildrenbefore.nextSibling;
		this.listelem.shouldbeoptions = new Array();
		for (a=0; a < this.instance.length; a++)
			this.listelem.shouldbeoptions[a] = new Option(this.instance[a].parentsprop.displayname,a,a==0,a==0);
	}

	// set first form element to be focused
	var searchthese = this.fieldset.getElementsByTagName('*');
	for (var a=0; a < searchthese.length; a++)
	{
		var thiselemname = searchthese[a].nodeName.toLowerCase();
		if (thiselemname == 'input' || thiselemname == 'select' || thiselemname == 'textarea')
		{
			this.tofocus = searchthese[a];
			break;
		}
	}
}
ObjectInterface.prototype.appendProperty = function(theprop, dontaddtoproplist)
{
	switch (ObjectInterface.objecttypes[theprop.objecttypename].type)
	{
		case ObjectType.TYPE_BOOL: case ObjectType.TYPE_SCALAR: case ObjectType.TYPE_STRING: case ObjectType.TYPE_MULTILINE: case ObjectType.TYPE_CHOICE:
			if (typeof(this.instance[theprop.javascriptname]) == 'undefined')
				this.instance[theprop.javascriptname] = theprop.defaultval;
			break;
		case ObjectType.TYPE_VECTOR: case ObjectType.TYPE_COLOR:
			if (typeof(this.instance[theprop.javascriptname]) == 'undefined')
				this.instance[theprop.javascriptname] = ObjectInterface.copyobj(theprop.defaultval, theprop.objecttypename);
			break;
	}
	switch (ObjectInterface.objecttypes[theprop.objecttypename].type)
	{
		case ObjectType.TYPE_BOOL:
			this.fieldset.appendChild(ObjectInterface.createboolinput(theprop.displayname, theprop.javascriptname + this.id, this.instance[theprop.javascriptname]));
			break;
		case ObjectType.TYPE_SCALAR:
			this.fieldset.appendChild(ObjectInterface.createscalarinput(theprop.displayname, theprop.javascriptname + this.id, this.instance[theprop.javascriptname]));
			break;
		case ObjectType.TYPE_STRING:
			this.fieldset.appendChild(ObjectInterface.createscalarinput(theprop.displayname, theprop.javascriptname + this.id, this.instance[theprop.javascriptname], true));
			break;
		case ObjectType.TYPE_MULTILINE:
			this.fieldset.appendChild(ObjectInterface.createmultilineinput(theprop.displayname, theprop.javascriptname + this.id, this.instance[theprop.javascriptname]));
			break;
		case ObjectType.TYPE_VECTOR:
			this.fieldset.appendChild(ObjectInterface.createvectorinput(theprop.displayname, theprop.javascriptname + this.id, this.instance[theprop.javascriptname]));
			break;
		case ObjectType.TYPE_COLOR:
			this.fieldset.appendChild(ObjectInterface.createvectorinput(theprop.displayname, theprop.javascriptname + this.id, this.instance[theprop.javascriptname], true));
			break;
		case ObjectType.TYPE_CHOICE:
			this.fieldset.appendChild(ObjectInterface.createchoiceinput(theprop.displayname, theprop.javascriptname + this.id, this.instance[theprop.javascriptname], ObjectInterface.objecttypes[theprop.objecttypename].propertylist));
			break;
		case ObjectType.TYPE_PROPERTYLIST:
			for (var a=0; a < ObjectInterface.objecttypes[theprop.objecttypename].propertylist.length; a++)
				this.appendProperty(ObjectInterface.objecttypes[theprop.objecttypename].propertylist[a]);
			return; // skip the rest of this function
		case ObjectType.TYPE_OBJECT: case ObjectType.TYPE_ARRAY: case ObjectType.TYPE_OBJECTCHOICE:
			this.fieldset.appendChild(this.createpropertybutton(theprop));
			ObjectInterface.appendnewline(this.fieldset);
			break;
	}
	if (dontaddtoproplist) ; else {
		this.properties[this.propnum] = theprop;
		this.propnum++;
	}
}
ObjectInterface.prototype.createpropertybutton = function(theprop)
{
	if (ObjectInterface.objecttypes[theprop.objecttypename].type == ObjectType.TYPE_OBJECTCHOICE && typeof(this.instance[theprop.javascriptname]) == 'undefined')
	{
		var thespan = document.createElement('span'), thelabel = document.createElement('label'), theselect = document.createElement('select');
		thespan.id = 'obj' + theprop.javascriptname + this.id;
		thelabel.appendChild(document.createTextNode(theprop.displayname + ':'));
		thelabel.setAttribute('for',(theselect.id = 'obj' + theprop.javascriptname + 'select' + this.id));
		theselect.shouldbeoptions = new Array();
		for (a=0; a < ObjectInterface.objecttypes[theprop.objecttypename].propertylist.length; a++) {
			theselect.shouldbeoptions[a] = new Option(ObjectInterface.objecttypes[theprop.objecttypename].propertylist[a].displayname,a,a==theprop.defaultval,a==theprop.defaultval);
			//theselect.options[a] = new Option(ObjectInterface.objecttypes[theprop.objecttypename].propertylist[a].displayname,a,a==theprop.defaultval,a==theprop.defaultval);
		}
		thespan.appendChild(thelabel); thespan.appendChild(document.createTextNode(' ')); thespan.appendChild(theselect); thespan.appendChild(document.createTextNode(' '));
		var thebtn = ObjectInterface.createbutton('Set...', 'obj' + theprop.javascriptname + 'btn' + this.id);
		thespan.appendChild(thebtn);

		thebtn.associatedinterface = this;
		thebtn.associatedlist = theselect;
		thebtn.associatedproperty = theprop;
		thebtn.onclick = function() {
			this.associatedinterface.showchildinterface(this.associatedproperty, this, null, this.associatedlist.selectedIndex);
		}

		return thespan;
	}
	else
	{
		var btntext = theprop.displayname;
		if (ObjectInterface.objecttypes[theprop.objecttypename].type == ObjectType.TYPE_OBJECTCHOICE)
			btntext += ' (' + ObjectInterface.objecttypes[theprop.objecttypename].propertylist[this.instance[theprop.javascriptname]].displayname + ')';
		var thebtn = ObjectInterface.createbutton(btntext + '...', theprop.javascriptname + this.id);
		thebtn.associatedinterface = this;
		thebtn.associatedproperty = theprop;
		thebtn.onclick = function() {
			this.associatedinterface.showchildinterface(this.associatedproperty, this);
		}
		return thebtn;
	}
}
ObjectInterface.prototype.appendArrayProperty = function(theprop)
{
	if (ObjectInterface.objecttypes[theprop.objecttypename].type == ObjectType.TYPE_PROPERTYLIST)
	{
		for (var a=0; a < ObjectInterface.objecttypes[theprop.objecttypename].propertylist.length; a++)
			this.appendArrayProperty(ObjectInterface.objecttypes[theprop.objecttypename].propertylist[a]);
		return;
	}
	this.properties[this.propnum] = theprop;
	this.propnum++;
}
ObjectInterface.prototype.showchildinterface = function(theprop, buttonelem, arrayeleminstance, objectchoice)
{
	var isarrayelem = (this.objecttype.type == ObjectType.TYPE_ARRAY),
		isobjectchoice = (ObjectInterface.objecttypes[theprop.objecttypename].type == ObjectType.TYPE_OBJECTCHOICE);
	var insertbefore = buttonelem;
	var theoriginalprop;
	
	if (!isarrayelem && !isobjectchoice)
	{
		buttonelem.temponclick = buttonelem.onclick;
		buttonelem.onclick = null;
	}
	else if (isobjectchoice)
	{
		if (typeof(this.instance[theprop.javascriptname]) == 'undefined')
			insertbefore = document.getElementById('obj' + theprop.javascriptname + this.id);
		else if (typeof(objectchoice) == 'undefined')
			objectchoice = this.instance[theprop.javascriptname];
		if (!this.switchingoptionchoice)
		{
			buttonelem.temponclick = buttonelem.onclick;
			buttonelem.onclick = null;
		}
		
		// fool it
		theoriginalprop = theprop;
		theprop = ObjectInterface.objecttypes[theprop.objecttypename].propertylist[objectchoice];
	}
	
	if (!isarrayelem)
	{
		insertbefore.style.display = 'none';
		if (insertbefore.nextSibling && insertbefore.nextSibling.nodeName.toLowerCase() == 'br')
			insertbefore.nextSibling.style.display = 'none';
	}
	
	var ifacenum = this.childinterfaces.length;
	var theinstance;

	if (isarrayelem)
		theinstance = ObjectInterface.copyobj(arrayeleminstance,theprop.objecttypename);
	else
		theinstance = ObjectInterface.copyobj(this.instance[theprop.javascriptname],theprop.objecttypename);
	
	// make sure the object/array exists before displaying an interface for it:
	if (typeof(theinstance) == 'undefined') {
		switch (ObjectInterface.objecttypes[theprop.objecttypename].type) {
			case ObjectType.TYPE_BOOL: case ObjectType.TYPE_SCALAR: case ObjectType.TYPE_STRING: case ObjectType.TYPE_MULTILINE: case ObjectType.TYPE_CHOICE: case ObjectType.TYPE_OBJECTCHOICE:
				theinstance = theprop.defaultval;
				break;
			case ObjectType.TYPE_VECTOR: case ObjectType.TYPE_COLOR:
				theinstance = theprop.defaultval.copy();
				break;
			case ObjectType.TYPE_ARRAY:
				theinstance = new Array();
				break;
			case ObjectType.TYPE_OBJECT:
				theinstance = new Object();
				break;
		}
	}
	
	// display the interface:
	this.childinterfaces[ifacenum] = new ObjectInterface(theprop, theinstance,this.id+'_'+ifacenum, false, isarrayelem, isobjectchoice, this, theoriginalprop);
	this.childinterfaces[ifacenum].parentinterface = this;
	this.childinterfaces[ifacenum].parentsifacenum = ifacenum;
	this.childinterfaces[ifacenum].parentsbuttonelem = buttonelem;
	
	if (isarrayelem)
		insertbefore = this.insertchildrenbefore;
	this.fieldset.insertBefore(this.childinterfaces[ifacenum].fieldset, insertbefore);

	if (this.childinterfaces[ifacenum].tofocus)
		this.childinterfaces[ifacenum].tofocus.focus();	
	ObjectInterface.fixieformbug();
}
ObjectInterface.fixieformbug = function() {
	// stupid IE form element bug; setting "checked" before a checkbox has been added to the document doesn't take effect
	var inputs=document.getElementsByTagName('input');
	for (a=0; a < inputs.length; a++)
	{
		if (inputs[a].shouldbechecked) {
			inputs[a].checked = true;
			inputs[a].shouldbechecked = false;
		}
	}
	inputs=document.getElementsByTagName('select');
	for (a=0; a < inputs.length; a++)
	{
		if (inputs[a].shouldbeselectednum && inputs[a].shouldbeselectednum >= 0) {
			inputs[a].selectedIndex = inputs[a].shouldbeselectednum;
			inputs[a].shouldbeselectednum = -1;
		}
		// ie 5 form bug... causes a memory error sometimes if i set the options right after creating it with the DOM
		if (inputs[a].shouldbeoptions && inputs[a].shouldbeoptions.length > 0) {
			if (inputs[a].options.length != 0)
				inputs[a].options.length = 0;
			for (var b=0; b < inputs[a].shouldbeoptions.length; b++)
				inputs[a].options[b] = inputs[a].shouldbeoptions[b];
			inputs[a].shouldbeoptions.length = 0;
		}
	}
}
ObjectInterface.prototype.cancel = function() {
	// cancel any open child interfaces
	for (var a=0; a < this.childinterfaces.length; a++)
		this.childinterfaces[a].cancel();
	if (this.parentinterface) this.parentinterface.childcancelled(this);
	else this.fieldset.parentNode.removeChild(this.fieldset);
}
ObjectInterface.prototype.complete = function() {
	this.completing = true;
	// complete any open child interfaces
	for (var a=0; a < this.childinterfaces.length; a++)
		this.childinterfaces[a].complete();
	this.childinterfaces.length = 0;
	this.retrieveProperties(this.instance);
	if (!this.isobject)
	{
		this.retrieveProperty(this.parentscorrespondingprop, this.instance);
		this.instance = this.instance[this.parentscorrespondingprop.javascriptname];
	}
	if (this.parentinterface) this.parentinterface.childcompleted(this);
	else this.fieldset.parentNode.removeChild(this.fieldset);
}
ObjectInterface.prototype.getcurrentinstance = function()
{
	var currentinstance = ObjectInterface.copyobj(this.instance, this.parentscorrespondingprop.objecttypename);
	for (var a=0; a < this.childinterfaces.length; a++)
		this.retrievechilddata(this.childinterfaces[a], currentinstance, this.childinterfaces[a].getcurrentinstance());
	this.retrieveProperties(currentinstance);
	if (!this.isobject)
	{
		this.retrieveProperty(this.parentscorrespondingprop, currentinstance);
		currentinstance = currentinstance[this.parentscorrespondingprop.javascriptname];
	}
	return currentinstance;
}
ObjectInterface.prototype.retrieveProperties = function(theinstance)
{
	if (this.objecttype.type == ObjectType.TYPE_OBJECT)
	{
		for (var a=0; a < this.objecttype.propertylist.length; a++)
			this.retrieveProperty(this.objecttype.propertylist[a], theinstance);
	}
}
ObjectInterface.prototype.retrieveProperty = function(theprop, theinstance)
{
	switch (ObjectInterface.objecttypes[theprop.objecttypename].type)
	{
		case ObjectType.TYPE_BOOL:
			theinstance[theprop.javascriptname] = ObjectInterface.getboolinput(theprop.javascriptname + this.id);
			break;
		case ObjectType.TYPE_SCALAR:
			theinstance[theprop.javascriptname] = ObjectInterface.getscalarinput(theprop.javascriptname + this.id);
			break;
		case ObjectType.TYPE_STRING:
			theinstance[theprop.javascriptname] = ObjectInterface.getstringinput(theprop.javascriptname + this.id);
			break;
		case ObjectType.TYPE_MULTILINE:
			theinstance[theprop.javascriptname] = ObjectInterface.getmultilineinput(theprop.javascriptname + this.id);
			break;
		case ObjectType.TYPE_VECTOR:
			theinstance[theprop.javascriptname] = ObjectInterface.getvectorinput(theprop.javascriptname + this.id);
			break;
		case ObjectType.TYPE_COLOR:
			theinstance[theprop.javascriptname] = ObjectInterface.getcolorinput(theprop.javascriptname + this.id);
			break;
		case ObjectType.TYPE_CHOICE:
			theinstance[theprop.javascriptname] = ObjectInterface.getchoiceinput(theprop.javascriptname + this.id);
			break;
		case ObjectType.TYPE_PROPERTYLIST:
			for (var a=0; a < ObjectInterface.objecttypes[theprop.objecttypename].propertylist.length; a++)
				this.retrieveProperty(ObjectInterface.objecttypes[theprop.objecttypename].propertylist[a], theinstance);
			break;
	}
}
ObjectInterface.prototype.childcompleted = function(childinterface) {
	this.retrievechilddata(childinterface, this.instance, childinterface.instance);
	if (!this.completing) this.childcancelled(childinterface);
}
ObjectInterface.prototype.retrievechilddata = function(childinterface, theinstance, thechildsinstance) {
	if (childinterface.isobjectchoice) {
		theinstance[childinterface.trueparentscorrespondingprop.javascriptname] = childinterface.objectchoicenum;
	}
	if (this.objecttype.type == ObjectType.TYPE_ARRAY) {
		var theindex = this.editingindex;
		if (theindex == -2) {
			theindex = this.instance.length;
			theinstance[theindex] = new Object();
		}
		theinstance[theindex].object = thechildsinstance;
		theinstance[theindex].parentsprop = childinterface.parentscorrespondingprop;
		if (this.editingindex == -2)
			this.listelem.options[this.listelem.options.length] = new Option(childinterface.parentscorrespondingprop.displayname,theindex,false,true);
		this.editingindex = -1;
	}
	else {
		theinstance[childinterface.parentscorrespondingprop.javascriptname] = thechildsinstance;
	}
}
ObjectInterface.prototype.childcancelled = function(childinterface) {
	if (this.objecttype.type == ObjectType.TYPE_OBJECT && !this.switchingoptionchoice)
	{
		var buttonelem;
		if (childinterface.isobjectchoice && typeof(this.instance[childinterface.trueparentscorrespondingprop.javascriptname]) == 'undefined')
		{
			var spanelem = childinterface.fieldset.nextSibling;
			spanelem.style.display = 'inline';
			if (spanelem.nextSibling && spanelem.nextSibling.nodeName.toLowerCase() == 'br')
				spanelem.nextSibling.style.display = 'inline';
			buttonelem = childinterface.parentsbuttonelem;
			buttonelem.onclick = buttonelem.temponclick;
			buttonelem.temponclick = null;
		}
		else if (childinterface.isobjectchoice && childinterface.fieldset.nextSibling.nodeName.toLowerCase() == 'span')
		{
			buttonelem = this.createpropertybutton(childinterface.trueparentscorrespondingprop);
			this.fieldset.insertBefore(buttonelem, childinterface.fieldset.nextSibling);
			this.fieldset.removeChild(buttonelem.nextSibling);
			buttonelem.nextSibling.style.display = 'inline';
		}
		else
		{
			buttonelem = childinterface.parentsbuttonelem;
			if (childinterface.isobjectchoice && typeof(this.instance[childinterface.trueparentscorrespondingprop.javascriptname]) != 'undefined')
				buttonelem.value = childinterface.trueparentscorrespondingprop.displayname + ' (' + ObjectInterface.objecttypes[childinterface.trueparentscorrespondingprop.javascriptname].propertylist[this.instance[childinterface.trueparentscorrespondingprop.javascriptname]].displayname + ')';
			buttonelem.style.display = 'inline';
			if (buttonelem.nextSibling && buttonelem.nextSibling.nodeName.toLowerCase() == 'br')
				buttonelem.nextSibling.style.display = 'inline';
			buttonelem.onclick = buttonelem.temponclick;
			buttonelem.temponclick = null;
		}
		buttonelem.focus();
	}
	else if (this.objecttype.type == ObjectType.TYPE_ARRAY)
	{
		this.editingindex = -1;
	}
	this.fieldset.removeChild(childinterface.fieldset);
	for (var a=childinterface.parentsifacenum; a < this.childinterfaces.length-1; a++)
	{
		this.childinterfaces[a] = this.childinterfaces[a+1];
		this.childinterfaces[a].parentsifacenum--;
	}
	--this.childinterfaces.length;
}
ObjectInterface.prototype.setcurrentinstance = function(newinstance)
{
	for (var a=0; a < this.childinterfaces.length; a++)
		this.childinterfaces[a].cancel();
	this.instance = ObjectInterface.copyobj(newinstance,this.parentscorrespondingprop.objecttypename);
	var oldfieldset = this.fieldset;
	this.createfieldset();
	oldfieldset.style.display = 'none';
	oldfieldset.parentNode.insertBefore(this.fieldset,oldfieldset);
	oldfieldset.parentNode.removeChild(oldfieldset);
	ObjectInterface.fixieformbug(); // foiled again! ugh.
}

ObjectInterface.createbutton = function(buttontext, id)
{
	var button = document.createElement('input');
	if (id)
		button.id = id;
	button.setAttribute('type','button');
	button.setAttribute('value',buttontext);
	return button;
}
ObjectInterface.appendnewline = function(elem)
{
	return elem.appendChild(document.createElement('br'));
}
ObjectInterface.createvectorinput = function(labeltext, idbase, defaultval, treatascolor)
{
	var labelelem, textbox, elem = document.createElement('span');
	labelelem = document.createElement('label');
	labelelem.setAttribute('for','obj'+idbase+'x');
	labelelem.appendChild(document.createTextNode(labeltext + ':'));
	elem.appendChild(labelelem);
	if (treatascolor)
		elem.appendChild(document.createTextNode(' rgb('));
	else
		elem.appendChild(document.createTextNode(' <'));

	textbox = document.createElement('input');
	textbox.id = 'obj' + idbase + 'x';
	textbox.setAttribute('type','textbox');
	if (treatascolor)
		textbox.setAttribute('value',defaultval.red);
	else
		textbox.setAttribute('value',defaultval.x);
	textbox.setAttribute('size','4');
	elem.appendChild(textbox);

	elem.appendChild(document.createTextNode(', '));

	textbox = document.createElement('input');
	textbox.id = 'obj' + idbase + 'y';
	textbox.setAttribute('type','textbox');
	if (treatascolor)
		textbox.setAttribute('value',defaultval.green);
	else
		textbox.setAttribute('value',defaultval.y);
	textbox.setAttribute('size','4');
	elem.appendChild(textbox);

	elem.appendChild(document.createTextNode(', '));

	textbox = document.createElement('input');
	textbox.id = 'obj' + idbase + 'z';
	textbox.setAttribute('type','textbox');
	if (treatascolor)
		textbox.setAttribute('value',defaultval.blue);
	else
		textbox.setAttribute('value',defaultval.z);
	textbox.setAttribute('size','4');
	elem.appendChild(textbox);

	if (treatascolor)
		elem.appendChild(document.createTextNode(')'));
	else
		elem.appendChild(document.createTextNode('>'));
	elem.appendChild(document.createElement('br'));
	return elem;
}
ObjectInterface.getvectorinput = function(idbase)
{
	return new Vector(parseFloat(document.getElementById('obj'+idbase+'x').value),parseFloat(document.getElementById('obj'+idbase+'y').value),parseFloat(document.getElementById('obj'+idbase+'z').value));
}
ObjectInterface.getcolorinput = function(idbase)
{
	return new Color(parseFloat(document.getElementById('obj'+idbase+'x').value),parseFloat(document.getElementById('obj'+idbase+'y').value),parseFloat(document.getElementById('obj'+idbase+'z').value));
}
ObjectInterface.createscalarinput = function(labeltext, idbase, defaultval, treatasstring)
{
	var labelelem, textbox, elem = document.createElement('span');
	labelelem = document.createElement('label');
	labelelem.setAttribute('for','obj'+idbase);
	labelelem.appendChild(document.createTextNode(labeltext + ':'));
	elem.appendChild(labelelem);
	elem.appendChild(document.createTextNode(' '));

	textbox = document.createElement('input');
	textbox.id = 'obj' + idbase;
	textbox.setAttribute('type','textbox');
	textbox.setAttribute('value',defaultval);
	if (treatasstring);
	else
		textbox.setAttribute('size','4');
	elem.appendChild(textbox);

	elem.appendChild(document.createElement('br'));
	return elem;
}
ObjectInterface.getscalarinput = function(idbase)
{
	return parseFloat(document.getElementById('obj'+idbase).value);
}
ObjectInterface.getstringinput = function(idbase)
{
	return document.getElementById('obj'+idbase).value;
}
ObjectInterface.createmultilineinput = function(labeltext, idbase, defaultval)
{
	var labelelem, textbox, elem = document.createElement('span');
	labelelem = document.createElement('label');
	labelelem.setAttribute('for','obj'+idbase);
	labelelem.appendChild(document.createTextNode(labeltext + ':'));
	elem.appendChild(labelelem);
	elem.appendChild(document.createElement('br'));
	
	textbox = document.createElement('textarea');
	textbox.id = 'obj' + idbase;
	textbox.value = defaultval;
	elem.appendChild(textbox);

	elem.appendChild(document.createElement('br'));
	return elem;
}
ObjectInterface.getmultilineinput = function(idbase)
{
	return document.getElementById('obj'+idbase).value;
}
ObjectInterface.createboolinput = function(labeltext, idbase, defaultval)
{
	var labelelem, checkbox, elem = document.createElement('span');

	checkbox = document.createElement('input');
	checkbox.id = 'obj' + idbase;
	checkbox.setAttribute('type','checkbox');
	checkbox.checked = defaultval;
	checkbox.shouldbechecked = defaultval; // stupid IE bug, followed up later
	elem.appendChild(checkbox);

	elem.appendChild(document.createTextNode(' '));

	labelelem = document.createElement('label');
	labelelem.setAttribute('for','obj'+idbase);
	labelelem.appendChild(document.createTextNode(labeltext));
	elem.appendChild(labelelem);

	elem.appendChild(document.createElement('br'));
	return elem;
}
ObjectInterface.getboolinput = function(idbase)
{
	return document.getElementById('obj'+idbase).checked;
}
ObjectInterface.createchoiceinput = function(labeltext, idbase, defaultval, choices)
{
	var labelelem, selectelem, elem = document.createElement('span');

	var labelelem, textbox;
	labelelem = document.createElement('label');
	labelelem.setAttribute('for','obj'+idbase);
	labelelem.appendChild(document.createTextNode(labeltext + ':'));
	elem.appendChild(labelelem);
	elem.appendChild(document.createTextNode(' '));

	selectelem = document.createElement('select');
	selectelem.id = 'obj' + idbase;

	selectelem.shouldbeoptions = new Array();
	for (a = 0; a < choices.length; a++)
	{
		selectelem.shouldbeoptions[a] = new Option(choices[a], a, a == defaultval, a == defaultval);
	}
	selectelem.shouldbeselectednum = defaultval;

	elem.appendChild(selectelem);

	elem.appendChild(document.createElement('br'));
	return elem;
}
ObjectInterface.getchoiceinput = function(idbase)
{
	return document.getElementById('obj'+idbase).selectedIndex;
}
ObjectInterface.copyobj = function(theobj, objecttypename) // deep copies an object
{
	var toreturn, proplist;
	if (typeof(theobj) == 'undefined') return theobj;
	if (ObjectInterface.objecttypes[objecttypename].propertylist && ObjectInterface.objecttypes[objecttypename].type != ObjectType.TYPE_CHOICE)
		proplist = ObjectInterface.buildproplist(ObjectInterface.objecttypes[objecttypename].propertylist,objecttypename);
	switch (ObjectInterface.objecttypes[objecttypename].type)
	{
		case ObjectType.TYPE_BOOL: case ObjectType.TYPE_SCALAR: case ObjectType.TYPE_STRING: case ObjectType.TYPE_MULTILINE: case ObjectType.TYPE_CHOICE: case ObjectType.TYPE_OBJECTCHOICE:
			toreturn = theobj;
			break;
		case ObjectType.TYPE_VECTOR: case ObjectType.TYPE_COLOR:
			toreturn = theobj.copy();
			break;
		case ObjectType.TYPE_OBJECT:
			toreturn = new Object();
			if (typeof(theobj) == 'undefined')
				theobj = new Object();
			for (var a=0; a < proplist.length; a++)
				toreturn[proplist[a].javascriptname] = ObjectInterface.copyobj(theobj[proplist[a].javascriptname], proplist[a].objecttypename);
			for (var a=0; a < proplist.length; a++)
			{
				if (ObjectInterface.objecttypes[proplist[a].objecttypename].type == ObjectType.TYPE_OBJECTCHOICE && typeof(theobj[proplist[a].javascriptname]) != 'undefined') {
					var theproperty = ObjectInterface.objecttypes[proplist[a].objecttypename].propertylist[theobj[proplist[a].javascriptname]];
					toreturn[theproperty.javascriptname] = ObjectInterface.copyobj(theobj[theproperty.javascriptname], theproperty.objecttypename);
				}
			}
			break;
		case ObjectType.TYPE_ARRAY:
			toreturn = new Array();
			for (var a=0; a < theobj.length; a++) {
				toreturn[a] = new Object();
				toreturn[a].object = ObjectInterface.copyobj(theobj[a].object, theobj[a].parentsprop.objecttypename);
				toreturn[a].parentsprop = theobj[a].parentsprop;
			}
			break;
	}
	return toreturn;
}
ObjectInterface.constructObject = function(theobj, objecttypename)
{
	var toreturn, proplist;
	if (ObjectInterface.objecttypes[objecttypename].propertylist && ObjectInterface.objecttypes[objecttypename].type != ObjectType.TYPE_CHOICE)
		proplist = ObjectInterface.buildproplist(ObjectInterface.objecttypes[objecttypename].propertylist,objecttypename);
	switch (ObjectInterface.objecttypes[objecttypename].type)
	{
		case ObjectType.TYPE_BOOL: case ObjectType.TYPE_SCALAR: case ObjectType.TYPE_STRING: case ObjectType.TYPE_MULTILINE: case ObjectType.TYPE_CHOICE: case ObjectType.TYPE_OBJECTCHOICE:
			toreturn = theobj;
			break;
		case ObjectType.TYPE_VECTOR: case ObjectType.TYPE_COLOR:
			toreturn = theobj.copy();
			break;
		case ObjectType.TYPE_OBJECT:
			toreturn = new Object();
			if (typeof(theobj) == 'undefined')
				theobj = new Object();
			for (var a=0; a < proplist.length; a++)
			{
				if (typeof(theobj[proplist[a].javascriptname]) == 'undefined')
				{
					switch (ObjectInterface.objecttypes[proplist[a].objecttypename].type)
					{
						case ObjectType.TYPE_BOOL: case ObjectType.TYPE_SCALAR: case ObjectType.TYPE_STRING: case ObjectType.TYPE_MULTILINE: case ObjectType.TYPE_CHOICE: case ObjectType.TYPE_OBJECTCHOICE:
							theobj[proplist[a].javascriptname] = proplist[a].defaultval;
							break;
						case ObjectType.TYPE_VECTOR: case ObjectType.TYPE_COLOR:
							theobj[proplist[a].javascriptname] = proplist[a].defaultval.copy();
							break;
					}
				}
				toreturn[proplist[a].javascriptname] = ObjectInterface.constructObject(theobj[proplist[a].javascriptname], proplist[a].objecttypename);
			}
			for (var a=0; a < proplist.length; a++)
			{
				if (ObjectInterface.objecttypes[proplist[a].objecttypename].type == ObjectType.TYPE_OBJECTCHOICE) {
					var theproperty = ObjectInterface.objecttypes[proplist[a].objecttypename].propertylist[theobj[proplist[a].javascriptname]];
					if (typeof(theobj[theproperty.javascriptname]) == 'undefined')
					{
						switch (ObjectInterface.objecttypes[proplist[a].objecttypename].type)
						{
							case ObjectType.TYPE_BOOL: case ObjectType.TYPE_SCALAR: case ObjectType.TYPE_STRING: case ObjectType.TYPE_MULTILINE: case ObjectType.TYPE_CHOICE: case ObjectType.TYPE_OBJECTCHOICE:
								theobj[theproperty.javascriptname] = theproperty.defaultval;
								break;
							case ObjectType.TYPE_VECTOR: case ObjectType.TYPE_COLOR:
								theobj[theproperty.javascriptname] = theproperty.defaultval.copy();
								break;
						}
					}
					toreturn[theproperty.javascriptname] = ObjectInterface.constructObject(theobj[theproperty.javascriptname], theproperty.objecttypename);
				}
			}
			toreturn.___constructionfunc = ObjectInterface.objecttypes[objecttypename].constructionfunc;
			var temp = toreturn.___constructionfunc();
			function findpropertylists(toreturn, tempobj, propertylist)
			{
				for (var a=0; a < propertylist.length; a++)
				{
					if (ObjectInterface.objecttypes[propertylist[a].objecttypename].type == ObjectType.TYPE_PROPERTYLIST) {
						findpropertylists(toreturn, tempobj, ObjectInterface.objecttypes[propertylist[a].objecttypename].propertylist);
						toreturn.___constructionfunc = ObjectInterface.objecttypes[propertylist[a].objecttypename].constructionfunc;
						toreturn.___constructionfunc(tempobj);
					}
				}
			}
			findpropertylists(toreturn, temp, ObjectInterface.objecttypes[objecttypename].propertylist);
			toreturn = temp;
			break;
		case ObjectType.TYPE_ARRAY:
			toreturn = new Object();
			var thearr = new Array();
			if (typeof(theobj) == 'undefined') {
				theobj = new Array();
			}
			for (var a=0; a < theobj.length; a++) {
				thearr[a] = ObjectInterface.constructObject(theobj[a].object, theobj[a].parentsprop.objecttypename);
			}
			toreturn.array = thearr;
			toreturn.___constructionfunc = ObjectInterface.objecttypes[objecttypename].constructionfunc;
			toreturn = toreturn.___constructionfunc();
			break;
	}
	return toreturn;
}
ObjectInterface.getcode = function(theobj, objecttypename)
{
	var toreturn = '', proplist;
	if (ObjectInterface.objecttypes[objecttypename].propertylist && ObjectInterface.objecttypes[objecttypename].type != ObjectType.TYPE_CHOICE)
		proplist = ObjectInterface.buildproplist(ObjectInterface.objecttypes[objecttypename].propertylist,objecttypename);
	switch (ObjectInterface.objecttypes[objecttypename].type)
	{
		case ObjectType.TYPE_BOOL: case ObjectType.TYPE_SCALAR: case ObjectType.TYPE_CHOICE: case ObjectType.TYPE_OBJECTCHOICE:
			toreturn += theobj;
			break;
		case ObjectType.TYPE_STRING: case ObjectType.TYPE_MULTILINE:
			toreturn += '"'+ObjectInterface.escapestring(theobj)+'"';
			break;
		case ObjectType.TYPE_VECTOR:
			toreturn += 'new Vector('+theobj.x+','+theobj.y+','+theobj.z+')';
			break;
		case ObjectType.TYPE_COLOR:
			toreturn += 'new Color('+theobj.red+','+theobj.green+','+theobj.blue+')';
			break;
		case ObjectType.TYPE_OBJECT:
			toreturn += '{';
			for (var a=0; a < proplist.length; a++)
			{
				if (typeof(theobj[proplist[a].javascriptname]) != 'undefined') {
					toreturn += proplist[a].javascriptname + ':';
					toreturn += ObjectInterface.getcode(theobj[proplist[a].javascriptname], proplist[a].objecttypename);
					toreturn += ',';
				}
			}
			for (var a=0; a < proplist.length; a++)
			{
				if (ObjectInterface.objecttypes[proplist[a].objecttypename].type == ObjectType.TYPE_OBJECTCHOICE && typeof(theobj[proplist[a].javascriptname]) != 'undefined') {
					var theproperty = ObjectInterface.objecttypes[proplist[a].objecttypename].propertylist[theobj[proplist[a].javascriptname]];
					if (typeof(theobj[theproperty.javascriptname]) != 'undefined') {
						toreturn += theproperty.javascriptname + ':';
						toreturn += ObjectInterface.getcode(theobj[theproperty.javascriptname], theproperty.objecttypename);
						toreturn += ',';
					}
				}
			}
			if (toreturn.substr(toreturn.length-1,1) == ',')
				toreturn = toreturn.substr(0,toreturn.length-1); // cut off last comma
			toreturn += '}';
			break;
		case ObjectType.TYPE_ARRAY:
			toreturn += '[';
			for (var a=0; a < theobj.length; a++) {
				toreturn += '{object:';
				toreturn += ObjectInterface.getcode(theobj[a].object, theobj[a].parentsprop.objecttypename);
				toreturn += ',parentsprop:';
				for (var b=0; b < proplist.length; b++) {
					if (proplist[b].objecttypename == theobj[a].parentsprop.objecttypename &&
						proplist[b].displayname == theobj[a].parentsprop.displayname)
					{
						toreturn += 'ObjectInterface.objecttypes.' + proplist[b].parentsobjecttypename + '.propertylist[' + proplist[b].parentsobjecttypeproplistnum + ']';
						break;
					}
				}
				toreturn += '}';
				if (a != theobj.length-1)
					toreturn += ',';
			}
			toreturn += ']';
			break;
	}
	return toreturn;
}
ObjectInterface.buildproplist = function(propertylist,parentsobjecttypename)
{
	var toreturn = new Array();
	for (var a=0; a < propertylist.length; a++) {
		if (ObjectInterface.objecttypes[propertylist[a].objecttypename].type == ObjectType.TYPE_PROPERTYLIST) {
			var sublist = ObjectInterface.buildproplist(ObjectInterface.objecttypes[propertylist[a].objecttypename].propertylist,propertylist[a].objecttypename);
			for (var b=0; b < sublist.length; b++)
				toreturn[toreturn.length] = sublist[b];
		}
		else {
			toreturn[toreturn.length] = propertylist[a];
			toreturn[toreturn.length-1].parentsobjecttypename = parentsobjecttypename;
			toreturn[toreturn.length-1].parentsobjecttypeproplistnum = a;
		}
	}
	return toreturn;
}

ObjectInterface.escapestring = function(str)
{
	str = str.replace(/\\/g,'\\\\').replace(/"/g,'\\"');

	var a,firstpartlen;
	while ((a=str.indexOf('\n')) != -1)
	{
		firstpartlen=a;
		if (str.charCodeAt(a-1) == 13)
			--firstpartlen;
		str = str.substr(0,firstpartlen) + '\\n' + str.substring(a+1,str.length);
	}
	return str;
}

ObjectInterface.objecttypes = new Object();