/** Class for creating graphical elements for Ajax client. */
var View = {

	// Stylesheet directory
	stylesheetDir: 'style',
	// Configuration of stylesheets, defines the template name and the root element name for each templates
	stylesheet: {
	    result: {template: 'gui.xsl', xmlRoot: 'Result'},
	    field: {template: 'gui.xsl', xmlRoot: 'Field'},
	    related: {template: 'gui.xsl', xmlRoot: 'Related'},
	    preview: {template: 'gui.xsl', xmlRoot: 'Preview'}
	},
	// Defines id prefixes for special types of elements
	prefix: {
		rowNumber: 'Row_', image: 'Image', related: 'Related_', relatedForm: 'RelatedForm_', queryInput: 'QueryInput_', 
		columnSort: 'ColumnSort_', columnCondition: 'ColumnCond_', editTextarea: 'EditArea_', moreHits: 'MoreHits_', enumeration: 'Enumeration_'
	},
	// View mode: 'gui' or 'preview'
	mode: 'gui',
	// The y-coordinate of pixel where spreadsheet content begins
	spreadsheetTop: 35,
	// Minimum width of the window
	minWinWidth: 660,
	// Size of table structured MView
	tableModeSize: {row: 4, column: 3},
	// Ids of marvin applets
	marvin: {
		query: {applet: 'MSketchQuery', container: 'QueryApplet', width: 0, height: 0},
		insert: {applet: 'MSketchInsert', container: 'InsertApplet', width: 0, height: 0}
	},
	// List of the dinamically created windows
	dynamicWindows: {related: {className: 'windowForm', loaded: []}, moreHits: {className: 'windowMoreHits', loaded: []}, 
					enumeration: {className: 'windowEnum', loaded: []}},
	// Stores if RGroup visualization is needed or not
	isRGroupNeeded: false,

	// Size of the structure images
	image: {width: 0, height: 0},
	// Array for image cache that makes the new image loading smoother
	// loadedImages: [],
	// Stores if MSketch has been opened or not
	sketchOpened: [],
	// Stores if the navigation pane is minimized or not
	navigationMinimized: false,
	// Indicates if a request is in progress or not
	inProgress: false,
	// Timing variable to check the status of applet preloading
	appletPreloadTiming: null,
	// Number of the last row opened for edit
	edited: {id: null, row: null, first: null, limit: null, color: null},
	// Indicates whether switch to spreadsheet mode is allowed or not
	switchAllowed: true,
	// Original values of a structure row before editing.
	originalFieldValues: [],
	// Selected Markush structure
	selectedMarkushStructure: null,

	/**
	 * Controller of the GUI initialization.
	 */
	initController: function() {
		Logic.molecules[View.marvin.insert.applet] = {ws: ''};
		// Table or relation tree information
		Ajax.getTableInfo();
		Logic.processUrl();
		Logic.setWritableFlag(Logic.getCurrentName());
		Logic.isWritable ? $('#navInsert').css('display', 'block') : $('#navInsert').css('display', 'none');
		// Scrolling
		Scrolling.init('scrollbarResult');
		Event.scrolling();
		// Set molecule image size
		Logic.getImageSize();
		Event.resize(View.spreadsheetTop, parseInt(View.image.height)+2);
		// Default search type: substructure
		View.setSearchType('s');
		Wndw.addClass(View.dynamicWindows.moreHits.className);
		Wndw.addClass(View.dynamicWindows.enumeration.className);
		// Options depending on server side data
		if (View.isRelationalMode()) {
			Wndw.addClass(View.dynamicWindows.related.className);
			View.setSelectOptions('tSelection', Logic.getRelationTreeNames(), false, Logic.slcted);	
		} else {
			View.setSelectOptions('tSelection', Logic.tables, false, Logic.slcted);
		}
		View.setSelectOptions('similarityType', Logic.getFingerprint());
		View.setSelectOptions('screeningConfig', Logic.getScreeningConfig(), true);
		// Query pane content and options menu content
		View.setQueryOptions(Logic.getStructureTableType());
		if (Logic.getStructureTableType() == 'markush') {
			$('#rgroupDefinitions').show();
		} else {
			$('#rgroupDefinitions').hide();
		}
		if (Logic.getStructureTableType() == 'reactions') {
			$('#outputType').hide();
			$('#reactionOutputType').show();
		} else {
			$('#reactionOutputType').hide();
			$('#outputType').show();
		}
		// Export fields content
		View.getExportFieldsHtml(Logic.getFieldsForSelected(true)).draw('fieldSelection');
		// Navigation and table selection events
		Event.init();
		// Preview
		Wndw.create('preview', null, 'drag,close');
		Wndw.attachToOpener('preview', 'navMinPreview');
		Wndw.attachToOpener('preview', 'navPreview');
		// Query
		View.displayQuery('generatedQuery');
		Wndw.create('query');
		Wndw.attachToOpener('query', 'navMinQuery', function() {
			View.openSketch(View.marvin.query.applet, View.marvin.query.container);});
		Wndw.attachToOpener('query', 'navQuery', function() {
			View.openSketch(View.marvin.query.applet, View.marvin.query.container);});
		// Url container
		Wndw.create('url', 'URL generator');
		Wndw.attachToOpener('url', 'generateUrlButton');
		// Options
		Wndw.create('options');
		Wndw.applyTabs('options', {chemicalTerms: 'Chemical Terms'});
		Wndw.attachToOpener('options', 'navOptions');
		// Insert
		View.displayInsert('generatedInsert');
		Wndw.create('insert');
		Wndw.attachToOpener('insert', 'navMinInsert', function() {
			View.openSketch(View.marvin.insert.applet, View.marvin.insert.container);});
		Wndw.attachToOpener('insert', 'navInsert', function() {
			View.openSketch(View.marvin.insert.applet, View.marvin.insert.container);});
		// Export
		Wndw.create('export');
		Wndw.applyTabs('export', {exportOptions: 'Options'});
		Wndw.attachToOpener('export', 'navMinExport');
		Wndw.attachToOpener('export', 'navExport');
		// About
		Wndw.create('about', null, 'close');
		Wndw.attachToOpener('about', 'navMinAbout');
		Wndw.attachToOpener('about', 'navAbout');
		// Markush
		Wndw.create('markush');
		Wndw.applyTabs('markush', {markushGeneral: 'General Options', markushDisplay: 'Display Options'});
		// Events
		Wndw.applyEvents('#query, #url, #options, #insert, #export, #preview, #about, #markush');
		Event.query();
		Event.options();
		Event.insert();
		Event.xport();
		Event.markush();
		Event.preview();
		Event.about();
		// Set graphical elements by url options
		View.setQueryValuesFromUrl();
		if (Logic.uriParam.option != null) {
			View.setOptionValuesFromUrl();
		}
		// Get default molecules
		Logic.makeQueryData();
		Ajax.setQuery();
		// Applet preloading
		if (Cache.cfg['guiAttributes']['appletPreloading'].val == 'true') {
			View.createDummyElement('div', 'preloadContainer', 'PreloadMViewContainer');
			View.getView('', 'PreloadMView', 0, 0, 0).draw('PreloadMViewContainer');
			View.createDummyElement('div', 'preloadContainer', 'PreloadMSketchContainer');
			View.getSketch('PreloadMSketch', 0, 0, '', null).draw('PreloadMSketchContainer');
		}
	},

	/**
	 * Return if we are in relational mode or not.
	 *
	 * return boolean
	 */
	isRelationalMode: function() {
		return (Cache.cfg['mode'] != null && Cache.cfg['mode'].val == 'relational');
	},

	/**
	 * Create a dummy DOM object with a given class.
	 *
	 * @param eType    Element type (div, img, etc.).
	 * @param eClass   Element class name.
	 * @param eId      Element identifier.
	 * @return DOM object
	 */
	createDummyElement: function(eType, eClass, eId) {
		var dummyObj = document.createElement(eType);
		dummyObj.className = eClass;
		if (eId != null) {
			dummyObj.id = eId;
		}
		document.body.appendChild(dummyObj);
		return dummyObj;
	},

	/**
	 * Remove a dummy DOM object.
	 *
	 * @param dummyObj   The object to remove.
	 */
	removeDummyElement: function(dummyObj) {
		document.body.removeChild(dummyObj);
	},

	/**
	 * Set search type in the Query pane.
	 *
	 * @param searchType   New value of search type.
	 */
	setSearchType: function(searchType) {
		document.getElementById('searchType').value = searchType;
	},

	/**
	 * Return search type.
	 *
	 * @return string
	 */
	getSearchType: function() {
		return $('#searchType').val();	
	},

	/**
	 * Create options for a drop-down list.
	 *
	 * @param selectId      Id of the <select> element.
	 * @param dataArr       Array of options.
	 * @param fromValue     If true, values will be the values of the dataArr, otherwise the indices of the dataArr.
	 * @param selectedInd   Index of the default option.
	 */
	setSelectOptions: function(selectId, dataArr, fromValue, selectedInd) {
		var option;
		var select = document.getElementById(selectId);
		select.innerHTML = '';
		if (dataArr == null || dataArr.length == 0) {
			option = document.createElement('option');
			option.appendChild(document.createTextNode('None'));
			option.value = 'none';
			select.appendChild(option);
		} else {
			for (var i = 0; i < dataArr.length; i++) {
				option = document.createElement('option');
				option.appendChild(document.createTextNode(dataArr[i]));
				option.value = (fromValue == true ? dataArr[i] : i);
				if (selectedInd != null && i == selectedInd) {
					option.selected = 'selected';
				}
				select.appendChild(option);
			}
		}
	},

	/**
	 * Set the table type depentent query options.
	 *
	 * @param tableType   Type of the structure table (query, markush, molecules, reactions, or any).
	 */
	setQueryOptions: function(tableType) {
		document.getElementById('tautomersOn').disabled = (tableType == 'query' ? true : false);
		document.getElementById('tautomersOff').disabled = (tableType == 'query' ? true : false);
		document.getElementById('optionSuperstructure').disabled = ((tableType == 'markush' || tableType == 'query') ? true : false);
		document.getElementById('optionSimilarity').disabled = ((tableType == 'markush' || tableType == 'query') ? true : false);
		var searchType = View.getSearchType();
		switch (searchType) {
			// Similarity
			case 't':
				document.getElementById('similarityType').disabled = false;
				document.getElementById('screeningConfig').disabled = false;
				document.getElementById('similarityThreshold').disabled = false;
				$('div#adv1, div#adv2, div#adv3, div#adv4').hide();
				$('#noAdvOpts').show();
				break;
			// Substructure, superstructure, full, full fragment
			case 's':
			case 'r':
			case 'f':
			case 'ff':
				document.getElementById('similarityType').disabled = true;
				document.getElementById('screeningConfig').disabled = true;
				document.getElementById('similarityThreshold').disabled = true;
				if (View.isRelationalMode()) {
					$('div#adv1, div#adv2, div#adv3').hide();
					$('div#adv4').show();
				} else {
					$('div#adv1, div#adv2, div#adv3, div#adv4').show();
				}
				$('#noAdvOpts').hide();
				break;
			// Duplicate
			case 'd':
				document.getElementById('similarityType').disabled = true;
				document.getElementById('screeningConfig').disabled = true;
				document.getElementById('similarityThreshold').disabled = true;
				if (View.isRelationalMode()) {
					$('div#adv1, div#adv2, div#adv3, div#adv4').hide();
				} else {
					$('div#adv2, div#adv4').hide();
					$('div#adv1, div#adv3').show();
				}
				$('#noAdvOpts').hide();
				break;
		}
	},

	/**
	 * Make the html content of the export fields tab.
	 *
	 * @param tableColumns   Columns which should be displayed on the tab.
	 * @return string of the result html.
	 */
	getExportFieldsHtml: function(tableColumns) {
		var html = '<table>';
		var even = false;
		var excludedFields = ['cd_structure', 'cd_markush', 'cd_flags', 'cd_pre_calculated'];
		for (var i = 0; i < tableColumns.length; i++) {
			if (tableColumns[i]['group'] != Logic.columnSet.fingerprint && 
				!Util.inArray(tableColumns[i]['name'], excludedFields)) {
	
				if (!even) {
					html += '<tr>';
				}
				var xml = '<'+tableColumns[i]['name']+' />';
				html += '<td>'+View.transformTemplate('field', xml)+'</td><td><input type="checkbox" class="field" value="'+tableColumns[i]['name']+'" checked="checked" /></td>';
				if (even) {
					html += '</tr>';
				}
				even = !even;
			}
		}
		if (!even) {
			html += '<td></td></tr>';
		}
		html += '</table>';
		return html;
	},

	/**
	 * Create the html code of an MView applet.
	 *
	 * @param molecule      String of the input molecule to display.
	 * @param vId           Id of the applet, can be null.
	 * @param editable      Editable parameter of MView. The value of it is 0 (default), 1 or 2.
	 * @param vWidth        Width of the applet. If null, the class variable is used.
	 * @param vHeight       Height of the applet. If null, the class variable is used.
	 * @param showRgroups   Indicates whether to show R-groups, if possible.
	 * @return string of the result html.
	 */
	getView: function(molecule, vId, editable, vWidth, vHeight, showRgroups) {
		var html;
		if (editable == null) {
			editable = 0;
		}
		if (navigator.appName == 'Microsoft Internet Explorer') {
	    	html = '<object '+(vId == null ? '' : 'id="'+vId+'" ') +
				'codeBase="http://java.sun.com/update/1.6.0/jinstall-6u14-windows-i586.cab#Version=1,5,0,0" ' + 
	   	    	'width="'+(vWidth == null ? View.image.width : vWidth)+'" height="'+(vHeight == null ? View.image.height : vHeight)+'" ' +
				'classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93">' +
				'<param value="'+Cache.cfg['marvinPath'].val+'" name="codebase" /> ' +
				'<param value="jmarvin.jar" name="archive" /> ' +
				'<param value="chemaxon/marvin/applet/JMView" name="code" /> ' +
				'<param value="true" name="scriptable" />' +
				'<param value="#f5f5f5" name="bgcolor" />' +
				'<param value="#f5f5f5" name="viewmolbg2d" />' +
				'<param value="#f5f5f5" name="viewmolbg3d" />' +
				'<param value="'+molecule+'" name="mol" />' +
				'<param value="'+(showRgroups ? 'true' : 'false')+'" name="rgroupsvisible" /> ' +
				'<param value="true" name="detachable" /> ' +
				'<param value="'+editable+'" name="editable" /> ' +
				'<param value="true" name="legacy_lifecycle" />' +
				'<param value="-Djnlp.packEnabled=true" name="java_arguments" />' +
				'</object>';
		} else {
	    	html = '<embed '+(vId == null ? '' : 'id="'+vId+'" ')+' width="'+(vWidth == null ? View.image.width : vWidth)+'" ' +
				'height="'+(vHeight == null ? View.image.height : vHeight)+'" legacy_lifecycle="true" java_arguments="-Djnlp.packEnabled=true" ' + 
				'viewmolbg3d="#f5f5f5" viewmolbg2d="#f5f5f5" mol="'+molecule+'" rgroupsvisible="'+(showRgroups ? 'true' : 'false')+'" ' +
				'bgcolor="#f5f5f5" code="chemaxon/marvin/applet/JMView" archive="jmarvin.jar" editable="'+editable+'" detachable="true" ' + 
				'codebase="'+Cache.cfg['marvinPath'].val+'" pluginspage="http://java.sun.com/javase/downloads/index.jsp" ' +
				'type="application/x-java-applet;version=1.5"/>';
		}
		return html;
	},

	/**
	 * Create the html code of a MView applet in table mode.
	 *
	 * @param molecules   Array of the input molecules to display in table cells.
	 * @param vId         Id of the applet, can be null.
	 * @param vWidth      Width of the applet. If null, the class variable is used.
	 * @param vHeight     Height of the applet. If null, the class variable is used.
	 * @return string of the result html.
	 */
	getViewTableMode: function(molecules, vId, vWidth, vHeight) {
		var html;
		var colSize = Math.min(View.tableModeSize.row, molecules.length);
		if (vWidth == null) {
			vWidth = colSize * View.image.width;
		}
		var rowSize = Math.ceil(molecules.length / colSize);
		if (vHeight == null) {
			vHeight = rowSize * View.image.height;
		}
		var cellHTML = '';
		for (var i = 0; i < molecules.length; i++) {
			if (navigator.appName == 'Microsoft Internet Explorer') {
				cellHTML += '<param value="@javascript-URI-encoded@'+escape(molecules[i])+'" name="cell'+i+'_0" />';
			} else {
				cellHTML += 'cell'+i+'_0="@javascript-URI-encoded@'+escape(molecules[i])+'" ';
			}
		}
		if (navigator.appName == 'Microsoft Internet Explorer') {
	    	html = '<object '+(vId == null ? '' : 'id="'+vId+'" ') +
				'codeBase="http://java.sun.com/update/1.6.0/jinstall-6u14-windows-i586.cab#Version=1,5,0,0" ' + 
	   	    	'width="'+vWidth+'" height="'+vHeight+'" ' +
				'classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93">' +
				'<param value="'+Cache.cfg['marvinPath'].val+'" name="codebase" />' +
				'<param value="jmarvin.jar" name="archive" />' +
				'<param value="chemaxon/marvin/applet/JMView" name="code" />' +
				'<param value="true" name="scriptable" />' +
				'<param value="#ffffff" name="bgcolor" />' +
				'<param value="#ffffff" name="viewmolbg2d" />' +
				'<param value="#ffffff" name="viewmolbg3d" />' +
				'<param value="'+rowSize+'" name="rows" />' +
				'<param value="'+colSize+'" name="cols" />' +
				cellHTML +
				'<param value=":M:150:96" name="param" />'+
				'<param value=":1:1:M:1:1:1:1:nw:n" name="layout" />'+
				'<param value="false" name="rgroupsvisible" /> ' +
				'<param value="true" name="detachable" /> ' +
				'<param value="0" name="editable" /> ' +
				'<param value="true" name="legacy_lifecycle" />' +
				'<param value="-Djnlp.packEnabled=true" name="java_arguments" />' +
				'</object>';
		} else {
	    	html = '<embed '+(vId == null ? '' : 'id="'+vId+'" ')+' width="'+vWidth+'" '+
				'height="'+vHeight+'" legacy_lifecycle="true" java_arguments="-Djnlp.packEnabled=true" rows="'+rowSize+'" cols="'+colSize+'" '+cellHTML +
				'viewmolbg3d="#ffffff" viewmolbg2d="#ffffff" rgroupsvisible="false" param=":M:150:96" layout=":1:1:M:1:1:1:1:nw:n" ' +
				'bgcolor="#ffffff" code="chemaxon/marvin/applet/JMView" archive="jmarvin.jar" editable="0" detachable="true" ' + 
				'codebase="'+Cache.cfg['marvinPath'].val+'" pluginspage="http://java.sun.com/javase/downloads/index.jsp" ' +
				'type="application/x-java-applet;version=1.5"/>';
		}
		return html;
	},

	/**
	 * Create the html code of an MSketch applet.
	 *
	 * @param mId        Id of the applet, can be null.
	 * @param mWidth     Width of the applet. If null, the class variable is used.
	 * @param mHeight    Height of the applet. If null, the class variable is used.
	 * @param molecule   String of the input molecule to display.
	 * @param detach     Indicates whether the detached marvin window should be detached or not. 
	 *                   If null, the applet will be embedded.
	 * @return string of the result html.
	 */
	getSketch: function(mId, mWidth, mHeight, molecule, detach) {
		var html;
		if (molecule == null) {
			molecule = '';
			if (Logic.molecules[mId] != null) {
				molecule = (Logic.appletMolFormat == 'sdf' || Logic.appletMolFormat == 'rdf' || Logic.appletMolFormat == 'mol' ? 
					Logic.molecules[mId].ws.replace(/\n/g, '\\n') : Logic.molecules[mId].ws);
			}
		}
		if (navigator.appName == 'Microsoft Internet Explorer') {
	    	html = '<object '+(mId == null ? '' : 'id="'+mId+'" ') +
				'codeBase="http://java.sun.com/update/1.6.0/jinstall-6u14-windows-i586.cab#Version=1,5,0,0" ' +
				'height="'+mHeight+'" width="'+mWidth+'" ' +
				'classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93">' + 
				'<param value="'+Cache.cfg['marvinPath'].val+'" name="codebase" />' + 
				'<param value="jmarvin.jar" name="archive" />' + 
				'<param value="chemaxon/marvin/applet/JMSketch" name="code" />' +
				'<param value="true" name="scriptable" />' +
				'<param value=\''+molecule+'\' name="mol" />' +
				'<param value="\\" name="escapeChar" />' +
				'<param value="true" name="legacy_lifecycle" />' +
				'<param value="-Djnlp.packEnabled=true" name="java_arguments" />' +
				'<param value="false" name="buttonmenubar" />';
			if (detach != null) {
				html += '<param value="'+(detach == true ? 'show' : 'hide')+'" name="detach" />'+
						'<param value="false" name="undetachByX" />';
			}
			html += '</object>';
		} else {
		    html = '<embed '+(mId == null ? '' : 'id="'+mId+'" ') +
				'width="'+mWidth+'" height="'+mHeight+'" java_arguments="-Djnlp.packEnabled=true" ' +
				'mol=\''+molecule+'\' escapeChar="\\" buttonmenubar="false" code="chemaxon/marvin/applet/JMSketch" legacy_lifecycle="true" ' +
				'archive="jmarvin.jar" codebase="'+Cache.cfg['marvinPath'].val+'" mayscript="true" ' +
				'pluginspage="http://java.sun.com/javase/downloads/index.jsp" type="application/x-java-applet;version=1.5" ';
			if (detach != null) {
				html += 'detach="'+(detach == true ? 'show' : 'hide')+'" undetachbyx="false" ';
			}
			html += '/>';
		}
		return html;
	},

	/**
	 * Create the html code of the MSketch covering image.
	 *
	 * @return string of the result html.
	 */
	getSketchText: function() {
		return '<img src="images/sketch.png" title="Click here to open Marvin Sketch..." alt="Click here to open Marvin Sketch..." />';
	},

	/**
	 * Display all images sequentially from the given one using preloading.
	 *
	 * @param first       Ordinal number of the first image in the result set.
	 * @param from        Ordinal number of the first image to be displayed on the page.
	 * @param limit       Number of images to be displayed.
	 * @param timestamp   Optional. Timestamp value to avoid browser image caching.
	 */
	drawImages: function(first, from, limit, timestamp) {
		var elementId = View.prefix.image+from;
		if (document.getElementById(elementId) != null) {
			var id = parseInt(first) + parseInt(from);
			var url = escape(View.isRelationalMode() ? Cache.cfg['soapPath']['getmoldataRel'].val : Cache.cfg['soapPath']['getmoldata'].val);
			var sslPort = Cache.cfg['jspPath']['sslPort'].val;
			// Create the new image
			var newImg = new Image();
			newImg.src = Cache.cfg['jspPath']['getimage'].val+'?url='+url+(sslPort == '' ? '' : '&ssl='+sslPort)+'&searchId='+Ajax.searchId+'&coloring='+escape(Logic.colorAndAlignmentOptions)+'&width='+View.image.width+'&height='+View.image.height+'&num='+id+(View.isRGroupNeeded ? '&rg=1' : '')+(View.mode == 'gui' ? '&bg=f5f5f5' : '')+(timestamp == null ? '' : '&'+timestamp);
			newImg.width = View.image.width;
			newImg.height = View.image.height;
			newImg.alt = 'Click to edit...';
			newImg.title = 'Click to edit...';
			if (View.mode == 'gui') {
				newImg.onmousedown = function() {
					// View.switchAllowed is set to false image is clicked to allow Marvin usage in edit mode
					View.switchAllowed = false;
					if (Ajax.appletLoading == false) {
						Ajax.getApplet(id, first);
					}
				};
			}
			// When the image has been already downloaded, display it
			newImg.onload = function() {
				var imgElement = document.getElementById(elementId);
				if (imgElement != null) {
					imgElement.innerHTML = '';	
					imgElement.appendChild(newImg);
				}
				newImg = null;
				// Request the next image only if the previous one is ready (relational mode)
				if (View.isRelationalMode() && limit > 1) {
					View.drawImages(first, from + 1, limit - 1, timestamp);
				}
			};
		}
		// Request more images in parallel (table mode)
		if (!View.isRelationalMode() && limit > 1) {
			View.drawImages(first, from + 1, limit - 1, timestamp);
		}
	},

	/**
	 * Display a molecule image given by its row number.
	 *
	 * @param rowNumber   Ordinal number of the row.
	 */
	drawImageToRow: function(rowNumber) {
		var d = new Date();
		var timestamp = d.getTime();
		View.drawImages(Ajax.molecules.first, rowNumber, 1, timestamp);
	},

	/**
	 * Display a molecule image given by its number.
	 *
	 * @param tableName   Name of the related table.
	 * @param molNumber   Ordinal number of the row.
	 */
	drawImageToForm: function(tableName, molNumber) {
		var d = new Date();
		var timestamp = d.getTime();
		var elementId = View.prefix.relatedForm+tableName+View.prefix.image+molNumber;
		if (document.getElementById(elementId) != null) {
			var url = escape(View.isRelationalMode() ? Cache.cfg['soapPath']['getmoldataRel'].val : Cache.cfg['soapPath']['getmoldata'].val);
			var sslPort = Cache.cfg['jspPath']['sslPort'].val;
			// Create the new image
			var newImg = new Image();
			newImg.src = Cache.cfg['jspPath']['getimage'].val+'?url='+url+(sslPort == '' ? '' : '&ssl='+sslPort)+'&searchId='+Ajax.searchId+'&coloring='+escape(Logic.colorAndAlignmentOptions)+'&width='+View.image.width+'&height='+View.image.height+'&num='+molNumber+(View.isRGroupNeeded ? '&rg=1' : '')+(timestamp == null ? '' : '&'+timestamp);
			newImg.width = View.image.width;
			newImg.height = View.image.height;
			newImg.alt = molNumber + 1;
			newImg.title = molNumber + 1;
			// When the image has been already downloaded, display it
			newImg.onload = function() {
				var imgElement = document.getElementById(elementId);
				if (imgElement != null) {
					imgElement.innerHTML = '';	
					imgElement.appendChild(newImg);
				}
			};
		}
	},

	/**
	 * Display the ordinal number of the row in a given element.
	 *
	 * @param first   Ordinal number of the first row in the result set.
	 * @param from    Zero-based index of the row on the page.
	 * $return result html string.
	 */
	getRowNumber: function(first, number) {
		return '<p>'+(number+parseInt(first)+1)+'</p>';
	},

	/**
	 * Get the valid operations for a column defined by its name.
	 *
	 * @param columnName   Name of the field.
	 * @return array of operation strings.
	 */
	getColumnOperations: function(columnName) {
		if (columnName.toLowerCase() == 'cd_formula') {
			return ['=', '<>', '<', '>', 'between', 'starts with', 'ends with', 'contains'];
		}
		var typeNum = Logic.getColumnType(columnName);
		switch (typeNum) {
			case 'BLOB':
			case 'BYTEA':
			case 'TINYBLOB':
			case 'MEDIUMBLOB':
			case 'LONGBLOB':
			case 'BINARY':
			case 'LONGBINARY':
			case 'IMAGE':
				return [];
			case 'INTEGER':
			case 'NUMBER(10,0)':
			case 'TIMESTAMP':
			case 'BOOLEAN':
				return ['=', '<>', '<', '<=', '>', '>=', 'between'];
			case 'DOUBLE':
			case 'FLOAT':
			case 'CHAR':
				return ['<', '>', 'between'];
			case 'DATE':
			case 'DATETIME':
				return ['is', 'before', 'after'];
			case 'VARCHAR':
			case 'TEXT':
			case 'TINYTEXT':
			case 'MEDIUMTEXT':
			case 'LONGTEXT':
			default:
				return ['=', '<>', '<', '<=', '>', '>=', 'between', 'starts with', 'ends with', 'contains'];
		}
	},

	/**
	 * Return the expression of a predefined Chemical Terms filter.
	 *
	 * @param filter   Label of the predefined filter.
	 * @return string of the Chemical Terms expression.
	 */
	getChemicalTermsExp: function(filter) {
		switch (filter) {
			case 'lipinski5':
				return '(mass() <= 500) &&\n(logP() <= 5) &&\n(donorCount() <= 5) &&\n(acceptorCount() <= 10);';
			case 'veber':
			 	return '(rotatableBondCount() <= 10) &&\n(PSA() <= 140);';
			case 'leadlike':
				return '(mass() <= 450) &&\n(logD("7.4") >= -4) &&\n(logD("7.4") <= 4) &&\n(ringCount() <= 4) &&\n(rotatableBondCount() <= 10) &&\n(donorCount() <= 5) &&\n(acceptorCount() <= 8);';
			case 'bioavail':
				return '((mass() <= 500) +\n(logP() <= 5) +\n(donorCount() <= 5) +\n(acceptorCount() <= 10) +\n(rotatableBondCount() <= 10) +\n(PSA() <= 200) +\n(fusedAromaticRingCount() <= 5)) >= 6;';
			case 'ghose': 
				return '((mass() >= 160) && (mass() <= 480) &&\n(atomCount() >= 20) &&\n(atomCount() <= 70) &&\n(logP() >= -0.4) && (logP() <= 5.6) &&\n(refractivity() >= 40) && (refractivity() <= 130));';
			case 'muegge': 
				return '(mass() >= 200) && (mass() <= 600) &&\n(ringCount() <= 7) &&\n(atomCount("6") >= 5) &&\n((atomCount() - atomCount("6") -\natomCount("1")) >= 2) &&\n(rotatableBondCount() <= 15) '+
					'&&\n(donorCount() <= 5) &&\n(acceptorCount() <= 10) &&\n(logP() >= -2) && (logP() <= 5) &&\n(PSA() <= 150);';
			case 'rule3': 
				return '(mass() <= 300) &&\n(logP() <= 3) &&\n(donorCount() <= 3) &&\n(acceptorCount() <= 3) &&\n(rotatableBondCount() <= 3) &&\n(topologicalPolarSurfaceArea() <= 60);';
		}
		return '';
	},

	/**
	 * Display the query pane inside a given element.
	 *
	 * @param elementId  The parent element of the query pane. 
	 */
	displayQuery: function(elementId) {
		var columns = Logic.getColumnsArray();
		var html = '';
		if (columns.length > 0) {
			html += '<table class="queryTable">';
			html += '<tr><th>Structure</th><th colspan="3" style="padding-left: 5px;">Fields</th></tr>';
			html += '<tr><td rowspan="'+columns.length+'"><div id="'+View.marvin.query.container+'" class="marvinDetach">'+View.getSketchText()+'</div></td>';
			html += '<td style="vertical-align: top;"><div class="queryFields"><table>';
			for (var i = 0; i < columns.length; i++) {
				var xml = '<'+columns[i]+' />';
				html += '<tr><td><p>'+View.transformTemplate('field', xml)+'</p></td>';
				html += '<td style="width: 380px;"><select class="querySelect" id="'+View.prefix.columnCondition+i+'">';
				html += '<option value="none">Ignored</option>';
				var colOps = View.getColumnOperations(columns[i]);
				for (var j = 0; j < colOps.length; j++) {
					var opValue = XML.convertSpecChars(colOps[j]);
					html += '<option value="'+opValue.replace(/ /g, '_')+'">'+opValue+'</option>';
				}
				html +=	'</select><div id="'+View.prefix.queryInput+i+'" class="queryInput">&nbsp;</div></td></tr>';
			}
			html += '</table></div></td></tr></table>';
		}
		html.draw(elementId);
	},

	/**
	 * Display the query pane inside a given element.
	 *
	 * @param elementId  The parent element of the query pane. 
	 */
	displayInsert: function(elementId) {
		var html = '';
		var columns = Logic.getCustomFields();
		html += '<table class="queryTable">';
		html += '<tr><th>Structure</th><th colspan="2" style="padding-left: 5px;">Custom fields</th></tr>';
		html += '<tr><td rowspan="'+columns.length+'"><div id="'+View.marvin.insert.container+'" class="marvinDetach">'+View.getSketchText()+'</div></td>';
		html += '<td style="vertical-align: top;"><div class="insertFields"><table>';
		if (columns.length < 1) {
			html += '<tr><td colspan="2"><p>None</p></td>';
		} else {
			for (var i = 0; i < columns.length; i++) {
				var xml = '<'+columns[i]+' />';
				html += '<tr><td><p>'+View.transformTemplate('field', xml)+'</p></td>';
				html += '<td style="width: 380px;"><input class="insertInput" id="'+columns[i]+'" type="text" value="" /></td></tr>';
			}
		}
		html += '</table></div></td></tr></table>';
		html.draw(elementId);
	},

	/**
	 * Display the structures and related data inside a given element.
	 *
	 * @param elementId  The parent element of the result content. 
	 */
	displayResult: function(elementId) {
		if (View.mode == 'gui') {
			View.transformTemplate('result', Cache.searchFieldsXml+Cache.rowsXml).draw(elementId);
		} else if (View.mode == 'preview') {
			var prefix = '';
			if (navigator.userAgent.match(/Safari/) != null) {
				prefix = '<Safari />';
			}
			View.transformTemplate('preview', prefix+Cache.searchFieldsXml+Cache.rowsXml).draw(elementId);
		}
		// Convert formula values to subscript texts
		$('#'+elementId+' .formula').each(function() {
			$(this).html($(this).html().replace(/([0-9]+)/g, '<sub>$1</sub>'));
		});
		// Display sorting indicator images
		$('.sort').each(function(i) {
			var columnName = $(this).attr('title');
			columnName = columnName.replace(/cd_formula$/, 'cd_sortable_formula');
			columnName = columnName.replace(/CD_FORMULA$/, 'CD_SORTABLE_FORMULA');
			if (columnName == Logic.sorting.column) {
				$(this).html($(this).html()+View.getSortingImage(columnName, Logic.sorting.asc));
			}
		});
		// Generate Ids for row number indicator and structure images
		$('#'+elementId+' .rowNumber').each(function(i) {
			$(this).attr('id', View.prefix.rowNumber+i);
		});
		$('#'+elementId+' .image').each(function(i) {
			$(this).attr('id', View.prefix.image+i);
		});
		if (View.mode == 'gui') {
			$('#'+elementId+' .related').each(function(i) {
				$(this).html(View.getRelatedDataLink(View.prefix.related+$(this).attr('title'), $(this).html()));
			});
		}
		// Generate images for more hits, edit and remove buttons
		var searchType = View.getSearchType();
		var tableType = Logic.getStructureTableType();
		if (Logic.getQuery() != '' && tableType != 'markush' && (searchType == 's' || searchType == 'r' || searchType == 'f' || searchType == 'ff')) {
			$('#'+elementId+' .moreHits').each(function(i) {
				$(this).html(View.getMoreHitsButton(i));
			});
		}
		if (tableType == 'markush') {
			$('#'+elementId+' .enumerate').each(function(i) {
				$(this).html(View.getEnumerateButton(i));
			});
		}
		var idField = 'cd_id';
		var error = false;
		if (Logic.isWritable) {
			$('#'+elementId+' .edit').each(function(i) {
				var id = Logic.getRowField(i, idField);
				if (id == '') {
					error = true;
				} else {
					$(this).html(View.getEditButton(id, i));
				}
			});
			$('#'+elementId+' .remove').each(function(i) {
				var id = Logic.getRowField(i, idField);
				if (id == '') {
					error = true;
				} else {
					$(this).html(View.getRemoveButton(id, i));
				}
			});
		}
		if (error) {
			alert("Some buttons haven't been created correctly because field '"+idField+"' was not found in the result xml. Changing the spreadsheet settings in config.xml may solve the problem.");
		}
	},

	/**
	 * Display related data form.
	 *
	 * @param tableName   Name of the related table.
	 * @param rowNum      Ordinal number of the molecule on the page.
	 * @param openerObj   DOM object which opens the form.
	 */
	displayRelatedData: function(tableName, rowNum, openerObj) {
		if (tableName == null) {
			return;
		}
		var molNum = Ajax.molecules.first+rowNum;
		var windowId = View.prefix.relatedForm+tableName+molNum;
		if (View.dynamicWindows.related.loaded[tableName] == null) {
			View.dynamicWindows.related.loaded[tableName] = [];
		}
		if (View.dynamicWindows.related.loaded[tableName][molNum] == null) {
			var row = '<RelatedTableName>'+tableName+'</RelatedTableName>'+Result.getRowWithRelatedData(Cache.rowsXml, tableName, rowNum);
			var newDiv = View.createDummyElement('div', View.dynamicWindows.related.className, windowId);
			newDiv.innerHTML = View.transformTemplate('related', row);
			document.getElementById('container').appendChild(newDiv);
			// Convert formula values to subscript texts
			$('#'+windowId+' .formula').each(function() {
				$(this).html($(this).html().replace(/([0-9]+)/g, '<sub>$1</sub>'));
			});
			$('#'+windowId+' .image').attr('id', View.prefix.relatedForm+tableName+View.prefix.image+molNum);
			Wndw.create(windowId, 'Related data in <em>'+tableName+'</em>');
			Wndw.applyEvents('#'+windowId);
			View.dynamicWindows.related.loaded[tableName][molNum] = Ajax.molecules.first;
		}
		View.drawImageToForm(tableName, molNum);
		Wndw.open(windowId, openerObj);
	},

	/**
	 * Display more similar hits form.
	 *
	 * @param rowNum      Ordinal number of the molecule on the page.
	 * @param openerObj   DOM object which opens the form.
	 */
	displayMoreHits: function(rowNum, openerObj) {
		var molNum = Ajax.molecules.first+rowNum;
		var windowId = View.prefix.moreHits+molNum;
		if (View.dynamicWindows.moreHits.loaded[molNum] == null) {
			var molecule = Ajax.getMolecule(rowNum, 'mrv');
			var moreHits = Ajax.getMoreHits(molecule);
			if (moreHits != null) {
				var newDiv = View.createDummyElement('div', View.dynamicWindows.moreHits.className, windowId);
				document.getElementById('container').appendChild(newDiv);
				var colSize = Math.min(View.tableModeSize.row, moreHits.length);
				var vWidth = colSize * View.image.width + 18;
				var rowSize = Math.min(View.tableModeSize.column, Math.ceil(moreHits.length / colSize));
				var vHeight = rowSize * View.image.height + Wndw.bottomDistance;
				Wndw.create(windowId, 'More hits of #'+(parseInt(molNum)+1));
				Wndw.resize(windowId, vWidth, vHeight);
				Wndw.applyEvents('#'+windowId);
				Wndw.open(windowId, openerObj, function() {
					var marvinId = 'MView'+windowId;
					document.getElementById(Wndw.prefix.content+windowId).innerHTML = View.getViewTableMode(moreHits, marvinId);	
					if (document.getElementById(marvinId) != null) {
						View.dynamicWindows.moreHits.loaded[molNum] = Ajax.molecules.first;
					}
				});
			}
		} else {
			Wndw.open(windowId, openerObj);
		}
	},

	/**
	 * Display enumeration form.
	 *
	 * @param rowNum      Ordinal number of the molecule on the page.
	 * @param openerObj   DOM object which opens the form.
	 */
	displayEnumeration: function(rowNum, openerObj) {
		var molNum = Ajax.molecules.first+rowNum;
		var windowId = View.prefix.enumeration+molNum;
		if (View.dynamicWindows.enumeration.loaded[molNum] == null) {
			var molecule = Ajax.getMolecule(rowNum, 'mrv');
			var enumeration = Ajax.markushEnumerate(molecule);
			if (enumeration != null) {
				var newDiv = View.createDummyElement('div', View.dynamicWindows.enumeration.className, windowId);
				document.getElementById('container').appendChild(newDiv);	
				var colSize = Math.min(View.tableModeSize.row, enumeration.length);
				var vWidth = colSize * View.image.width + 18;
				var rowSize = Math.min(View.tableModeSize.column, Math.ceil(enumeration.length / colSize));
				var vHeight = rowSize * View.image.height + Wndw.bottomDistance;
				Wndw.create(windowId, 'Enumeration of #'+(parseInt(molNum)+1));
				Wndw.resize(windowId, vWidth, vHeight);
				Wndw.applyEvents('#'+windowId);	
				Wndw.open(windowId, openerObj, function() {		
					var marvinId = 'MView'+windowId;
					document.getElementById(Wndw.prefix.content+windowId).innerHTML = View.getViewTableMode(enumeration, marvinId);
					if (document.getElementById(marvinId) != null) {
						View.dynamicWindows.enumeration.loaded[molNum] = Ajax.molecules.first;
					}
				});
			}
		} else {
			Wndw.open(windowId, openerObj);
		}
	},

	/**
	 * Get html for operands of a column condition.
	 *
	 * @param querySelectId   Id of the column condition operator drop-down list.
	 * @param valueArr        Operand values given in an array.
	 * @return string of the result html.
	 */
	getInputContent: function(querySelectId, valueArr) {
		var html;
		var selectValue = document.getElementById(View.prefix.columnCondition+querySelectId).value;
		switch (selectValue) {
			case 'none':
				html = '&nbsp;';
				break;
			case 'between':
				html = '<input type="text" class="queryArg" id="query'+querySelectId+'_from" value="'+(valueArr != null && valueArr[0] != null ? valueArr[0] : "")+'" /><p style="float: left; margin-right: 6px;">AND</p><input type="text" class="queryArg" id="query'+querySelectId+'_to" value="'+(valueArr != null && valueArr[1] != null ? valueArr[1] : "")+'" />';
				break;
			case 'is':
			case 'before':
			case 'after':
				html = '<input type="text" class="queryArg" id="query'+querySelectId+'" value="'+(valueArr != null && valueArr[0] != null ? valueArr[0] : "")+'" /><img src="images/calendar.png" alt="Calendar" title="Calendar" onclick="DatePicker.show(\'query'+querySelectId+'\');" />';
				break;
			default:
				html = '<input type="text" class="queryArg" id="query'+querySelectId+'" value="'+(valueArr != null && valueArr[0] != null ? valueArr[0] : "")+'" />';
		}
		return html;
	},

	/**
	 * Return html of the more hits button.
	 *
	 * @param rowNumber    Zero-based number of the row in the result set of the current spreadsheet page.
	 * @return string of the html.
	 */
	getMoreHitsButton: function(rowNumber) {
		return '<img class="moreHitsImg" src="images/table.png" title="More similar hits" alt="More similar hits" '+
				'style="margin-top: 5px;" onclick="View.displayMoreHits('+rowNumber+', this);" />';
	},

	/**
	 * Return html of the markush enumeration button.
	 *
	 * @param rowNumber    Zero-based number of the row in the result set of the current spreadsheet page.
	 * @return string of the html.
	 */
	getEnumerateButton: function(rowNumber) {
		return '<img class="enumerateImg" src="images/table.png" title="Enumerate" alt="Enumerate" '+
				'style="margin-top: 5px;" onclick="View.selectedMarkushStructure = '+rowNumber+'; Wndw.open(\'markush\', this);" />';
	},

	/**
	 * Return html of the molecule edit button.
	 *
	 * @param moleculeId   CdId of the molecule to which the button will be attached.
	 * @param rowNumber    Zero-based number of the row in the result set of the current spreadsheet page.
	 * @return string of the html.
	 */
	getEditButton: function(moleculeId, rowNumber) {
		return '<img class="editImg" src="images/edit.png" title="Edit" alt="Edit" '+
				'style="margin-top: 5px;" onclick="View.switchToEditMode('+moleculeId+', '+rowNumber+');" />';
	},

	/**
	 * Return html of the molecule remove button.
	 *
	 * @param moleculeId   CdId of the molecule to which the button will be attached.
	 * @return string of the html.
	 */
	getRemoveButton: function(moleculeId) {
		return '<img class="removeImg" src="images/remove.png" title="Remove" alt="Remove" '+
				'style="margin-top: 5px;" onclick="View.removeMolecule('+moleculeId+');" />';
	},

	/**
	 * Return html of sorting image.
	 *
	 * @param columnName  Name of the column.
	 * @param asc  True, if the sorting is ascending, and false, if descending.
	 * @return string of the html.
	 */
	getSortingImage: function(columnName, asc) {
		var source;
		var imageTitle;
		if (asc == true) {
			source = 'images/sort_asc.png';
			imageTitle = 'Ascending sort';
		} else {
			source = 'images/sort_desc.png';
			imageTitle = 'Descending sort';
		}
		return '<img class="sortingImg" style="margin-top: 1px;" width="16" height="12" src="'+source+'" title="'+imageTitle+'" alt="'+imageTitle+'" />';
	},

	/**
	 * Return html of an icon that indicates existing related data for a row.
	 *
	 * @param relatedId   Id of the icon, will be clickable.
	 * @param txt         Text before the icon that will be also clickable.
	 * @return html string.
	 */
	getRelatedDataLink: function(relatedId, txt) {
		var columnNameAndRowNum = relatedId.split('_');
		var rowNum = columnNameAndRowNum[columnNameAndRowNum.length-1];
		var columnName = relatedId.substr(View.prefix.related.length, relatedId.length-View.prefix.related.length-rowNum.length-1);
		return '<span title="Related data..." onclick="View.displayRelatedData(Logic.getTableForRelatedColumn(\''+columnName+'\'), '+rowNum+', this);">... ('+txt+
			'<img class="relatedImg" id="'+relatedId+'" src="images/view_related.png" title="Related data..." alt="Related data..." />)</span>';
	},

	/**
	 * Display a confirm message, and remove the given structure if the user wants.
	 *
	 * @param moleculeId   CdId of the molecule to remove.
	 */
	removeMolecule: function(moleculeId) {
		if (confirm('Do you really want to remove the molecule? (ID: '+moleculeId+')')) {
			Ajax.removeMolecule(moleculeId);
		}
	},

	/**
	 * Display changes when a result row is switched to edit mode.
	 *
	 * @param moleculeId   CdId of the molecule to which the button will be attached.
	 * @param rowNumber    Zero-based number of the row which is being edited.
	 */
	switchToEditMode: function(moleculeId, rowNumber) {	
		var columns = Logic.getColumnsArray();
		var customFields = Logic.getCustomFields();
		var simPositions = Logic.getSimilarityPositions();
		View.drawImageToRow(rowNumber);
		if (View.edited.row != null) {
			$('#spreadsheetResult .row').each(function(i) {
				if (i == View.edited.row) {
					$(this).css('background-color', View.edited.color);
					var absPos = 0;
					var simFound = 0;
					$(this).find('.column').each(function(j, val) {
						if (Logic.searchType == 't' && j < absPos + simFound + simPositions[absPos]) {
							return;
						}
						simFound += simPositions[absPos];
						for (var k = 0; k < customFields.length; k++) {
							if (columns[absPos] == customFields[k]) {
								val.innerHTML = XML.convertSpecChars(Logic.getRowField(i, columns[absPos]));
								absPos++;
								return;
							}
						}
						absPos++;
					});
				}
			});
		}
		$('#spreadsheetResult .row').each(function(i) {
			if (i == rowNumber) {
				var bgColor = $(this).css('background-color');
				$(this).css('background-color', '#dddddd');
				var absPos = 0;
				var simFound = 0;
				$(this).find('.column').each(function(j, val) {
					if (Logic.searchType == 't' && j < absPos + simFound + simPositions[absPos]) {
						return;
					}
					simFound += simPositions[absPos];
					for (var k = 0; k < customFields.length; k++) {
						if (columns[absPos] == customFields[k]) {
							// View.switchAllowed is set to false when textarea is clicked in edit mode
							if (document.getElementById(View.prefix.editTextarea+columns[absPos]) == null) {
								var fieldValue = XML.convertSpecChars(Logic.getRowField(i, columns[absPos]));
								val.innerHTML = '<textarea id="'+View.prefix.editTextarea+columns[absPos]+'" class="editInput" onmousedown="View.switchAllowed = false;">'+fieldValue+'</textarea>';
								View.originalFieldValues[columns[absPos]] = fieldValue;
							}
							absPos++;
							return;
						}
					}
					absPos++;
				});
				View.edited = {id: moleculeId, row: rowNumber, first: Ajax.molecules.first, limit: Ajax.molecules.limit, color: bgColor};
			}	
		});
	},

	/**
	 * Display changes when a result row is switched to normal mode.
	 *
	 * @param moleculeId   CdId of the molecule to which the button will be attached.
	 * @param rowNumber    Zero-based number of the row which is being switched back to normal mode.
	 */
	switchToSpreadsheetMode: function(moleculeId, rowNumber) {
		if (Ajax.molecules.first == View.edited.first && Ajax.molecules.limit == View.edited.limit) {
			if (confirm('Save changes of the molecule? (ID: '+moleculeId+')')) {
				Ajax.editMolecule(moleculeId);
			}
			var columns = Logic.getColumnsArray();
			var customFields = Logic.getCustomFields();
			var simPositions = Logic.getSimilarityPositions();
			$('#spreadsheetResult .row').each(function(i) {
				if (i == View.edited.row) {
					$(this).css('background-color', View.edited.color);
					var absPos = 0;
					var simFound = 0;
					$(this).find('.column').each(function(j, val) {
						if (Logic.searchType == 't' && j < absPos + simFound + simPositions[absPos]) {
							return;
						}
						simFound += simPositions[absPos];
						for (var k = 0; k < customFields.length; k++) {
							if (columns[absPos] == customFields[k]) {
								val.innerHTML = XML.convertSpecChars(Logic.getRowField(i, columns[absPos]));
								absPos++;
								return;
							}
						}
						absPos++;
					});
				}
			});
			View.drawImageToRow(rowNumber);
		}
		View.edited = {id: null, row: null, first: null, limit: null, color: null};
	},

	/**
	 * Perform the animations of the Navigation pane.
	 *
	 * @param navigationWidth   Width of the Navigation pane when maximized.
	 */
	animateNavigation: function(navigationWidth) {
		var resultWidth = document.getElementById('result').clientWidth;
		if (View.navigationMinimized == false) {
			$('#navigationContent a.navText, #navigationTitle, #numOfResults, #logo').hide();
			$('#navigation').animate({width: Cache.cfg['guiAttributes']['navigationWidth'].val}, 300);
			$('#result').animate({left: parseInt(Cache.cfg['guiAttributes']['navigationWidth'].val)+1, 
				width: resultWidth+navigationWidth-Cache.cfg['guiAttributes']['navigationWidth'].val}, 300, function() {
					// To elimitate IE6 overflow bug
					$('#result').css('overflow', 'auto');
					$('#result').css('overflow-y', 'hidden');
			});
			View.navigationMinimized = true;
		} else {
			$('#result').animate({left: parseInt(navigationWidth)+1, width: resultWidth-navigationWidth+parseInt(Cache.cfg['guiAttributes']['navigationWidth'].val)}, 300);
			$('#navigation').animate({width: navigationWidth}, 300, function() {
				$('#navigationContent a.navText, #navigationTitle, #numOfResults, #logo').show();
				// To elimitate IE6 overflow bug
				$('#result').css('overflow', 'auto');
				$('#result').css('overflow-y', 'hidden');
			});
			View.navigationMinimized = false;
		}
	},

	/**
	 * Set the Preview from, Preview to, and Number of hits values.
	 */
	setPreviewNumbers: function() {
		$('.number').each(function() {
			$(this).html(Scrolling.noElements);
		});
		$('#previewInputFrom').val(Scrolling.elementId+1);
		$('#previewInputTo').val(Scrolling.elementId+Scrolling.elemsPerPage);
	},

	/**
	 * Set JChem, VM and OS versions in About pane.
	 *
	 * @param jchemData   JChem version.
	 * @param vmData      VM version.
	 * @param osInfo      Operating system version.
	 */
	setInfo: function(jchemData, vmData, osData) {
		document.getElementById('jchemInfo').innerHTML = jchemData;
		document.getElementById('vmInfo').innerHTML = vmData;
		document.getElementById('osInfo').innerHTML = osData;
	},

	/**
	 * Perform graphical changes when a request starts.
	 */
	setProgressWait: function() {
		$('#loading').show();
		View.inProgress = true;
		if (document.getElementById('tSelection') != null) {
			document.getElementById('tSelection').disabled = true;
		}
		if (document.getElementById('alignment') != null) {
			document.getElementById('alignment').disabled = true;
		}
		if (document.getElementById('submitButton') != null) {
			document.getElementById('submitButton').disabled = true;
		}
		if (document.getElementById('retrieveAllButton') != null) {
			document.getElementById('retrieveAllButton').disabled = true;
		}
		if (document.getElementById('exportButton') != null) {
			document.getElementById('exportButton').disabled = true;
		}
	},

	/**
	 * Perform graphical changes when a request ends.
	 */
	setProgress: function() {
		if (SOAP.requestNum > 0 || View.appletPreloadTiming != null) {
			View.setProgressWait();
		} else {
			if (document.getElementById('tSelection') != null) {
				document.getElementById('tSelection').disabled = false;
			}
			if (document.getElementById('alignment') != null) {
				document.getElementById('alignment').disabled = false;
			}
			if (document.getElementById('submitButton') != null) {
				document.getElementById('submitButton').disabled = false;
			}
			if (document.getElementById('retrieveAllButton') != null) {
				document.getElementById('retrieveAllButton').disabled = false;
			}
			if (document.getElementById('exportButton') != null) {
				document.getElementById('exportButton').disabled = false;
			}
			View.inProgress = false;
			$('#loading').hide();
		}
	},

	/**
	 * Set the value of the url generator.
	 */
	setUrl: function() {
		var searchType = View.getSearchType();
		var url = window.location.protocol+'//'+window.location.host+window.location.pathname+'?t='+Logic.slcted;
		if (searchType != 's') {
			url += '&s='+searchType;
		}
		if (Logic.molecules[View.marvin.query.applet] != null && Logic.molecules[View.marvin.query.applet].url != '') {
			url += '&q='+escape(Logic.molecules[View.marvin.query.applet].url);
		}
		var condition = Logic.getUrlCondition();
		if (condition != null) {
			url += '&c='+escape(condition);
		}
		url += '&o='+escape(Logic.getOptions(searchType == 't'))+'\n';
		$('#urlContainer').val(url);
	},

	/**
	 * Load MSketch applet, if it is not ready yet.
	 *
	 * @param sketchId      Id of the Marvin Sketch.
	 * @param containerId   Id of the applet container element.
	 * @param molecule      Molecule string to be initialized in the applet, it can be null.
	 */
	loadSketch: function(sketchId, containerId, molecule) {
		if (document.getElementById(sketchId) == null) {
			var size = Logic.getMarvinSize(containerId);
			View.getSketch(sketchId, size.x, size.y, molecule, !View.sketchOpened[sketchId]).draw(containerId);
			View.sketchOpened[sketchId] = true;
		}
	},

	/**
	 * Display MSketch, if it has already been loaded.
	 *
	 * @param sketchId      Id of the Marvin Sketch.
	 * @param containerId   Id of the applet container element.
	 */
	openSketch: function(sketchId, containerId) {
		if (View.sketchOpened[sketchId] == true) {
			var sketch = document.getElementById(sketchId);
			if (sketch != null) {
				document.getElementById(containerId).removeChild(sketch);
			}
			var size = Logic.getMarvinSize(containerId);
			View.getSketch(sketchId, size.x, size.y, null, false).draw(containerId);
		}
	},

	/**
	 * Remove the MSketch if exists, and stores the molecule drawn inside to the memory.
	 *
	 * @param sketchId   Id of the Marvin Sketch.
	 */
	closeSketch: function(sketchId) {
		var sketch = document.getElementById(sketchId);
		if (View.sketchOpened[sketchId] == true && sketch != null) {
			Logic.storeQueryMolecule(sketchId);
			sketch.parentNode.removeChild(sketch);
		}
	},

	/**
	 * Set Query pane elements by processed url.
	 */
	setQueryValuesFromUrl: function() {
		if (Logic.uriParam.molecule != null) {
			Logic.molecules[View.marvin.query.applet] = {ws: Logic.uriParam.molecule};
			View.loadSketch(View.marvin.query.applet, View.marvin.query.container, Logic.uriParam.molecule);
		}
		if (Logic.uriParam.srch != null) {
			View.setSearchType(Logic.uriParam.srch);
			View.setQueryOptions(Logic.tableMetaData[Logic.slcted][0]);	
		}
		var columns = Logic.getColumnsArray();
		for (var i = 0; i < columns.length; i++) {
			if (Logic.uriParam.conditionDetailed[columns[i]] != null) {
				$('#'+View.prefix.columnCondition+i).val(Logic.uriParam.conditionDetailed[columns[i]].operator);
				View.getInputContent(i, Logic.uriParam.conditionDetailed[columns[i]].operand).draw(View.prefix.queryInput+i);
			}
		}
	},

	/**
	 * Set Option pane elements by processed url.
	 */
	setOptionValuesFromUrl: function() {
		var opts = Logic.uriParam.option.split(Logic.separator);
		for (var i = 0; i < opts.length; i++) {
			var option = opts[i].split(':');
			if (option.length == 2) {
				switch (option[0]) {
					case 'dissimilarityMetric':
						document.getElementById('similarityType').value = option[1];
						break;
					case 'simThreshold':
						document.getElementById('similarityThreshold').value = option[1];
						break;
					case 'maxHitCount':
						document.getElementById('maxHits').value = option[1];
						break;
					case 'maxTime':
						document.getElementById('maxTime').value = parseInt(option[1])/60000;
						break;
					case 'returnNonHits':
						if (option[1] == 'y') {
							document.getElementById('inverseResultSet').checked = true;
						}
						break;
					case 'ctFilter':
						var chemTerms = XML.convertSpecChars(option[1], false);
						document.getElementById('chemTermsArea').value = chemTerms;
						$('#chemTermsActive').css('visibility', chemTerms == '' ? 'hidden' : 'visible');
						var defaultFilters = ['lipinski5', 'veber', 'leadlike', 'bioavail', 'ghose', 'muegge', 'rule3'];
						for (ind in defaultFilters) {
							if (chemTerms == View.getChemicalTermsExp(defaultFilters[ind])) {
								document.getElementById('chemTermsFilter').value = defaultFilters[ind];
								break;
							}
						}
						break;
					default:
						var elems = document.getElementsByName(option[0]);
						for (var j = 0; j < elems.length; j++) {
							if (elems[j].value == option[1]) {
								document.getElementById(elems[j].id).checked = true;
							}
						}
						break;
				}
			}
		}
	},

	/**
 	 * Perform an XSL transformation on one of the chosen template. (see: View.stylesheet)
 	 *
 	 * @param tempateName  Name of the XSL template.
 	 * @param xmlContent  XML data that should be applied on the template.
 	 * @return html string.
 	 */
	transformTemplate: function(templateName, xmlContent) {
		Cache.add.xsl(View.stylesheet[templateName].template);
		if (View.stylesheet[templateName].xmlRoot != null) {
			xmlContent = '<'+View.stylesheet[templateName].xmlRoot+'>'+xmlContent+'</'+View.stylesheet[templateName].xmlRoot+'>';
		}
		return XML.transformXSL(XML.parse(xmlContent), Cache.XSL[View.stylesheet[templateName].template])
	}
};

/**
 * Chainable function to set inner HTML content of a DOM element.
 *
 * @param elementId
 */
String.prototype.draw = function(elementId) {
	document.getElementById(elementId).innerHTML = this;
};
