if(window.RG === undefined) window.RG = {};

RG.version = {version:1.2, comment:""}

RG.initRG = function(){
	try{
		if(RG.initRegion)
			RG.initRegion();
		if(RG.initContextHelp)
			RG.initContextHelp();
	}catch(e){
	
	}
}

RG.describe = function(object){
	if(object != null){
		if(typeof(object) == "object"){
			var str = "{";
			var first = true;
			for(var n in object){
				if(first){
					str += n + ":" + RG.describe(object[n]);
					first = false;
				}else
					str += ", " + n + ":" + RG.describe(object[n]);
			}
			str += "}";
			return str;
		}else if(typeof(object) == "string")
			return "\"" + object + "\"";
		else
			return object.toString();
	}else if(object === null)
		return "null";
	else
		return "undefined";
}

RG.initRegion = function(){
	RG.RegionSelector = Ext.extend(Ext.BoxComponent, {
		// @cfg {String} chrId - chromosome select id
		// @cfg {String} asmbId - assembly select id
		// @cfg {Number} controlWidth - width of 'select'
		// @cfg {number} tabIndex - tabIndex, takes 2
		tabIndex:0,
		initComponent: function() {
			RG.RegionSelector.superclass.initComponent.apply(this, arguments);
		},
		// private
		onRender: function(ct, position) {
			if(!RG.RegionSelector.template){
				RG.RegionSelector.template = new Ext.Template(
					"<div>",
					"<ul>",
					"<li><label for='{chid}'>Chromosome:</label><select id='{chid}' tabindex='{tab1}'><option>-none-</option></select></li>",
					"<li><label for='{asid}'>Assembly:</label><select id='{asid}' tabindex='{tab2}'><option>-none-</option></select></li>",
					"</ul>",
					"</div>"
				);
				RG.RegionSelector.template.compile();
			}
			this.template = RG.RegionSelector.template;
			var chId = this.chrId || Ext.id();
			var asId = this.asmbId || Ext.id();
			var tabIndex = this.tabIndex || 1;
			var args = {chid: chId, asid: asId, tab1:tabIndex, tab2:tabIndex+1};
			if(position)
				this.el = this.template.insertBefore(position, args, true);
			else
				this.el = this.template.append(ct, args, true);
			if(this.cls)
				this.el.addClass(this.cls);
			RG.RegionSelector.superclass.onRender.apply(this, arguments);
			
			this.chrSelector = Ext.get(chId);
			this.asmbSelector = Ext.get(asId);
			if(this.controlWidth){
				this.chrSelector.setWidth(this.controlWidth + 2);
				this.asmbSelector.setWidth(this.controlWidth + 2);
			}
		}

	});

	Ext.reg("mvregionsel", RG.RegionSelector);
	
	RG.validate = function(eventObj, node, params){ // internal
		node = Ext.get(node);
		var value = node.getValue();
		if(value){
			var ctxt = {mode:params.mode, partial:params.partial};
			if(ncbiformatter.validate(value, ctxt)){
				if(RG.FormatError.getErrorSource() == node.id)
					RG.FormatError.hideError();
				return true;
			}else{
				var el = Ext.get(node);
				var box = el.getBox();
				RG.FormatError.setErrorSource(node.id);
				if(params.mode)
					RG.FormatError.showObjError(value, box.x + this.errorOffset.x, box.y + this.errorOffset.y);
				else
					RG.FormatError.showPosError(value, box.x + this.errorOffset.x, box.y + this.errorOffset.y);
				return false;
			}
		}
		if(RG.FormatError.getErrorSource() == node.id)
			RG.FormatError.hideError();
		return true;
	};

	RG.PositionSelector = Ext.extend(Ext.BoxComponent, {

		label: "Position", // label, From or To
		validation: null, // validation regexp or func
		errorOffset: {x:-80, y:40},	
		// @cfg {Number} controlWidth
		// @cfg {String} textId
		// @cfg {String} headerId
		// @cfg {Number} tabIndex
		tabIndex:0,
		// @cfg {String} boxHeader, if not null - draw a header above
		
		initComponent: function() {
			RG.PositionSelector.superclass.initComponent.apply(this, arguments);
		},
		// private
		onRender: function(ct, position) {
			if(!this.template){
				var array = [
					"<div>",
					"<label for='{id}'>{label}:</label>",
					"<input type='text' id='{id}' tabindex='{tab}'/>",
					"</div>"
				];
				if(this.boxHeader != null)
					array.splice(1, 0, "<h4><label id='{headerId}' for='{id}'>{header}</label></h4>");
				this.template = new Ext.Template(array);
				this.template.compile();
			}
			var id = this.textId || Ext.id();
			var headerId = this.headerId || Ext.id();
			var tabIndex = this.tabIndex || 1;
			var args = {id: id, headerId: headerId, label: this.label, header: this.boxHeader, tab:tabIndex};
			if(position)
				this.el = this.template.insertBefore(position, args, true);
			else
				this.el = this.template.append(ct, args, true);
			if(this.cls)
				this.el.addClass(this.cls);
			RG.PositionSelector.superclass.onRender.apply(this, arguments);

			this.text = Ext.get(id);
			if(this.controlWidth){
				this.text.setWidth(this.controlWidth);
				if(this.boxHeader != null){
					var header = Ext.get(headerId);
					var margin = (this.controlWidth + 6) + "px";
					header.setStyle({"margin-right": margin});
				}
			}
			
			// Add validation
			this.text.addListener("keyup", RG.validate, this, {mode:0, partial:true});
			this.text.addListener("blur", RG.validate, this, {delay:100, mode:0, partial:false, id:id});
		}
	});

	Ext.reg("mvregionpos", RG.PositionSelector);


	RG.ObjectSelComponent = Ext.extend(Ext.BoxComponent, {
		errorOffset: {x:-80, y:40},	
		// @cfg {Number} controlWidth
		// @cfg {Number} tabIndex
		// @cfg {String} label
		tabIndex:0,
		
		initComponent: function() {
			RG.ObjectSelComponent.superclass.initComponent.apply(this, arguments);
		},
		
		// private
		onRender: function(ct, position) {
			if(!RG.ObjectSelComponent.template){
				RG.ObjectSelComponent.template = new Ext.Template(
					"<div>",
					"<h4><label id='{headerId}' for='{objid}'>{label}</label></h4>",
					"<ul>",
					"<li><label for='{objid}'>Feature:</label>",
					"<input type='text' id='{objid}' tabindex='{tab1}'/></li>",
					"<li><label for='{modeid}'>Name:</label>",
					"<select id='{modeid}' tabindex='{tab2}'>",
					"<option value=''>is</option>",
					"<option value='1'>begins with</option>",
					"</select></li>",
					"<li><label for='{typeid}'>Type:</label>",
					"<select id='{typeid}' tabindex='{tab3}'>",
					"<option value=''>Any</option>",
					"<option value='gene'>Gene</option>",
					"<option value='clone'>Clone</option>",
					"<option value='snp'>SNP</option>",
					"<option value='marker,sts'>Marker</option>",
					"<option value='transcript'>Transcript</option>",
					"</select></li>",
					"</ul>",
					"</div>"
				);
				RG.ObjectSelComponent.template.compile();
			}
			this.template = RG.ObjectSelComponent.template;
			var typeId = Ext.id(), modeId = Ext.id(), objId = Ext.id(), headerId = Ext.id();
			var tabIndex = this.tabIndex || 1;
			var label = this.label || "Region";
			var args = {typeid: typeId, modeid: modeId, objid: objId, headerId: headerId, label: label, tab1:tabIndex, tab2:tabIndex+1, tab3:tabIndex+2};
			if(position)
				this.el = this.template.insertBefore(position, args, true);
			else
				this.el = this.template.append(ct, args, true);
			if(this.cls)
				this.el.addClass(this.cls);
			RG.ObjectSelComponent.superclass.onRender.apply(this, arguments);
			
			this.typeSelector = Ext.get(typeId);
			this.modeSelector = Ext.get(modeId);
			this.nameField = Ext.get(objId);
			if(this.controlWidth){
				this.typeSelector.setWidth(this.controlWidth + 2);
				this.modeSelector.setWidth(this.controlWidth + 2);
				this.nameField.setWidth(this.controlWidth);
				var header = Ext.get(headerId);
				var margin = (this.controlWidth + 6) + "px";
				header.setStyle({"margin-right": margin});
			}
			this.nameField.addListener("keyup", RG.validate, this, {buffer:200, mode:1, partial:true});
			this.nameField.addListener("blur", RG.validate, this, {delay:100, mode:1, partial:false, id:objId});
		}
	});

	Ext.reg("mvregionobj", RG.ObjectSelComponent);

	RG.HPosComponent = Ext.extend(Ext.BoxComponent, {
		errorOffset: {x:-80, y:40},	
		// @cfg {Number} inputWidth
		// @cfg {Number} controlWidth
		tabIndex: 0,
		// @cfg {String} tableCls - table class
		tableCls: "",
		
		initComponent: function() {
			RG.HPosComponent.superclass.initComponent.apply(this, arguments);
		},
		
		// private
		onRender: function(ct, position) {
			if(!RG.HPosComponent.template){
				RG.HPosComponent.template = new Ext.Template(
					"<div><table class='{tableCls}'>",
					"<tr><th><label for='{chrId}'>Region</label></th><th><label for='{fromId}'>Range</label></th></tr>",
					"<tr>",
					"<td><label for='{chrId}'>Chromosome:</label><select id='{chrId}' tabindex='{tab1}'></select></td>",
					"<td><label for='{fromId}'>From:</label><input type='text' id='{fromId}' tabindex='{tab3}'/></td>",
					"</tr><tr>",
					"<td><label for='{asmId}'>Assembly:</label><select id='{asmId}' tabindex='{tab2}'></select></td>",
					"<td><label for='{toId}'>To:</label><input type='text' id='{toId}', tabindex='{tab4}'/></td>",
					"</tr></table></div>"
				);
				RG.HPosComponent.template.compile();
			}
			this.template = RG.HPosComponent.template;
			var chrId = Ext.id(), asmId = Ext.id(), fromId = Ext.id(), toId = Ext.id();
			var tabIndex = this.tabIndex || 1;
			var args = {chrId:chrId, asmId:asmId, fromId:fromId, toId:toId, tableCls:this.tableCls, tab1:tabIndex, tab2: tabIndex+1, tab3: tabIndex+2, tab4:tabIndex+3};
			if(position)
				this.el = this.template.insertBefore(position, args, true);
			else
				this.el = this.template.append(ct, args, true);
			if(this.cls)
				this.el.addClass(this.cls);
			RG.HPosComponent.superclass.onRender.apply(this, arguments);
			
			this.chrSelector = Ext.get(chrId);
			this.asmbSelector = Ext.get(asmId);
			this.fromField = Ext.get(fromId);
			this.toField = Ext.get(toId);
			
			this.fromField.addListener("keyup", RG.validate, this, {mode:0, partial:true});
			this.fromField.addListener("blur", RG.validate, this, {delay:100, mode:0, partial:false, id:id});
			this.toField.addListener("keyup", RG.validate, this, {mode:0, partial:true});
			this.toField.addListener("blur", RG.validate, this, {delay:100, mode:0, partial:false, id:id});
			if(this.inputWidth){
				this.fromField.setWidth(this.inputWidth);
				this.toField.setWidth(this.inputWidth);
			}
			if(this.controlWidth){
				this.asmbSelector.setWidth(this.controlWidth);
				this.chrSelector.setWidth(this.controlWidth);
			}
		}
	});
	Ext.reg("rghposcmp", RG.HPosComponent);
	
	RG.HObjComponent = Ext.extend(Ext.BoxComponent, {
		errorOffset: {x:-80, y:40},	
		// @cfg {Number} inputWidth
		// @cfg {Number} controlWidth
		tabIndex: 0,
		objTypeCls:"rg-obj-type",
		// @cfg {String} tableCls - table class
		tableCls: "",
		
		initComponent: function() {
			RG.HObjComponent.superclass.initComponent.apply(this, arguments);
		},
		
		// private
		onRender: function(ct, position) {
			if(!RG.HObjComponent.template){
				var modeOptions = "<option value=''>is</option><option value='1'>begins with</option>";
				var objTypes = [{v:"", t:"Any"}, {v:"gene", t:"Gene"}, {v:"clone", t:"Clone"}, {v:"snp", t:"SNP"}, {v:"marker,sts", t:"Marker"}, {v:"transcript", t:"Transcript"}];
				var objTypeOptions = "";
				for(var n=0;n<objTypes.length;n++)
					objTypeOptions += "<option value='" + objTypes[n].v + "'>" + objTypes[n].t + "</option>";
				RG.HObjComponent.template = new Ext.Template(
					"<div><table class='{tableCls}'>",
					"<tr><th><label for='{chrId}'>Region</label></th><th class='{objTypeCls}'><label for='{fromTypeId}'>Feature type</label></th><th colspan='2'><label for='{fromModeId}'>Feature name</label></th></tr>",
					"<tr>",
					"<td><label for='{chrId}'>Chromosome:</label><select id='{chrId}' tabindex='{tab1}'></select></td>",
					"<td class='{objTypeCls}'><label for='{fromTypeId}'>From:</label><select id='{fromTypeId}' tabindex='{tab3}'>",
					objTypeOptions,
					"</select></td>",
					"<td><select id='{fromModeId}' tabindex='{tab4}'>",
					modeOptions,
					"</select></td>",
					"<td><input type='text' id='{fromId}' tabindex='{tab5}'/></td>",
					"</tr><tr>",
					"<td><label for='{asmId}'>Assembly:</label><select id='{asmId}' tabindex='{tab2}'></select></td>",
					"<td class='{objTypeCls}'><label for='{toTypeId}'>To:</label><select id='{toTypeId}' tabindex='{tab6}'>",
					objTypeOptions,
					"</select></td>",
					"<td><select id='{toModeId}' tabindex='{tab7}'>",
					modeOptions,
					"</select></td>",
					"<td><input type='text' id='{toId}' tabindex='{tab8}'/></td>",
					"</tr></table></div>"
				);
				RG.HObjComponent.template.compile();
			}
			this.template = RG.HObjComponent.template;
			var chrId = Ext.id(), asmId = Ext.id(), fromId = Ext.id(), toId = Ext.id();
			var fromTypeId = Ext.id(), fromModeId = Ext.id(), toTypeId = Ext.id(), toModeId = Ext.id();
			var tabIndex = this.tabIndex || 1;
			var args = {chrId:chrId, asmId:asmId, fromId:fromId, toId:toId, 
				fromTypeId:fromTypeId, fromModeId:fromModeId, toTypeId:toTypeId, toModeId:toModeId,
				objTypeCls:this.objTypeCls, tableCls: this.tableCls,
				tab1:tabIndex, tab2: tabIndex+1, tab3: tabIndex+2, tab4:tabIndex+3, tab5:tabIndex+4, tab6:tabIndex+5, tab7:tabIndex+6, tab8:tabIndex+7};
			if(position)
				this.el = this.template.insertBefore(position, args, true);
			else
				this.el = this.template.append(ct, args, true);
			if(this.cls)
				this.el.addClass(this.cls);
			RG.HObjComponent.superclass.onRender.apply(this, arguments);
			
			this.chrSelector = Ext.get(chrId);
			this.asmbSelector = Ext.get(asmId);
			this.fromField = Ext.get(fromId);
			this.toField = Ext.get(toId);
			this.fromModeSelector = Ext.get(fromModeId);
			this.toModeSelector = Ext.get(toModeId);
			this.fromTypeSelector = Ext.get(fromTypeId);
			this.toTypeSelector = Ext.get(toTypeId);
			
			this.fromField.addListener("keyup", RG.validate, this, {mode:1, partial:true});
			this.fromField.addListener("blur", RG.validate, this, {delay:100, mode:1, partial:false, id:id});
			this.toField.addListener("keyup", RG.validate, this, {mode:1, partial:true});
			this.toField.addListener("blur", RG.validate, this, {delay:100, mode:1, partial:false, id:id});
			if(this.inputWidth){
				this.fromField.setWidth(this.inputWidth);
				this.toField.setWidth(this.inputWidth);
			}
			if(this.controlWidth){
				this.asmbSelector.setWidth(this.controlWidth);
				this.chrSelector.setWidth(this.controlWidth);
			}
		}
	});
	Ext.reg("rghobjcmp", RG.HObjComponent);

	
	RG.FormatError = function(){
		var panel, bodyEl, posErrorTpl, objErrorTpl, errorSource;
		
		var handleClose = function(){
			if(panel && panel.isVisible())
				panel.hide();
		};
		
		return {
			getPanel: function() {
				if(!panel){
					panel = new Ext.Window({
						autoCreate:true,
						title:"Error",
						resizable:false,
						constrain:true,
						constrainHeader:true,
						minimizable:false,
						maximizable:false,
						stateful:false,
						modal:false,
						shim:true,
						buttonAlign:"right",
						width:280,
						height:"auto",
						autoHeight:true,
						plain:true,
						footer:true,
						closable:true,
						close: function(){
								handleClose();
							}
					});
					panel.render(document.body);
					panel.header.addClass("rg-error-header");
				}
				return panel;
			},
			getPosErrorTemplate: function(){
				if(!posErrorTpl){
					posErrorTpl = new Ext.Template(
						"<div><h5>The value <span>{value}</span> is invalid</h5><p>Specify position as a number optionally followed by a unit (case insensitive):<ul>",
						"<li>M - for Mega-basepairs (1,000,000 basepairs)</li>",
						"<li>K - for Kilo-basepairs (1,000 basepairs)</li>",
						"</ul>or leave the position empty</p>",
						"<p>Examples:<ul><li>10.321M</li><li>10,321K</li><li>10,321,000</li></ul></p></div>"
					);
					posErrorTpl.compile();
				}
				return posErrorTpl;
			},
			getObjErrorTemplate: function(){
				if(!objErrorTpl){
					objErrorTpl = new Ext.Template(
						"<div><h5>The name <span>{value}</span> is invalid</h5>",
						"<p>A feature name can contain only alphanumeric characters, dot (.), dash(-) and start with a letter or a digit<p></div>"
					);
					objErrorTpl.compile();
				}
				return objErrorTpl;
			},
			
			showPosError: function(value, x, y){
				var p = this.getPanel();
				if(bodyEl)
					bodyEl.remove();
				var tpl = this.getPosErrorTemplate();
				bodyEl = Ext.get(tpl.append(p.body, {value:value}));
				bodyEl.addClass("rg-error");
				if(x || y)
					p.setPosition(x,y);
				p.setTitle("Invalid position format");
				p.show();
				return this;
			},
			
			showObjError: function(value, x,y){
				var p = this.getPanel();
				if(bodyEl)
					bodyEl.remove();
				var tpl = this.getObjErrorTemplate();
				bodyEl = Ext.get(tpl.append(p.body, {value:value}));
				bodyEl.addClass("rg-error");
				if(x || y)
					p.setPosition(x,y);
				p.setTitle("Invalid feature name");
				p.show();
				return this;
			},
			
			hideError: function(){
				if(panel)
					panel.hide();
				return this;
			},
			
			getErrorSource: function(){
				return errorSource;
			},
			setErrorSource: function(info){
				errorSource = info;
			}
		};
	}();


	RG.Region = function(config){
		config = config || {};
		Ext.apply(this, config);
		
		this.addEvents(
			"disable",
			"enable",
			// relayed events
			"beforeshow",
			"show",
			"beforhide",
			"hide",
			"beforerender",
			"render",
			"submit"
		);
		
		this.createPanel();
		
		NotificationCenter.addObserver(this, "buildInfoLoaded", RG.Utils.BuildInfoLoaded);
		NotificationCenter.addObserver(this, "buildInfoFailed", RG.Utils.BuildInfoFailed);
		if(this.buildInfo && (this.buildInfo.load || this.buildInfo.load == null))
			this.loadBuildInfo();
	}
	RG.RegionMatrix = function() {};


	Ext.extend(RG.Region, Ext.util.Observable, {
		// @cfg {String} type - "panel", "window"
		type: "panel",
		// @cfg {Number} controlWidth
		controlWidth:160,
		// @cfg {Number} inputWidth
		inputWidth:164,
		// @cfg {Number} panelWidth, for horizontal panels
		panelWidth: 252,
		// @cfg {String} activeTab: pos, hpos, obj, hobj
		activeTab: "pos",
		// @cfg {Array} tabs - tabs to include and tabs' order
		tabs: ["pos", "obj"],
		// @cfg {Number} tabWidth - tab width
		tabWidth: 180,
		// @cfg {String} title - panel title
		title: "Go to Region",
		// @cfg {Number} tabIndex - initial tab index
		tabIndex: 100,
		// @cfg {Bool} header - whether to draw header for static panel
		header: true,
		// @cfg {String} boxCls - box class
		boxCls: "rg-box",
		// @cfg {Bool} plainAssemblyList
		plainAssemblyList: false,
		// @cfg {String} cls - region panel class, default - none
		// @cfg {String} ctCls - panel's container class, default - none
		// @cfg {String} tabCls - base class for tab panel
		tabCls: "rg-tab-panel",
		tableCls: "rg-table",
		baseCls: "rg-wrap",
		// @cfg {String} panelCls - base class for panel
		// @cfg {String} buttonCls - additional class for button
		// @cfg {String} buttonCtCls - additional class for button container
		// @cfg {String | Object} buttonStyle - css style for button
		// @cfg {Object} buildInfo {taxid:value, build:build-spec, org:org-name, buildName:build-name, load:true}
		// @cfg {Mix} renderTo - if not set renders to document body
		// @cfg {String} helpFile	- context help file name (no extension)
		// @cfg {Object} style - panel/window style
		// @cfg {Array} tools

		// @cfg {String} headerSpacerColor
		headerSpacerColor: "rgb(114,162,227)",
		// @cfg {String} positionHeader, if specified shows subheader above from/to on vertical widget for pos tab
		positionHeader: "Range",

		errorOffset: {x:-80, y:40},	

		// @cfg {String} errorClass - class for eror panel
		errorCls: "rg-error",
		
		// private - request type
		rtRef: {pos:0, hpos:0, obj:1, hobj:1},
		loading: false, // private
		// enableAllChromosomes: false, // optionally allow chromosomes with no data
		
		createPanel: function(){
			this.submitId = Ext.id();
			
			var panels = this.panels = {};
			var boxCls = this.boxCls;
			if(this.tabs.indexOf("pos") >= 0){ // create position search panel
				var items = [
					{xtype: "mvregionsel", cls: boxCls, controlWidth: this.controlWidth, tabIndex:this.tabIndex},
					{xtype: "mvregionpos", cls: boxCls, label:"From", controlWidth: this.controlWidth, tabIndex:this.tabIndex+2},
					{xtype: "mvregionpos", cls: boxCls, label:"To", controlWidth: this.controlWidth, tabIndex:this.tabIndex+3}
				];
				if(this.positionHeader != null)
					items[1].boxHeader = this.positionHeader; // specify header
				panels["pos"] = new Ext.Panel({
					items: items,
				//	deferredRender: false,
					baseCls: "rg-panel rg-pos-panel",
					tabId: "pos",
					autoWidth: true,
					autoHeight:true,
					border: false,
					bodyBorder: true,
					title: "Search by Position"
				});
			}
			
			if(this.tabs.indexOf("hpos") >= 0) // create horizontal position search panel
				panels["hpos"] = new Ext.Panel({
					items:{xtype:"rghposcmp", cls:boxCls, controlWidth:this.controlWidth, inputWidth:this.inputWidth, tableCls:this.tableCls, tabIndex:this.tabIndex},
					baseCls: "rg-panel rg-hpos-panel",
					tabId: "hpos",
					autoWidth:true,
					autoHeight:true,
					border:false,
					bodyBorder:true,
					title: "Search by Position"
				});
				
			if(this.tabs.indexOf("hobj") >= 0)
				panels["hobj"] = new Ext.Panel({
					items:{xtype:"rghobjcmp", cls:boxCls, controlWidth:this.controlWidth, inputWidth:this.inputWidth, tableCls:this.tableCls, tabIndex:this.tabIndex},
					baseCls: "rg-panel rg-hobj-panel",
					tabId: "hobj",
					autoWidth:true,
					autoHeight:true,
					border:false,
					bodyBorder:true,
					title: "Search by Feature"
				});
			
			if(this.tabs.indexOf("obj") >= 0) // create object search panel
				panels["obj"] = new Ext.Panel({
					items: [
						{xtype: "mvregionsel", cls: boxCls, controlWidth: this.controlWidth, tabIndex:this.tabIndex},
						{xtype: "mvregionobj", cls: boxCls, label: "From", controlWidth: this.controlWidth, tabIndex:this.tabIndex+2},
						{xtype: "mvregionobj", cls: boxCls, label: "To", controlWidth: this.controlWidth, tabIndex:this.tabIndex+5}
					],
				//	deferredRender: false,
					baseCls: "rg-panel rg-obj-wrap",
					tabId: "obj",
					autoWidth: true,
					autoHeight: true,
					border: false,
					bodyBorder: true,
					title: "Search by Feature"
				});
			

			var activeTab = this.tabs.indexOf(this.activeTab); // index of active tab tag
			if(activeTab < 0){
				activeTab = 0;
				this.activeTab = this.tabs[0];
			}
			
			// create item array for given tabs
			var items = [];
			for(var n=0;n<this.tabs.length;n++){
				if(panels[this.tabs[n]])
					items.push(panels[this.tabs[n]]);
				else
					throw "Region: invalid tab name '" + this.tabs[n] + "', not suported";
			}
			if(items.length == 0)
				throw "Region: no tab items specified for Region panel";

			var tabCls = this.tabCls || "x-tab-panel";

			if(this.type == "window"){
				this.regionMsgId = Ext.id();
				var tools = [];
				if(this.helpFile)
					tools.push({id:"help", handler: this.help, scope:this});
				var headerSpacerStyle = "solid 2px " + this.headerSpacerColor;
			
				var tabPanel = this.tabPanel = new Ext.TabPanel({
					baseCls: tabCls,
					items: items,
					activeTab: activeTab,
					tabWidth:this.tabWidth || 180,
					autoWidth: true,
				//	defaults:{autoHeight: true}, // required for IE rendering 
					plain:true,
					animCollapse:true,
					deferredRender: false,
					layoutOnTabChange: true,
					listeners: {tabchange: {fn: this.ontabchange, scope: this}},
					buttons:[{xtype:"button", text:"Go", type: "Submit", id: this.submitId, cls:this.buttonCls, ctCls:this.buttonCtCls, style:this.buttonStyle, tabIndex:this.tabIndex+9}]
				});
				this.regionPanel = new Ext.Window({
					items:[
						{xtype: "box", autoEl: "div", id: this.regionMsgId, cls: "rg-header", style: {height:"auto", margin:"0 0 8px", "border-bottom":headerSpacerStyle}},
						tabPanel
					],
			//		deferredRender: false,
					title: this.title,
					width:this.panelWidth,
					shadow: false,
					shim: false, // does not work in FF on OSX
					draggable: true,
					resizable:false,
					constrain:true,
					bodyStyle: {padding:"4px 0", background:"white"}, // padding: 4px
					cls: this.cls,
					ctCls: this.ctCls,
					baseCls: this.baseCls || "x-window",
					tools: tools,
					style: this.style,
					hideMode: "visibility",
					closable: true,
					closeAction:"hide"
				});
			}else{
				var tabPanel = this.tabPanel = new Ext.TabPanel({
					baseCls: tabCls,
					items: items,
					activeTab: activeTab,
					tabWidth:this.tabWidth || 180,
					autoWidth: true,
				//	defaults:{autoHeight: true}, // required for IE rendering 
					plain:true,
					animCollapse:true,
					deferredRender: false,
					layoutOnTabChange: true,
					listeners: {tabchange: {fn: this.ontabchange, scope: this}},
					buttons:[{xtype:"button", text:"Go", type: "Submit", id: this.submitId, cls:this.buttonCls, ctCls:this.buttonCtCls, style:this.buttonStyle, tabIndex:this.tabIndex+9}]
				});
			
				var tools = this.tools;
				if(this.helpFile){
					if(!tools)
						tools = [];
					tools.push({id:"help", handler: this.help, scope:this});
				}
				this.regionPanel = new Ext.Panel({
					items:[tabPanel],
					width:this.panelWidth,
					autoWidth:false,
					plain:true,
					deferredRender:false,
					header:this.header,
					bodyStyle: {padding:"4px 0 0", background:"white"}, // padding: 4px
					baseCls: this.baseCls || "x-panel",
					title: this.title,
					tools:tools
				});
			}
			
			this.regionPanel.on("render", this._setup, this, {single: true, delay: 20});
			this.regionPanel.on("beforerender", this._presetup, this, {single: true, delay: 10});
			
			this.relayEvents(this.regionPanel, ["beforeshow", "show", "beforehide", "hide"]);
			
			this.headerTemplate = new Ext.Template("<h4>{org}<span>{build}</span>{error}</h4>");
			this.headerTemplate.compile();
		},
		
		_presetup: function(panel) {
			this.fireEvent("beforerender", this);
		},
		
		_setup: function(panel) {
			if(window.console && window.debug)
				window.console.log("+ setup");

			this.ref = {};
			if(this.panels["pos"]){
				this.ref.pos = {
					type: 0,
					chrm: this.panels["pos"].items.items[0].chrSelector,
					asmb: this.panels["pos"].items.items[0].asmbSelector,
					from: this.panels["pos"].items.items[1].text,
					to: this.panels["pos"].items.items[2].text
				};
			}else if(this.panels["hpos"]){
				this.ref.hpos = {
					type: 0,
					chrm: this.panels["hpos"].items.items[0].chrSelector,
					asmb: this.panels["hpos"].items.items[0].asmbSelector,
					from: this.panels["hpos"].items.items[0].fromField,
					to: this.panels["hpos"].items.items[0].toField
				};
			}
			
			if(this.panels["obj"]){
				this.ref.obj = {
					type: 1,
					chrm: this.panels["obj"].items.items[0].chrSelector,
					asmb: this.panels["obj"].items.items[0].asmbSelector,
					from: this.panels["obj"].items.items[1].nameField,
					to: this.panels["obj"].items.items[2].nameField,
					fromMode: this.panels["obj"].items.items[1].modeSelector,
					toMode: this.panels["obj"].items.items[2].modeSelector,
					fromObjType: this.panels["obj"].items.items[1].typeSelector,
					toObjType: this.panels["obj"].items.items[2].typeSelector
				};
			}else if(this.panels["hobj"]){
				this.ref.hobj = {
					type: 1,
					chrm: this.panels["hobj"].items.items[0].chrSelector,
					asmb: this.panels["hobj"].items.items[0].asmbSelector,
					from: this.panels["hobj"].items.items[0].fromField,
					to: this.panels["hobj"].items.items[0].toField,
					fromMode: this.panels["hobj"].items.items[0].fromModeSelector,
					toMode: this.panels["hobj"].items.items[0].toModeSelector,
					fromObjType: this.panels["hobj"].items.items[0].fromTypeSelector,
					toObjType: this.panels["hobj"].items.items[0].toTypeSelector
				};
			}
			this.submitButton = Ext.get(this.submitId);
			this.messageField = Ext.get(this.regionMsgId);
			
			delete this.panels;
			delete this.submitId;
			delete this.regionMsgId;
			
			this.rendered = true;
			this._updateLoading();

			if(this.header){
				if(this.tabCls)
					this.tabPanel.body.addClass(this.tabCls + "-body-noborder");
				this.regionPanel.addClass(this.baseCls + "-border");
			}else{
				if(this.tabCls)
					this.tabPanel.body.addClass(this.tabCls + "-body-border");
				this.regionPanel.addClass(this.baseCls + "-noborder");
			}
						
			this.submitButton.on("click", this._buttonclick, this);
			this.regionPanel.el.on("keypress", this._keypressed, this, {delay:10});
			if(this.loaded)
				this._finishLoading();
			this.fireEvent("render", this);
		},
		_keypressed: function(eventObj, node, params){
			var target = eventObj.target;
			if(eventObj.getKey() == eventObj.ENTER && target.nodeName.toLowerCase() != "button")
				this.performAction();
		},
		_buttonclick: function(eventObj, node, params){
			this.performAction();
			var e = eventObj.browserEvent;
			if(e.stopPropagation)
				e.stopPropagation();
			else 
				e.cancelBubble = true;
		},
		
		ontabchange: function(tabPanel, tab){
			var tag = tab.tabId;
			if(this.rendered && this.ref[tag]){
				this.ref[tag].chrm.focus();
				this.activeTab = tag;
			}
		},
		
		// private. Changes loading status and reflects it in the bar
		_updateLoading: function() {
			for(var t in this.ref){
				for(var n in this.ref[t]){
					if(this.ref[t][n].dom)
						this.ref[t][n].dom.disabled = this.loading? true : false;
				}
			}
			if(this.loading && this.messageField)
				this.messageField.dom.innerHTML = "Loading ...";
		
			if(this.loading)
				this.tabPanel.footer.addClass("rg-loading");
			//	this.regionPanel.getEl().mask("Loading ...", "x-mask-loading");
			else
				this.tabPanel.footer.removeClass("rg-loading");
			//	this.regionPanel.getEl().unmask();
		},
		setLoading: function(flag){
			this.loading = flag? true : false;
			if(window.console && window.debug){
				if(this.loading)
					console.log("+ start loading");
				else
					console.log("+ stop loading");
			}
			if(this.rendered)
				this._updateLoading();
			if(this.loading)
				this.fireEvent("disable", this);
			else
				this.fireEvent("enable", this);
		},
		setLoaded: function(flag){
			this.loaded = flag? true : false;
			
		},
		
		// private
		loadBuildInfo: function(){
			if(this.buildInfo && this.buildInfo.taxid && this.buildInfo.build){
				this.setLoaded(false);
				this.setLoading(true);
				this.clearBuildInfo();
				RG.Utils.getBuildInfo(this.buildInfo.taxid, this.buildInfo.build, null);
			}
		},
		
		setBuildInfo: function(buildInfo){
			this.buildInfo = buildInfo;
			if(buildInfo && (buildInfo.load || buildInfo.load == null))
				this.loadBuildInfo();
		},
		
		clearBuildInfo: function(){
			delete this.cromosomes;
			delete this.assemblies;
		},

		_setChromosomes: function(array) {
			var cmp = function(chr0, chr1){
				// not transitive !!!
				if(chr0.order > 0 && chr1.order > 0){
					if(chr0.order < chr1.order)
						return -1;
					else if(chr0.order > chr1.order)
						return 1;
					else if(chr0.chrnum && chr1.chrnum)
						return chr0.chrnum < chr1.chrnum? -1 : (chr0.chrnum > chr1.chrnum? 1 : 0);
					else
						return chr0.chr < chr1.chr? -1 : (chr0.chr > chr1.chr? 1 : 0);
				}else if(chr0.chrnum && chr1.chrnum)
					return chr0.chrnum < chr1.chrnum? -1 : (chr0.chrnum > chr1.chrnum? 1 : 0);
				else
					return chr0.chr < chr1.chr? -1 : (chr0.chr > chr1.chr? 1 : 0);
			};
			
			this.chromosomes = [];
			// Create region matrix. It references chromosomes to assemblies
			// entries are:
			// chr: ChrRef
			// ChrRef - {uid:assembly-ref} where uid - assembly-id
			// assembly-ref - {l:chr-length-on-assembly, accession:accession}
						
			this.regionMatrix = new RG.RegionMatrix();
			if(array && array.length > 0){
				for(var n=0;n<array.length;n++){
					var chr = array[n];
					if((window.allowMTChrom || !chr.is_mt) && !chr.is_un && !chr.nodata){
						this.chromosomes.push(chr.chr);
						// Add entry to the region matrix
						var obj = this.regionMatrix[chr.chr] = {maxlen: chr.l, is_mt: chr.is_mt, is_un: chr.is_un, name: chr.chr_name};
						if(chr.seq) 
							for(var k=0;k<chr.seq.length;k++) // by assembly id
								obj[chr.seq[k].uid] = {l: chr.seq[k].l, uid: chr.seq[k].uid, accession: chr.seq[k].accession};
					}
				}
				this.chromNotAvailable = false;
			}else
				this.chromNotAvailable = true;
		},
		
		_setAssemblies: function(array){
			this.asmbRef = {};
			this.assemblies = [];
			if(array && array.length > 0){
				if(this.plainAssemblyList){
					for(var n=0;n<array.length;n++){
						var obj = {name: array[n].name, uid: array[n].uid, reference: array[n].reference, complete: array[n].complete};
						this.asmbRef[obj.name] = obj;
						this.assemblies.push(obj);
					}
				}else{
					var refGroup = {group:"reference", list:[]};
					var compGroup = {group:"complete", list:[]};
					var partGroup = {group:"partial", list:[]};
					for(var n=0;n<array.length;n++){
						var obj = {name: array[n].name, uid: array[n].uid, reference: array[n].reference, complete: array[n].complete};
						this.asmbRef[obj.name] = obj;
						if(array[n].reference)
							refGroup.list.push(obj);
						else if(array[n].complete)
							compGroup.list.push(obj);
						else
							partGroup.list.push(obj);
					}
					if(refGroup.list.length > 0)
						this.assemblies.push(refGroup);
					if(compGroup.list.length > 0)
						this.assemblies.push(compGroup);
					if(partGroup.list.length > 0)
						this.assemblies.push(partGroup);
				}
			}
		},
		
		buildInfoLoaded: function(note) {
			if(this.buildInfo && note.userInfo.request.taxid == this.buildInfo.taxid && note.userInfo.request.build == this.buildInfo.build){
				this.buildInfo.orgName = Utils.valueForKeyPath.apply(note, ["userInfo.object.org.sc-name"]);
				this.buildInfo.buildName = Utils.valueForKeyPath.apply(note, ["userInfo.object.build.short-name"]);
				var array = Utils.valueForKeyPath.apply(note, ["userInfo.object.assemblies.assembly"]);
				this._setAssemblies(array);
				
				var array = Utils.valueForKeyPath.apply(note, ["userInfo.object.chromosomes.chr"]);
				this._setChromosomes(array);

				if(window.console && window.debug)
					window.console.log("+ build info loaded");
				this.finishLoading();
			}
		},
		
		_finishLoading: function(){
			if(this.rendered){
				for(var t in this.ref){
					this._loadChromosomes(this.ref[t].chrm, this.chromosomes, this.rtRef[t]);
					this._loadAssemblies(this.ref[t].asmb, this.assemblies, this.rtRef[t]);
				}
			}
			if(this.rendered && this.messageField){
				this.headerTemplate.overwrite(this.messageField, {org: this.buildInfo.orgName, build: this.buildInfo.buildName, error:""});
			}
		},
		finishLoading: function(){
			this.setLoading(false);
			this._finishLoading();
			this.setLoaded(true);
		},
		
		_loadChromosomes: function(el, array, flag){
			el.dom.innerHTML = "";
			if(flag){
				var opt = document.createElement("option");
				opt.value = "";
				opt.appendChild(document.createTextNode("- All - "));
				el.dom.appendChild(opt);
			}
			for(var n=0;n<array.length;n++){
				if(array[n]){
					var opt = document.createElement("option");
					opt.value = array[n];
					opt.appendChild(document.createTextNode(array[n]));
					el.dom.appendChild(opt);
				}
			}
		},
		_loadAssemblies: function(el, groups, flags) {
			el.dom.innerHTML = "";
			if(groups.length > 0)
				for(var n=0;n<groups.length; n++){
					if(groups[n].list && groups[n].list.length > 0){ // group
						var grp = document.createElement("optgroup");
						grp.label = groups[n].group;
						for(var j=0;j<groups[n].list.length;j++){
							var opt = groups[n].list[j];
							var optEl = document.createElement("option");
							optEl.value = opt.name;
							optEl.uid = opt.uid;
							optEl.reference = opt.reference;
							optEl.appendChild(document.createTextNode(opt.name));
							grp.appendChild(optEl);
						}
						el.dom.appendChild(grp);
					}else if(groups[n].name){ // assembly
						var opt = groups[n];
						var optEl = document.createElement("option");
						optEl.value = opt.name;
						optEl.uid = opt.uid;
						optEl.reference = opt.reference;
						optEl.appendChild(document.createTextNode(opt.name));
						el.dom.appendChild(optEl);
					}
				}
			else{
				var optEl = document.createElement("option");
				optEl.value = "";
				optEl.appendChild(document.createTextNode("-none-"));
				el.dom.appendChild(optEl);
			}
		},
		buildInfoFailed: function(note){
			this.setLoading(false);
			if(this.rendered && this.messageField){
				this.messageField.dom.innerHTML = "Loading Error";
				Ext.Msg.alert("Loading Error", "Failed to load build info for " + this.buildInfo.taxid + ": " + note.userInfo);
			}
			if(window.console)
				console.log("Failed to get build info: ", note.userInfo);
			
		},
		
		setRegionMatrix: function(matrix){
			this.regionMatrix = matrix;
			if(matrix && matrix.chrList){
				this.chromosomes = matrix.chrList;
				this.chromosomeLoaded = true;
			}
			if(matrix && matrix.asmbList){
				this._setAssemblies(matrix.asmbList);
				this.assemblyLoaded = true;
			}
			this.finishLoading();
		},
		
		// internal
		closePanel: function(){
			this.regionPanel.hide();
		},
		
		// internal
		help: function(eventObj, el, config){
			if(this.helpFile)
				RG.ContextHelp.showHelp(this.helpFile, {el: el, offY: 80});
		},
		
		// show window at position
		show: function(config){
			// config 
			// el - element to align
			// offX - offset x by
			// offY - offset y by
			if(!this.regionPanel.rendered){
				var renderTo = this.renderTo || document.body;
				this.regionPanel.render(renderTo);
			}
			config = config || {};
			var xy;
			if(config.el)
				xy = this.regionPanel.getEl().getAlignToXY(config.el, "tr-tl");
			else
				xy = this.regionPanel.getEl().getAlignToXY(this.regionPanel.container, "c-c");
			if(config.offX != null)
				xy[0] += config.offX;
			if(config.offY != null)
				xy[1] += config.offY;
			this.regionPanel.setPosition(xy[0], xy[1]);
			this.regionPanel.show();
		},
		
		// update region controls with params
		// params object must have property 'tab' equal to a valid tab
		update: function(params){
			if(params && this.rendered && this.ref[params.tab]){
				var ref = this.ref[params.tab];
				if(params.chr != null && ref.chrm)
					ref.chrm.dom.value = params.chr;
				if(params.asm != null && ref.asmb)
					ref.asmb.dom.value = params.asm;
				try{
					if(params.begin != null && ref.from){
						switch(ref.type){
							case 0: // position
								ref.from.dom.value = ncbiformatter.formatNumber(parseInt(params.begin));
								break;
							case 1: // object
								var data = ncbiformatter.parseTypedValue(params.begin);
								ref.from.dom.value = data.value;
								if(ref.fromObjType)
									ref.fromObjType.dom.value = data.type;
								break;
						}
					}
					if(params.end != null && ref.to){
						switch(ref.type){
							case 0: // position
								ref.to.dom.value = ncbiformatter.formatNumber(parseInt(params.end));
								break;
							case 1: // object
								var data = ncbiformatter.parseTypedValue(params.end);
								ref.to.dom.value = data.value;
								if(ref.toObjType)
									ref.toObjType.dom.value = data.type;
								break;
						}
					}
					if(params.m != null && ref.type == 1){
						if((params.m & 1) && ref.fromMode)
							ref.fromMode.dom.value = 1;
						if((params.m & 2) && ref.toMode)
							ref.toMode.dom.value = 1;
					}
				}catch(e){
					if(window.console)
						window.console.log("*** Error in parameters: " + e);
				}
			}
		},
		
		// returns region query or error
		regionQuery: function(){
			var query = {taxid: this.buildInfo.taxid, build: this.buildInfo.build, options:{}};
			var chr = this.ref[this.activeTab].chrm;
			if(chr.getValue())
				query.chr = chr.getValue();
			if(this.ref[this.activeTab] && this.ref[this.activeTab].asmb){
				query.asm = this.ref[this.activeTab].asmb.getValue();
				var asmb = this.asmbRef[query.asm];
				if(asmb && asmb.reference)
					query.options.referenceAssembly = true;
			}
			query.options.asmb = this.asmbRef[query.asm];
			query.options.chrm = this.regionMatrix[query.chr];

			query.rt = this.rtRef[this.activeTab];
			query.options.tab = this.activeTab;
			
			if(this.rtRef[this.activeTab] == 0)
				this.addPositionRequestToQuery(query);
			else if(this.rtRef[this.activeTab] == 1)
				this.addObjectRequestToQuery(query);
			return query;
		},
		
		// private
		performAction: function(eventObj, el, config){
			if(window.debug && window.console)
				console.log("+ perform action");
			var query = this.regionQuery();
			if(query.error){
				if(query.message)
	//				Ext.MessageBox.alert("Invalid Values", query.message + "<br/>Please correct your input and try again");
					Ext.MessageBox.show({
						title: "Invalid Input Values",
						msg: "<p>" + query.message + "<p/><p>Please correct your input and try again</p>",
						buttons: Ext.MessageBox.OK,
						icon: Ext.MessageBox.ERROR,
						cls: "rg-alert"
					});
				return true;
			}
			
			this.fireEvent("submit", this, query);
			return false;
		
		},
		
		validatePosition: function(query, loc, maxLoc){
			if(loc < 0){
				query.error = true;
				query.message = "Position value cannot be negative";
				return false;
			}
			if(maxLoc > 0 && loc > maxLoc){
				query.error = true;
				query.message = "Position must be equal or less than the chromosome length for selected assembly, which is " + ncbiformatter.formatNumber(maxLoc);
				return false;
			}
			return true;
		},
		
		addPositionRequestToQuery: function(query)
		{
			var data = ncbiformatter.parsePosition(this.ref[this.activeTab].from.getValue());
			var maxLoc = 0;
			if(query.chr && query.asm){
				var asmb = this.asmbRef[query.asm] || {};
				var ref = this.regionMatrix[query.chr]? this.regionMatrix[query.chr][asmb.uid] : {l:0};
				if(ref)
					maxLoc = ref.l;
				else
					maxLoc = this.regionMatrix[query.chr].maxlen;
			}
			
			
			if(data){
				if(data.error){
					query.message =  "Invalid 'from' position format.\nPlease correct"; // alert if message
					query.error = true;
					return;
				}
				if(!this.validatePosition(query, data.loc, maxLoc)){
					return;
				}
				query.begin = data.loc;
			}
			data = ncbiformatter.parsePosition(this.ref[this.activeTab].to.getValue());
			if(data){
				if(data.error){
					query.message = "Invalid 'to' position format.\nPlease correct";
					query.error = true;
					return;
				}
				if(!this.validatePosition(query, data.loc, maxLoc)){
					return;
				}
				query.end = data.loc;
			}
			if(!query.begin && !query.end){
				query.message = "You must specify at least one of From and To positions.";
				query.error = true;
			}
			if(query.begin != null && query.end != null && query.begin >= query.end){
				query.message = "Invalid range- the 'To' position must be greater than the 'From' position.";
				query.error = true;
			}
		},
		
		addObjectRequestToQuery: function(query)
		{
			query.m = 0;
			var data = this.ref[this.activeTab]? ncbiformatter.parseObjectName(this.ref[this.activeTab].from.getValue()) : null;
			if(data){
				if(data.error){
					query.message = "Invalid 'from' feature name format.\nPlease correct";
					query.error = true;
					return;
				}
				query.begin = data.value;
				var value = null;
				if(this.ref[this.activeTab].fromObjType && (value = this.ref[this.activeTab].fromObjType.getValue()))
					query.begin += "[" + value + "]";
				if(this.ref[this.activeTab].fromMode.getValue())
					query.m += 1;
			}
			data = this.ref[this.activeTab]? ncbiformatter.parseObjectName(this.ref[this.activeTab].to.getValue()) : null;
			if(data){
				if(data.error){
					query.message = "Invalid 'to' feature name format.\nPlease correct";
					query.error = true;
					return;
				}
				query.end = data.value;
				var value = null;
				if(this.ref[this.activeTab].toObjType && (value = this.ref[this.activeTab].toObjType.getValue()))
					query.end += "[" + value + "]";
				if(this.ref[this.activeTab].toMode.getValue())
					query.m += 2;
			}
			if(!query.begin && !query.end){
				query.message = "You must specify at least one feature.";
				query.error = true;
			}
		}
	});
	
	RG.Utils = {
		infoURL: "mvinfo.cgi",
		dataInfoURL: "mvdatainfo.cgi",
		
		BuildInfoLoaded: "Utils.BuildInfoLoaded",
		BuildInfoFailed: "Utils.BuildInfoFailed",
		dir: (window.mapviewPath? window.mapviewPath + "/mvhome" : "/projects/mapview/mvhome"),
		
		getBuildInfo: function(taxid, build, env, context)
		{
			var rq = {taxid:taxid, build:build, env:env, rq:"asmb,chr"};
			var onLoad = function(object){
				Notification.postNotification(RG.Utils.BuildInfoLoaded, this, {request:rq, object:object, context:context});
			}
			var onError = function(error, arg)
			{
				Notification.postNotification(RG.Utils.BuildInfoFailed, this, {request:rq, errorCode:error, errorInfo:arg, context:context});
			}
			Utils.sendHTTPRequest(RG.Utils.dir + "/" + RG.Utils.dataInfoURL, rq, this, onLoad, onError, RG.Model.BuildInfo);
		},
		setDirectory: function(dir)
		{
			RG.Utils.dir = dir;
		}
	};

	(function(){
		var buildInfo_seq = {
				attributes: {"assembly-id":{objectClass:Number, name:"uid"}, length:{objectClass:Number, name:"l"}, accession:String, gi:String}
			};
		var buildInfo_chr = {
				attributes: {chr:String, order:Number, chr_name:String, is_mt:Utils.Boolean, is_un:Utils.Boolean, nodata:Utils.Boolean, length:{name:"l", objectClass:Number}},
				seq: {objectClass: Utils.XMLObject, array:true, entity:buildInfo_seq}
			};
		var buildInfo_assembly = {
				attributes: {"assembly-id":{objectClass:String, name:"uid"}, complete:Utils.Boolean, description:String, name:String, reference:Utils.Boolean, "short-name":{objectClass:String, name:"shortName"}}
			};
		RG.Model = {
			BuildInfo: {
				org: {objectClass:Utils.XMLObject, entity: {attributes: {taxid:String}}},
				build: {objectClass:Utils.XMLObject, entity:{
					attributes:{available:Utils.Boolean, has_chrs:Utils.Boolean, new_schema_text:Utils.Boolean, taxid:Number, build:Number, version:Number}
				}},
				chromosomes: {objectClass:Utils.XMLObject, entity:{
					chr: {objectClass: Utils.XMLObject, array:true, entity:buildInfo_chr}
				}},
				assemblies: {objectClass:Utils.XMLObject, entity:{
					assembly: {objectClass:Utils.XMLObject, array:true, entity:buildInfo_assembly}
				}}
			}
		};
	})();
}
