Ext.apply(Ext.util.Format, {
	/**
	 * Display a DNA or protein sequence using a fixed-width font with a black
	 * space ever 10 char. Supports annotations in HTML.
	 * 
	 * @param {String}
	 *            sequence A random sequence of DNA or amino-acids.
	 * @return {String} The converted text
	 */
	proteinSequence : function(sequence) {
		if (sequence == null)
			return '';
		sequence = sequence.toUpperCase();
		var str = '';
		var inTag = false;
		var count = -1;
		for (i = 0; i < sequence.length; i++) {
			if (sequence.charAt(i) == '<')
				inTag = true;
			if (!inTag)
				count++;
			if (sequence.charAt(i) == '>')
				inTag = false;
			if (count > 9) {
				str += ' ';
				count = 0;
			}
			if (sequence.charAt(i) != ' ') {
				str += sequence.charAt(i);
			}
		}
		return '<tt>' + str + '</tt>';
	},
	
	ncbiBLASTN : function(value) {
		if(value==null || value==0)
			return '';
		return '&nbsp;&nbsp;&nbsp;&nbsp;<a target=_blank qtip="BLASTP sequence on NCBI" href="http://www.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Put&DATABASE=nr&PROGRAM=blastn&QUERY='+value+'"><img src="images/icons/ncbi.gif"> <small>BLASTN</small></a>';
	},

	ncbiBLASTP : function(value) {
		if(value==null || value==0)
			return '';
		return '&nbsp;&nbsp;&nbsp;&nbsp;<a target=_blank qtip="BLASTP sequence on NCBI" href="http://www.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Put&DATABASE=nr&PROGRAM=blastp&QUERY='+value+'"><img src="images/icons/ncbi.gif"> <small>BLASTP</small></a>';
	},


	
	speciesFormat : function(value) {
		try{
			var record = Passport.gridSpeciesCombo.findRecord(Passport.gridSpeciesCombo.valueField, value);
			return record ? "<i>"+record.get(Passport.gridSpeciesCombo.displayField)+"</i>" : value;
		} catch(err) {
			return value;
		}
	},
        speciesFormatNoHTML : function(value) {
                try{
                        var record = Passport.gridSpeciesCombo.findRecord(Passport.gridSpeciesCombo.valueField, value);
                        return record ? record.get(Passport.gridSpeciesCombo.displayField) : value;
                } catch(err) {
                        return value;
                }
        },

	/**
	 * Displays an icon for known platforms
	 * 
	 * @param {string}
	 *            value Name of the OS
	 * @return {String} The [img] tag
	 */
	
	platformIcon : function(value) {
		switch (value) {
			case 'Windows' :
				return '<img src="images/icons/platform/windows2.png" title="Windows">';
			case 'Macintosh' :
				return '<img src="images/icons/platform/apple.png" title="Macintosh">';
			case 'X11' :
				return '<img src="images/icons/platform/linux.png" title="X11">';
			default :
				return value;
		}
	},

	/**
	 * Displays an icon for known browser
	 * 
	 * @param {string}
	 *            value Name of the brwoser
	 * @return {String} The [img] tag
	 */
	browserIcon : function(value) {
		var browser = value.substring(0,value.indexOf('_'));
		var version = value.substring(1+value.indexOf('_'));
		switch (browser) {
			case 'Chrome' :
				return '<img src="images/icons/platform/chrome.png" alt="Chrome" title="'+value+'"><small>'+version+'</small>';
			case 'Firefox' :
				return '<img src="images/icons/platform/firefox.png" alt="Firefox" title="'+value+'"><small>'+version+'</small>';
			case 'Safari' :
				return '<img src="images/icons/platform/safari.png" alt="Safari" title="'+value+'"><small>'+version+'</small>';
			case 'Opera' :
				return '<img src="images/icons/platform/opera.png" alt="Opera" title="'+value+'"><small>'+version+'</small>';
			default :
				return value;
		}
	},

	/**
	 * Displays an icon for history operation
	 * 
	 * @param {string}
	 *            value Name of the operation
	 * @return {String} The [img] tag
	 */
	operationIcon : function(value) {
		switch (value) {
			case 'create' :
				return '<img src="images/icons/database_add.png" alt="create" title="create">';
			case 'update' :
				return '<img src="images/icons/database_edit.png" alt="update" title="update">';
			case 'delete' :
				return '<img src="images/icons/database_delete.png" alt="delete" title="delete">';
			default :
				return value;
		}
	},
	
	/**
	 * Removes white spaces from the given value
	 */
	 stripSpaces : function(value) {
		return (value==null? '' : Ext.util.Format.stripTags(value).replace(/[ \t\r\n]*/g,""));
	},

	/**
	 * Displays an icon for known platforms
	 * 
	 * @param {string}
	 *            value Name of the OS
	 * @return {String} The [img] tag
	 */
	pcrResult : function(value) {
		switch (value) {
			case -1 :
			case '-1' :
				return '-';
			case 0 :
			case '0' :
				return '+/-';
			case 1 :
			case '1' :
				return '+';
			default :
				return '';
		}
	},
	vto_result : function (value) {
		threshold = 1.5;
		stdev = 0.1;
		if(value==null)
			return "";
		value = value+"";
		
		if(value.indexOf(':') !=-1) { // This is for the summary
			var tmp = "";
			var vals = value.split("<br>");
			for(i = 0; i < vals.length; i++) {
				var splitVals = vals[i].split(": ");

				var flag = splitVals[1].charAt(0);
				splitVals[1] = splitVals[1].substring(1);
				switch(flag) {
					case '=':flag='';break;
					case '>':flag='&ge;';break;
					case '<':flag='&le;';break;
					case '#':flag='';splitVals[1]=1000000;break;
					default: splitVals[1]=flag+splitVals[1];flag="";break;
				}			
			
				
				
				if(splitVals[1]>=threshold+stdev) {
					if(splitVals[1]==1000000) {
						splitVals[1]='&#8734;';
					}
					tmp += "<span style='color:#FF9191'>"+splitVals[0]+": </span>"+"<span style='font-weight:bold;color:red'>"+(splitVals[1]<0?"":(flag+" "+splitVals[1]))+"</span>";
				} else if(splitVals[1]<threshold+stdev && splitVals[1]>=threshold-stdev)
					tmp += "<span style='color:#EDC183'>"+splitVals[0]+": </span>"+"<span style='font-weight:bold;color:orange'>"+(splitVals[1]<0?"":(flag+" "+splitVals[1]))+"</span>";
				else
					tmp += "<span style='color:#BBB'>"+splitVals[0]+": </span>"+(splitVals[1]<0?"":splitVals[1]);
					
				tmp += "<br>";
			}
			return tmp;
			
		} else if(value.indexOf(' ') !=-1) { // This is for the avg
			if(value.substring(0,7)>10000)
				return "<span style='font-weight:bold;color:red'>&#8734;</span>";
			
			if(value.substring(0,value.indexOf(' '))>=threshold+stdev)
				return "<span style='font-weight:bold;color:red'>"+value.substring(0,value.indexOf(' '))+"</span><span style='color:#FF9191'>"+value.substring(value.indexOf(' '))+"</span>";
			else if(value.substring(0,value.indexOf(' '))<threshold+stdev && value.substring(0,value.indexOf(' '))>=threshold-stdev)
				return "<span style='font-weight:bold;color:orange'>"+value.substring(0,value.indexOf(' '))+"</span><span style='color:#EDC183'>"+value.substring(value.indexOf(' '))+"</span>";
			else
				return value.substring(0,value.indexOf(' '))+ "<span style='color:#bbb'>"+value.substring(value.indexOf(' '))+"</span>";
		
		} else { // Regular case			
			var flag = value.charAt(0);
			value = value.substring(1);
			switch(flag) {
				case '=':flag='';break;
				case '>':flag='&ge;';break;
				case '<':flag='&le;';break;
				case '#':flag='';value=1000000;break;
				default: value=flag+value;flag="";break;
			}			
			if(value>=threshold+stdev) {
				if(value==1000000)
					value='&#8734;';
				return "<span style='font-weight:bold;color:red'>"+(value<0?"":(flag+" "+value))+"</span>";
			} else if(value<threshold+stdev && value>=threshold-stdev)
				return "<span style='font-weight:bold;color:orange'>"+(value<0?"":(flag+" "+value))+"</span>";
			else
				return (value<0?"":(flag+" "+value));

		}
	},

	/**
	 * Displays a icon to highligh elements that appear on the plots
	 * 
	 * @param {Boolean}
	 *            value Boolean value to convert
	 * @return {String} The converted text
	 */
	booleanPlotted : function(value) {
		return (value==null || value == 0)
				? ''
				: '<img src="images/icons/chart_line.png" alt="plotted">';
	},
	
	interpolatedData : function(value) {
		return (value==null || value == 0)
				? '<img src="images/icons/hd-check.gif" alt="experimental" qtip="This record was <b>not</b> interpolated">'
				: '';
	},
	
	/**
	 * Displays a tick or a cross instead of a 'yes' or 'no' for boolean values.
	 * 
	 * @param {Boolean}
	 *            value Boolean value to convert
	 * @return {String} The converted text
	 */
	booleanAccept : function(value) {
		return (value != 0)
				? '<img src="images/icons/tick.png" alt="yes">'
				: '';
	},

	/**
	 * Displays a tick or a cross instead of a 'yes' or 'no' for boolean values.
	 * 
	 * @param {Boolean}
	 *            value Boolean value to convert
	 * @return {String} The converted text
	 */
	booleanStarred : function(value) {
		return (value != 0)
				? '<img src="images/icons/star.png" alt="##">'
				: '';
	},

	/**
	 * Displays a tick or a cross instead of a 'yes' or 'no' for boolean values.
	 * 
	 * @param {Boolean}
	 *            value Boolean value to convert
	 * @return {String} The converted text
	 */
	booleanAccept3 : function(value) {
		if(value==null || value==0)
			return '';
		return (value == 'Y' || value=='y' || value==1)
				? '<img src="images/icons/tick.png" alt="yes">'
				: value;
	},

	/**
	 * Displays a tick or a cross instead of a 'yes' or 'no' for boolean values.
	 * 
	 * @param {Boolean}
	 *            value Boolean value to convert
	 * @return {String} The converted text
	 */
	booleanAccept2 : function(value) {
		return (value != 0)
				? '<img src="images/icons/accept.png" alt="yes">'
				: '<img src="images/icons/decline.png" alt="no">';
	},

	/**
	 * Displays appropriate icons based on user permissions.
	 * 
	 * @param {Boolean}
	 *            value Boolean value to convert
	 * @return {String} The converted text
	 */
	writable : function(value) {
		if(value == 'w')
			return '<img src="images/icons/table_edit.png" title="Write permission">';
		else if(value=='r')
			return '<img src="images/icons/table_view.png" title="Read-only access">';
		else
			return 'error';
	},

	/**
	 * Replaces <code>;</code> by <code><br></code>
	 * 
	 * @param {String}
	 *            value Value with the PO numbers separated by a ';'
	 * @return {String} The converted text
	 */
	formatPONumber : function(value) {
		return value ? value.replaceAll(';', '<br>') : '';
	},

	/**
	 * Handles nicely empty strings.
	 * 
	 * @param {String}
	 *            value Value to display
	 * @return {String} The converted text
	 */
	empty : function(value) {
		return (value != null && value.length > 0) ? value : '&nbsp;';
	},

	/**
	 * Appends the given unit to the number. Uses 4 decimals.
	 * 
	 * @param {Double}
	 *            value Number to format in the unit
	 * @param {string}
	 *            unit Unit of the value to format
	 * @return {String} The formated text
	 */
	formatWithUnit : function(value, unit) {
		return value ? Ext.util.Format.number(value, '0,000.00') + " <small>"
				+ unit + "</small>" : '';
	},

	formatCelsius : function(value) {
		return value ? Ext.util.Format.number(value, '0,000')
				+ " <small>&deg;C</small>" : '';
	},

	formatNM : function(value) {
		return value ? Ext.util.Format.number(value, '0,000')
				+ " <small>nm</small>" : '';
	},

	formatMinutes : function(value) {
		return value ? Ext.util.Format.number(value, '0,000')
				+ "'" : '';
	},

	formatMW : function(value) {
		return value ? Ext.util.Format.number(value, '0,000.00')
				+ " <small>g/mol</small>" : '';
	},

	formatMW_kDa : function(value) {
		return value ? Ext.util.Format.number(value / 1000, '0,000.00')
				+ " <small>kDa</small>" : '';
	},
	
	formatMW_kDa_noscale : function(value) {
		return value ? Ext.util.Format.number(value, '0,000.00')
				+ " <small>kDa</small>" : '';
	},

	formatMolarity : function(value) {
		return value ? Ext.util.Format.number(value, '0,000.00')
				+ " <small>&micro;M</small>" : '';
	},

	formatUG : function(value) {
		return value ? Ext.util.Format.number(value, '0,000')
				+ " <small>&micro;g</small>" : '';
	},

        formatUGNoHTML : function(value) {
                return value ? Ext.util.Format.number(value, '0,000')
                                + " &micro;g" : '';
        },

	/**
	 * Formats the length of a nucleotide sequence
	 * 
	 * @param {Integer}
	 *            value Length, in base-pairs
	 * @return {String} The formated text
	 */
	formatDNALength : function(value) {
		return value ? Ext.util.Format.number(value, '0,000')
				+ " <small>nt</small>" : '';
	},

	/**
	 * Formats the length of a polypeptide
	 * 
	 * @param {Integer}
	 *            value Length, in AA
	 * @return {String} The formated text
	 */
	formatAALength : function(value) {
		return value ? Ext.util.Format.number(value, '0,000')
				+ " <small>aa</small>" : '';
	},

	/**
	 * Generates the link for EC numbers
	 * 
	 * @param {string}
	 *            value EC number
	 * @return {String} The formated text
	 */
	formatLink_ec : function(value) {
		return value
				? "<a href='http://www.ebi.ac.uk/intenz/query?cmd=SearchEC&ec="
						+ value
						+ "' target=_blank title='Get more information on EBI'>"
						+ value + "</a>"
				: '';
	},

	/**
	 * Generates the link for EC numbers
	 * 
	 * @param {string}
	 *            value EC number
	 * @return {String} The formated text
	 */
	formatLink_taxon : function(value) {
		return value
				? "<a href='http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id="
						+ value
						+ "' target=_blank title='Get more information on NCBI Taxonomy'>"
						+ value + "</a>"
				: '';
	},

	/**
	 * Generates the link for InterPro entries
	 * 
	 * @param {string}
	 *            value InterPro ID
	 * @return {String} The formated text
	 */
	formatLink_interpro : function(value) {
		return value ? "<a href='http://www.ebi.ac.uk/interpro/ISearch?query="
				+ value
				+ "' target=_blank title='Get more information on InterPro'>"
				+ value + "</a>" : '';
	},

	/**
	 * Generates the link for PubMed literature
	 * 
	 * @param {string}
	 *            value PubMed ID
	 * @return {String} The formated text
	 */
	formatLink_pubmed : function(value) {
		return value
				? "<a href='http://www.ncbi.nlm.nih.gov/pubmed/"
						+ value
						+ "' target=_blank title='Get more information on InterPro'>PMID "
						+ value + "</a>"
				: '';
	},

	/**
	 * Generates the link for PubMed literature
	 * 
	 * @param {string}
	 *            value PubMed ID
	 * @return {String} The formated text
	 */
	formatEmail : function(value, name) {
		return value ? "<a href='mailto:" + value + "' target=_blank>"
				+ (name ? name : value) + "</a>" : '';
	},

	/**
	 * Formats the activity of a well in a plate using color codes
	 * 
	 * @param {Integer}
	 *            value Activity of the well
	 * @return {String} The image
	 */
	formatActivityWell : function(value) {
		switch (value) {
			case 0 :
				return '<img src="images/icons/bullet_red_16.png" title="'
						+ value + '">';
			case 1 :
				return '<img src="images/icons/bullet_yellow_16.png" title="'
						+ value + '">';
			case 2 :
				return '<img src="images/icons/bullet_blue_16.png" title="'
						+ value + '">';
			case 3 :
				return '<img src="images/icons/bullet_green_16.png" title="'
						+ value + '">';
			default :
				return '<img src="images/icons/control_blank.png" title="'
						+ value + '">';
		}
	},

	/**
	 * Insert the given value in a fieldset
	 * 
	 * @param {string}
	 *            value Anything
	 * @param {string}
	 *            title Title of the fieldset
	 * @param {string}
	 *            iconClass Icon class if needed
	 * @return {String} The fieldset if the value exists, an empty string
	 *         otherwise
	 */
	formatFieldSet : function(value, title, iconClass) {
		if (value == null || value.length < 2)
			return '';
		return '<fieldset><legend '
				+ (iconClass == null || iconClass.length < 1
						? ''
						: 'class="icon-detailPanel ' + iconClass + '"') + '>'
				+ title + '</legend>' + value + '</fieldset><br>';

	},

	/**
	 * Formats the activity of a well in a plate using color codes
	 * 
	 * @param {Integer}
	 *            value Activity of the well
	 * @return {String} The image
	 */
	formatActivityLegend : function(value) {
		var s = "<legend class='icon-detailPanel icon-activity";
		switch (value) {
			case 0 :
				s += '-red';
				break;
			case 1 :
				s += '-yellow';
				break;
			case 2 :
				s += '-blue';
				break;
			case 3 :
				s += '-green';
				break;
			default :
				s += '';
				break;
		}
		return s + "'>Activity</legend>";
	},

	/**
	 * Formats the activity of a well in a plate using color codes
	 * 
	 * @param {Integer}
	 *            value Activity of the well
	 * @return {String} The image
	 */
	formatBooleanLegend : function(value, title) {
		var s = "<legend class='icon-detailPanel icon-boolean-";
		if (value) {
			s += 'yes';
		} else {
			s += 'no';
		}
		return s + "'>" + title + "</legend>";
	},
	
	/**
	 * This functions returns the input string padded on the left, the right, or both sides to the specified padding length. If the optional argument “pad” is not supplied, the input is padded with spaces, otherwise it is padded with characters from “pad” up to the “len” length.
	 * From: http://www.webtoolkit.info/javascript-pad.html
	 *
	 * @param {string} value
	 * @param {int} len
	 * @param {char} pad
	 * @param {int} dir
	 * @return {String} The padded string
	 */
	pad : function(value, len, pad) { 
		return padLeft(value,len,pad);
	},

	/**
	 * @deprecated OBSOLETE: use CustomFormater#formatVirtualPlate
	 *
	 * Displays the picture of the given plate. IMage is in a hidden div that
	 * can be displayed by click on link defined in
	 * <code>formatPCRPlate96</code>. If image can't be loaded, onError
	 * destroys the link so that no broken picture is displayed.
	 * 
	 * @param {String}
	 *            v Position in the plate (A1 to H12). Other positions are
	 *            ignored
	 * @param {String}
	 *            attribute Name of the plate. Should be the same value used
	 *            when calling formatPCRPlate96()
	 * @return {String} The representation of the PCR plate as a table
	 */
	formatPCRPlate96Image : function(v, attribute) {
		return '<div id="div-pcrplate96-img-'
				+ attribute
				+ '-'
				+ v
				+ '"'
				+ ' onclick="document.getElementById(this.id).style.display=\'none\';" class="pcrplate96-img">'
				+ '<img id="pcrplate96-img-'
				+ v
				+ '" src="images/plate_screening/'
				+ v
				+ '.jpg" height="200" alt="Image not available"'
				+ ' onError="document.getElementById(\'td-\'+this.parentElement.id.substring(4)).innerHTML=\'Plate photo not available.\';" />'
				+ '<br>Click on photo to hide</div>';
	},
	
	/**
	 * Shows the selected wells in a user friendly virtual plate that overlays the actual picture of the plate.
	 * Works by pairs of plates (for the 2 dilutions)
	 *
	 * @param {string} v The selected wells
	 * @param {string} id ID of the currnet plate
	 * @param {string} id2 ID of the other plate
	 * @param {string} column name of the column in Passport.spreadsheet_store that contains the selected wells
	 * @return {string} The virtual plate
	 * 
	 * @see lib#switchSwitcher, lib#switchLight, lib#switchPlateWell
	 */
	formatVirtualPlate: function(v,id,id2,column) {
		var nbRows = 8;
		var nbCols = 12;
		var rowLabel = 'ABCDEFGH';
		
		v=v.split(/[;, ]+/);
		
		
		var pcr_plate = '<table id="pcrplate96-table-'+id+'" align="center" class="pcr_plate" style="opacity:'+Passport.VIRTUAL_PLATE_OPACITY+';filter: alpha(opacity='+100*Passport.VIRTUAL_PLATE_OPACITY+') ;">';
		
		// Serious stuf starts here
		for (var pcr_i = 1; pcr_i < nbRows+1; pcr_i++) {
			pcr_plate += '<tr id="pcrplate96-'+id+'-'+rowLabel.charAt(pcr_i - 1)+'">';
			for (pcr_j = 1; pcr_j < nbCols+1; pcr_j++) {
				var position = ''+ rowLabel.charAt(pcr_i - 1) +pcr_j;
				pcr_plate += '<td '+(v.contains(position)?'class="selected"':'')+' id="pcrplate96-'+id+'-'+position+'" title="'+ position + '" onclick="Javascript:switchPlateWell(this.id,\'pcrplate96-'+id2+'-'+position+'\',\''+position+'\',\''+column+'\');"></td>';
			}
			pcr_plate += '</tr>';
		}
		pcr_plate += '</table>';
		return pcr_plate;
	},

	/**
	 * @deprecated OBSOLETE: use CustomFormater#formatVirtualPlate
	 * 
	 * Formats the position of a well in a PCR plate with 96 wells.
	 * 
	 * @param {String}
	 *            v Position in the plate (A1 to H12). Other positions are
	 *            ignored
	 * @param {String}
	 *            attribute Name of the plate. Should be the same value used
	 *            when calling formatPCRPlate96Image()
	 * @return {String} The representation of the PCR plate as a table
	 */
	formatPCRPlate96 : function(v, attribute) {
		var store = Passport.plate_stores[attribute];
		var currentActivity = Passport.plate_stores['currentActivity'];

		// For the image

		var rowLabel = 'ABCDEFGH';
		var row = -1;
		var col = -1;
		var activityLabels = ["No activity", "Low activity", "Activity",
				"High activity", "", "", "", "", "", ""];
		if (v != null && v.length > 1) {
			row = rowLabel.indexOf(v.toUpperCase().charAt(0)) + 1;
			col = parseInt(v.substring(1));
		}

		// Virtual plate with icons
		var pcr_plate = '<table id="pcrplate96-table-' + attribute
				+ '" align="center" class="pcr_plate"><tr>';
		for (var pcr_j = 0; pcr_j < 13; pcr_j++) {
			pcr_plate += '<th ' + (pcr_j == col ? 'class="selected"' : '')
					+ '>' + (pcr_j > 0 ? '' + pcr_j : '') + '</th>';
		}
		pcr_plate += '</tr>';
		for (var pcr_i = 1; pcr_i < 9; pcr_i++) {
			pcr_plate += '<tr><th ' + (pcr_i == row ? 'class="selected"' : '')
					+ '>' + rowLabel.charAt(pcr_i - 1) + '</th>';
			for (pcr_j = 1; pcr_j < 13; pcr_j++) {
				var activity = '-';
				var descriptionWell = "[" + rowLabel.charAt(pcr_i - 1) + pcr_j
						+ "] Empty";
				if (pcr_i == row && pcr_j == col) { // Selected well
					activity = currentActivity;
					descriptionWell = "Selected well. See description for details.";
					pcr_plate += '<td '
							+ (activity > -1 && activity < 10
									? 'class="activity'
											+ activity
											+ (pcr_i == row && pcr_j == col
													? 'selected'
													: '') + '"'
									: '')
							+ ' title="'
							+ (activityLabels[activity] || (''
									+ rowLabel.charAt(pcr_i - 1) + pcr_j))
							+ '" id="clones_pcrplate_'
							+ rowLabel.charAt(pcr_i - 1)
							+ pad(pcr_j, 2, '0')
							+ '_'
							+ attribute
							+ '" onclick="Javascript:setPCRPlate96Info(this.id.substring(20), this.id.substring(16,19), this.className.substring(8,9), this.innerHTML)" ><div style="display:none;">'
							+ descriptionWell + '</div></td>';
				} else { // All wells but selected one
					if (store
							&& store
									.getById(rowLabel.charAt(pcr_i - 1) + pcr_j)) { // If a
						// store
						// is
						// properly
						// defined
						activity = store.getById(rowLabel.charAt(pcr_i - 1)
								+ pcr_j).get('activity');
						descriptionWell = store.getById(rowLabel.charAt(pcr_i
								- 1)
								+ pcr_j).get('description');
						pcr_plate += '<td '
								+ (activity > -1 && activity < 10
										? 'class="activity'
												+ activity
												+ (pcr_i == row && pcr_j == col
														? 'selected'
														: '') + '"'
										: '')
								+ ' title="'
								+ (activityLabels[activity] || (''
										+ rowLabel.charAt(pcr_i - 1) + pcr_j))
								+ '" id="clones_pcrplate_'
								+ rowLabel.charAt(pcr_i - 1)
								+ pad(pcr_j, 2, '0')
								+ '_'
								+ attribute
								+ '" onclick="Javascript:setPCRPlate96Info(this.id.substring(20), this.id.substring(16,19), this.className.substring(8,9), this.innerHTML)" ><div style="display:none;">'
								+ descriptionWell + '</div></td>';
					} else { // No store or empty well
						pcr_plate += '<td '
								+ (activity > -1 && activity < 10
										? 'class="activity' + activity + '"'
										: '')
								+ ' title="'
								+ (activityLabels[activity] || 'Empty well')
								+ '" id="pcrplate_'
								+ rowLabel.charAt(pcr_i - 1)
								+ pad(pcr_j, 2, '0')
								+ '_'
								+ attribute
								+ '" onclick="Javascript:setPCRPlate96Info(this.id.substring(13), this.id.substring(9,12), this.className.substring(8), this.innerHTML)" ondblclick="Javascript:setPCRPlate96(this.id.substring(9,12),this.id.substring(13))"><div style="display:none;">'
								+ descriptionWell + '</div></td>';
					}
				}

			}
			pcr_plate += '</tr>';
		}
		pcr_plate += '<tr><th colspan=13 class="pcrplate96-show" id="td-pcrplate96-img-'
				+ attribute
				+ '-'
				+ (store.getAt(0) ? store.getAt(0).get('plate') : '')
				+ '" ><span class="pcrplate96-show" id="pcrplate96-img-'
				+ attribute
				+ '-'
				+ (store.getAt(0) ? store.getAt(0).get('plate') : '')
				+ '" onclick="document.getElementById(\'div-\'+this.id).style.display=\'block\';">Show plate photo</span></th></tr>';
		pcr_plate += '<tr><th colspan=13><div class="pcrplate96-info" id="pcrplate96-info-'
				+ attribute
				+ '">Click on wells for more information.</div></th></tr></table>';

		return pcr_plate;
	}
});

Ext.apply(Ext.util.Format, {
			formatInteger : function(value) {
				var numberFormat = {
					decimalSeparator : '.',
					decimalPrecision : 0,
					groupingSeparator : ',',
					groupingSize : 3
				};
				var format = Ext.apply(Ext.apply({}, this.numberFormat),
						numberFormat);
				if (typeof value !== 'number') {
					value = String(value);
					if (format.currencySymbol) {
						value = value.replace(format.currencySymbol, '');
					}
					if (format.groupingSeparator) {
						value = value.replace(new RegExp(
										format.groupingSeparator, 'g'), '');
					}
					if (format.decimalSeparator !== '.') {
						value = value.replace(format.decimalSeparator, '.');
					}
					value = parseFloat(value);
				}
				var neg = value < 0;
				value = Math.abs(value).toFixed(format.decimalPrecision);
				var i = value.indexOf('.');
				if (i >= 0) {
					if (format.decimalSeparator !== '.') {
						value = value.slice(0, i) + format.decimalSeparator
								+ value.slice(i + 1);
					}
				} else {
					i = value.length;
				}
				if (format.groupingSeparator) {
					while (i > format.groupingSize) {
						i -= format.groupingSize;
						value = value.slice(0, i) + format.groupingSeparator
								+ value.slice(i);
					}
				}
				if (format.currencySymbol) {
					value = format.currencySymbol + value;
				}
				if (neg) {
					value = '-' + value;
				}
				return value;
			}
		});

/**
 * Set the position in the PCR plate in the selected record in the spreadsheet
 * 
 * @param {String}
 *            position Position in the PCR plate (A1 to H12)
 * @param {String}
 *            column Name of the attribute to update
 */
function setPCRPlate96(position, column) {
	var row = Passport.spreadsheet_panel.getSelectionModel().getSelectedCell()[0];
	Passport.spreadsheet_store.getAt(row).set(column,
			position.charAt(0) + parseInt(position.substring(1), 10));
	updateDetailPanel(row);
	Passport.spreadsheet_panel.getTopToolbar().items.get(0).enable();
	Passport.spreadsheet_panel.getTopToolbar().items.get(1).enable();
}

/**
 * Set the position in the PCR plate in the selected record in the spreadsheet
 * 
 * @param {String}
 *            position Position in the PCR plate (A1 to H12)
 * @param {String}
 *            attribute Name of the attribute to update
 * @param {int}
 *            activity Activity of the selected well
 * @param {string}
 *            description Description of the content of the selected well
 */
function setPCRPlate96Info(attribute, position, activity, description) {
	description = description.substring(description.indexOf('>') + 1,
			description.lastIndexOf('</'));
	if (description.indexOf('Empty') != -1) {
		description = 'Empty';
		activity = -1;
	}
	document.getElementById('pcrplate96-info-' + attribute).innerHTML = "<span style='border-right:1px solid #922339;padding-right:5px;margin-right:5px;height:70px;font-weight:bold;font-size:small;float:left;'>"
			+ position
			+ "<br><br>"
			+ Ext.util.Format.formatActivityWell(1 * activity)
			+ "</span>"
			+ description;
}

