var pepSequencer = null;
var pepSeqHelp = null;
var pepSeqBody = null;
var pepSeqLookUpFrame = null;
var pepSeqGeneratorResult = null;
var pepSeqProvider = 'uniprot'; // default selection of radio buttons
var pepSeqInputSequence = null;
var pepSeqInputOffset = null;
var pepSeqInputLength = null;
var pepSeqLabelLength = null;
var pepSeqCopyButton = null;
var pepSeqExport = null;
var pepSeqExportType = 'html'; // default selection of radio buttons

var pepSeqWait = null;
var pepSeqResults = null;

var pepSeqCopyCallback = null;
var pepSeqCopyCallbackOption = null;
var pepSeqCloseCallback = null;
var pepSeqCloseCallbackOption = null;

var pepSeqServerStorageCallback = null;

var pepSeqInterfaceURL = '/pepsequencer/interface.php'; // has to be absolute path
var pepSeqAjaxCounter = 0; // should be incremented on AJAX calls; will be echoed back from server to identify cancelled actions in client
var pepSeqQuery = '';
var pepSeqPagingStep = 100;
var pepSeqPagingPage = 0;
var pepSeqLastData = new Object();
var pepSeqGeneratedData = null;
var pepSeqSelectedResult = new Object();

var pepSeqLastRetrievedSequence = '';
var pepSeqLastRetrievedName = '';

var pepSeqDefaultLength = 15;
var pepSeqDefaultOffset = 4;

var pepSeqSecuritySequence = '';
var pepSeqSecurityToken = '';

/**
 * Break up sequence in smalle blocks and lines for better readability on output formatting.
 * @param sequence {String} sequence to break
 * @param blocklength {int} number of characters in one block
 * @param blocksperline {int} number of blocks in one line
 * @param blockseparator {String} how to separate blocks from each other
 * @param linebreak {String} how to break lines
 * @param prependindexes {boolean} add start indexes in front of lines
 * @param indexseparator {String} how to separate prepended indexes from content
 * @returns {String} formatted output
 */
function pepSeqBreakSequence(sequence, blocklength, blocksperline, blockseparator, linebreak, prependindexes, indexseparator) {
	var totallength = sequence.length;
	var out = '';
	var blockcounter = 0;
	var position = 1;
	var indexlength = (''+totallength).length;
	
	if (prependindexes) {
		out += pepSeqPadJustifiedRight(''+position, indexlength, ' ') + indexseparator;
	}
	
	while (sequence.length > 0) {
		if (sequence.length > blocklength) {
			out += sequence.substring(0, blocklength);
			sequence = sequence.substring(blocklength);
			position += blocklength;
			
			if (++blockcounter >= blocksperline) {
				// line break
				out += linebreak;
				blockcounter = 0;
				
				if (prependindexes) {
					out += pepSeqPadJustifiedRight(''+position, indexlength, ' ') + indexseparator;
				}
			} else {
				// separate blocks
				out += blockseparator;
			}
		} else {
			out += sequence;
			sequence = '';
		}
	}
	
	return out;
}

/**
 * Pads a string to be justified left (adds characters to the end of the string).
 * @param s  {String} input string
 * @param n  {int}    desired minimum length of the string
 * @param ch {String} character(s) to append until minimum length is met
 * @returns  {String} padded string (or unchanged if too long)
 */
function pepSeqPadJustifiedLeft(s, n, ch) {
	while (s.length<n) {
		s += ch;
	}
	return s;
}

/**
 * Pads a string to be justified right (adds characters to the beginning of the string).
 * @param s  {String} input string
 * @param n  {int}    desired minimum length of the string
 * @param ch {String} character(s) to prepend until minimum length is met
 * @returns  {String} padded string (or unchanged if too long)
 */
function pepSeqPadJustifiedRight(s, n, ch) {
	while (s.length<n) {
		s = ch + s;
	}
	return s;
}

/**
 * Cleans the given protein sequence from invalid characters.
 * @param sequence {String} dirty input
 * @returns {String} cleaned sequence
 */
function pepSeqCleanSequence(sequence) {
	return sequence.replace(/[^a-zA-Z]/g, '');
}

/**
 * Generate all subsequences. If errors occur, output object will have textual content in out.export array.
 * Last returned object is also stored in global variable pepSeqGeneratedData.
 * @param sequence {String} sequence to process; will be cleaned before processing
 * @param peptidelength {String} length of subsequences to generate (will be converted into integer)
 * @param peptideoffset {String} offset of subsequences (will be converted into integer)
 * @returns {Object} output object, always: .errors              {Array}  error messages (as String)
 *                                          .cleanedsequence     {String} sequence after cleaning
 *                                          .cleanedlength       {int}    length of sequence after cleaning
 *                                 success: .peptidelength       {int}    peptide length (subsequences)
 *                                          .peptideoffset       {int}    peptide offset
 *                                          .peptidecount        {int}    number of full length peptides
 *                                          .peptideoverlap      {int}    peptide overlap (length-offset)
 *                                          .output              {Object} formatted output
 *                                                 .html         {String} HTML output (table, double header)
 *                                                 .plaintext    {String} plain text
 *                                                 .csv          {String} CSV (with semicolon)
 *                                          .data                {Array}  all generated data (table rows)
 *                                             []                {Object} generated data is stored as objects in array
 *                                             [].startaa        {int}    sequence string offset (starting at 1)
 *                                             [].sequence       {String} subsequence
 *                                             [].sequencelength {int}    length of subsequence
 */
function pepSeqGenerate(sequence, peptidelength, peptideoffset) {
	var peptidecount = -1;
	var csvseparator = ';'; // semicolon for Excel import by double-click without import dialog
	var numericpadding = 4;
	var sequencename = '';
	var out = new Object();
	out.errors = new Array();

	// preprocess input
	peptidelength = parseInt(peptidelength);
	peptideoffset = parseInt(peptideoffset);
	sequence = pepSeqCleanSequence(sequence);
	out.cleanedsequence = sequence;
	out.cleanedlength = sequence.length;
	
	// compare to latest retrieved sequence and use name on match
	if (sequence.toLowerCase() == pepSeqLastRetrievedSequence.toLowerCase()) {
		sequencename = pepSeqLastRetrievedName;
	}

	// check input
	if (isNaN(peptidelength)) {
		out.errors.push('peptide length is not a number');
	}
	if (isNaN(peptideoffset)) {
		out.errors.push('peptide offset is not a number');
	}
	if (peptidelength<1) {
		out.errors.push('invalid peptide length');
	}
	if (peptideoffset<1) {
		out.errors.push('invalid peptide offset');
	}
	if (peptidelength<=peptideoffset) {
		out.errors.push('peptide offset has to be less than peptide length');
	}
	if (sequence.length<1) {
		out.errors.push('no sequence entered');
	}
	
	// return on error
	if (out.errors.length > 0) {
		pepSeqGeneratedData = out;
		return out;
	}
	
	// calculate missing values
	var peptideoverlap = (peptidelength-peptideoffset);
	
	// return if no peptides
	if (sequence.length < peptidelength) {
		out.errors.push('sequence is too short, no sequences can be generated yet');
		pepSeqGeneratedData = out;
		return out;
	}
	
	// start saving textual output
	var header = '';
	out.output = new Object();
	out.output.html = '';
	out.output.plaintext = '';
	out.output.csv = '';
	out.output.pdf = new Object();
	out.output.pdf.formattedsequence = pepSeqBreakSequence(sequence, 10, 6, ' ', '\n', true, '  ');
	
	// process data
	out.data = new Array();
	var i = 0;
	var underrun = false;
	var firstunderrun = true;
	do {
		// "calculate" sequence
		out.data[i] = new Object();
		out.data[i].startaa = i*peptideoffset + 1;
		out.data[i].sequence = sequence.substr(out.data[i].startaa - 1, peptidelength);
		out.data[i].sequencelength = out.data[i].sequence.length;

		// check for underrun
		underrun = (out.data[i].sequencelength < peptidelength);
		out.data[i].underrun = underrun;
		out.data[i].firstunderrun = firstunderrun;

		// output one row in plain text
		out.output.plaintext += pepSeqPadJustifiedRight(''+(i+1), numericpadding, ' ')+' '+
		                        pepSeqPadJustifiedRight(''+out.data[i].startaa, numericpadding, ' ')+' '+
								pepSeqPadJustifiedLeft(out.data[i].sequence, peptidelength, ' ')+' '+
		                        pepSeqPadJustifiedRight(''+out.data[i].sequencelength, numericpadding, ' ')+' '+
								((underrun && !firstunderrun) ? '*' : '')+'\r\n';

		// output one row in CSV
		out.output.csv += ''+(i+1)+csvseparator+out.data[i].startaa+csvseparator+'"'+out.data[i].sequence+'"'+csvseparator+out.data[i].sequencelength+csvseparator;
		if (underrun && !firstunderrun) {
			out.output.csv += '"incomplete"';
		}
		out.output.csv += '\r\n';

		// output one row in HTML
		if (underrun && !firstunderrun) {
			out.output.html += '<tr class="underrun">';
		} else {
			out.output.html += '<tr>';
		}
		out.output.html += '<td class="row">'+(i+1)+'</td>';
		out.output.html += '<td class="startaa">'+out.data[i].startaa+'</td>';
		out.output.html += '<td class="sequence">'+out.data[i].sequence+'</td>';
		out.output.html += '<td class="sequencelength">'+out.data[i].sequencelength+'</td>';
		out.output.html += '</tr>';

		// remember peptidecount on first underrun and toggle flag
		if (underrun && firstunderrun) {
			peptidecount = i+1;
			firstunderrun = false;
		}

		// increment for next iteration
		i++;
	} while (out.data[i-1].sequencelength > peptideoffset);

	// set peptide count if we don't have it already
	if (peptidecount < 0) {
		peptidecount = out.data.length;
	}
	
	// save basic data to output object
	out.peptidelength = peptidelength;
	out.peptideoffset = peptideoffset;
	out.peptidecount = peptidecount;
	out.peptideoverlap = peptideoverlap;
	out.sequencename = sequencename;

	// basic information in CSV
	header  = '"Peptide length:"'+csvseparator+peptidelength+'\r\n';
	header += '"Offset:"'+csvseparator+peptideoffset+'\r\n';
	header += '"Overlap:"'+csvseparator+peptideoverlap+'\r\n';
	header += '"Peptide count:"'+csvseparator+peptidecount+'\r\n';
	header += '"Sequence name:"'+csvseparator+sequencename+'\r\n';
	header += '"Cleaned sequence:"'+csvseparator;
	header += pepSeqBreakSequence(sequence, 10, 6, '"'+csvseparator+'"', '"\r\n'+csvseparator, true, csvseparator+'"');
	header += '"\r\n\r\n';
	header += '"Index"'+csvseparator+'"Start AA"'+csvseparator+'"Peptide Sequence"'+csvseparator+'"Peptide length"\r\n';
	out.output.csv = header + out.output.csv;

	// basic information in HTML
	header  = '<div class="parameters">';
	header += '<div class="peptidelength"><span class="label">Peptide length:</span> <span class="value">'+peptidelength+'</span></div>';
	header += '<div class="peptideoffset"><span class="label">Offset:</span> <span class="value">'+peptideoffset+'</span></div>';
	header += '<div class="peptideoverlap"><span class="label">Overlap:</span> <span class="value">'+peptideoverlap+'</span></div>';
	header += '<div class="peptidecount"><span class="label">Peptide count:</span> <span class="value">'+peptidecount+'</span></div>';
	header += '<div class="sequencename"><span class="label">Sequence name:</span> <span class="value">'+sequencename+'</span></div>';
	header += '<div class="cleanedsequence"><span class="label">Cleaned sequence:</span> <span class="length">(length: '+sequence.length+')</span> ';
	header += '<pre class="value">'+pepSeqBreakSequence(sequence, 10, 6, ' ', '<br />', true, '  ').replace(/ /,'&nbsp;')+'</pre></div>';
	header += '</div>';
	var headercols = '<th class="row">Index</th><th class="startaa">Start AA</th><th class="sequence">Peptide Sequence</th><th class="sequencelength">Peptide length</th>';
	header += '<table class="extraheader"><tr>'+headercols+'</tr></table>';
	header += '<div class="contenttable"><table>';
	header += '<tr class="normalheader">'+headercols+'</tr>';
	out.output.html = header + out.output.html;
	
	// basic information in plain text
	header  = 'Peptide length: '+peptidelength+'\n';
	header += 'Offset:         '+peptideoffset+'\n';
	header += 'Overlap:        '+peptideoverlap+'\n';
	header += 'Peptide count:  '+peptidecount+'\n';
	header += 'Sequence name:  '+sequencename+'\n';
	header += '\n';
	header += 'Cleaned input sequence: (length: '+sequence.length+')\n';
	header += pepSeqBreakSequence(sequence, 10, 6, ' ', '\n', true, '  ');
	header += '\n';
	header += '\n';
	var plaintablehead    = pepSeqPadJustifiedRight('i',  numericpadding, ' ')+' '+
							pepSeqPadJustifiedRight('aa', numericpadding, ' ')+' '+
							pepSeqPadJustifiedLeft('sequence', peptidelength, ' ')+' '+
							pepSeqPadJustifiedRight('length', numericpadding, ' ');
	header += plaintablehead + '\n';
	header += pepSeqPadJustifiedLeft('', plaintablehead.length, '-')+'\n';
	out.output.plaintext = header + out.output.plaintext;
	
	// close table in HTML output
	out.output.html += '</table>';
	
	// add footnote to plaintext output
	out.output.plaintext += '\n';
	out.output.plaintext += '* indicates an incomplete peptide sequence which has not been included in peptide count\n';

	// store globally
	pepSeqGeneratedData = out;
	
	return out;
}

/**
 * Removes invalid characters from input object and returns the integer value.
 * @param obj {jQuery} jQuery object to get and set value
 * @return {Integer} integer value of object or NaN
 */
function pepSeqCleanAndReturnInteger(obj) {
	// get string representation of value from object
	var s = obj.val();

	// clean input before conversion

	// remove any non-decimal character
	if (s.search(/[^\d]/g) != -1) {
		s = s.replace(/[^\d]/g, '');
		obj.val(s);
	}

	// remove leading zeros (would be interpreted octal then)
	// should be applied after all invalid chars have been cleared out or
	// other characters in front would lead to zeros not being removed
	if (s.search(/^[0]+/) != -1) {
		s = s.replace(/^[0]+/, '');
		obj.val(s);
	}

	// return parsed integer
	return parseInt(s);
}

/**
 * Grabs data from input fields, runs the generator and renders the result view.
 */
function pepSeqGenerateFromField() {
	// get plain input
	var sequence = pepSeqInputSequence.val();
	var offset = pepSeqCleanAndReturnInteger(pepSeqInputOffset);
	var sequencelength = pepSeqCleanAndReturnInteger(pepSeqInputLength);

	// generate sequences and update length of cleaned sequence
	var res = pepSeqGenerate(sequence, sequencelength, offset);
	pepSeqLabelLength.html(res.cleanedlength); // FIXME: does not work after close and reopen on Opera 10 Beta 3
	
	if (res == null) {
		pepSeqGeneratorResult.text('Could not process input.');
	} else if (res.errors.length > 0) {
		var html = '';
		html += '<div class="errors">';
		html += '<div class="introduction">';
		html += 'No results yet due to the following errors:';
		html += '</div>';
		html += '<ul>';
		for (var i=0; i<res.errors.length; i++) {
			html += '<li>'+res.errors[i]+'</li>';
		}
		html += '</ul>';
		html += '</div>';
		pepSeqGeneratorResult.html(html);
	} else {
		pepSeqGeneratorResult.html(res.output.html);
	}
	return false;
}

/**
 * Open Peptide Sequencer window.
 * @param config['modal'] {boolean} show window modal?
 * @param config['closecallback'] {function} function to call when dialog is closed
 * @param config['closecallbackoption'] {object} something to additionally pass onto callback function unchanged - used to pass references etc.
 * @param config['copycallback'] {function} function to call when copy button is clicked
 * @param config['copycallbackoption'] {object} something to additionally pass onto callback function unchanged - used to pass references etc.
 * @param config['copycaption'] {String} caption of copy button
 * @param config['copyenable'] {boolean} enable copy button?
 */
function pepSeqOpen(config) {
	// default settings
	if (config==undefined) config = {};
	if (config['modal']==undefined) config['modal'] = false;
	if (config['closecallback']==undefined) config['closecallback'] = null;
	if (config['closecallbackoption']==undefined) config['closecallbackoption'] = null;
	if (config['copycallback']==undefined) config['copycallback'] = null;
	if (config['copycallbackoption']==undefined) config['copycallbackoption'] = null;
	if (config['copycaption']==undefined) config['copycaption'] = 'Copy to Quote Request';
	if (config['copyenable']==undefined) config['copyenable'] = false;
	
	// we need a security token before we can query databases or export
	if (pepSeqSecurityToken == '') pepSeqAcquireSecurityToken();
	
	
	pepSeqCloseCallback = config['closecallback'];
	pepSeqCloseCallbackOption = config['closecallbackoption'];
	pepSeqCopyCallback = config['copycallback'];
	pepSeqCopyCallbackOption = config['copycallbackoption'];
	
	if (config['copyenable']) {
		pepSeqCopyButton.removeClass('buttonremoved');
		jQuery('.conditionalonlyonquoterequest', pepSeqHelp).css('display','block');
	} else {
		pepSeqCopyButton.addClass('buttonremoved');
		jQuery('.conditionalonlyonquoterequest', pepSeqHelp).css('display','none');
	}
	pepSeqCopyButton.text(config['copycaption']);

	// stop all Flash movies
	/*
	if (fsrStopAll != undefined && fsrStopAll != null) {
		fsrStopAll();
	}
	*/
   
	pepSequencer.dialog('option', 'modal', config['modal']);
	pepSequencer.dialog('open');
	return false;
}

/**
 * Internally used callback function to call a function defined on PepSeqOpen.
 */
function pepSeqClosed() {
	if (pepSeqCloseCallback!=null) {
		pepSeqCloseCallback(pepSeqGeneratedData, pepSeqCloseCallbackOption);
	}

	// resume all Flash movies
	/*
	if (fsrResumeAll != undefined && fsrResumeAll != null) {
		fsrResumeAll();
	}
	*/
}

/**
 * Wrapper function to open Peptide Sequencer modal and copy results to the next-by field of class pepseqdroptarget.
 */
function pepSeqOpenModalAndWriteResult(source) {
	/* deprecated call; define copy button and copycallback */
	pepSeqOpen({modal: true, callback: pepSeqOpenModalAndWriteResultCallback, callbackoption: source});
	return false;
}

/**
 * Callback function for pepSeqOpenModalAndWriteResult() to copy results.
 */
function pepSeqOpenModalAndWriteResultCallback(data, callbackoption) {
	var target = jQuery(callbackoption).parent().find('.pepseqdroptarget');
	target.val(data.output.plaintext);
}

/**
 * Wrapper function to open Peptide Sequencer modal and append results to the next-by field of class pepseqdroptarget.
 */
function pepSeqOpenModalAndAppendResult(source) {
	pepSeqOpen({modal: true, copyenable: true, copycallback: pepSeqOpenModalAndAppendResultCallback, copycallbackoption: source});
	return false;
}

/**
 * Callback function for pepSeqOpenModalAndAppendResult() to copy results.
 */
function pepSeqOpenModalAndAppendResultCallback(data, callbackoption) {
	var target = jQuery(callbackoption).parent().find('.pepseqdroptarget');
	var oldValue = target.val();
	var separator = '\r\n\r\n';
	if (oldValue=='') separator = '';
	if (data.output != undefined) {
		target.val(oldValue + separator + data.output.plaintext);
		pepSequencer.dialog('close');
	}
}

/**
 * Closes the modal wait dialog.
 */
function pepSeqWaitDialogClose() {
	pepSeqWait.dialog('close');
}

/**
 * Opens the modal wait dialog.
 * @param title {String} dialog title
 * @param body {String} dialog HTML body (message)
 */
function pepSeqWaitDialogOpen(title, body, cancellable) {
	if (cancellable == null) {
		cancellable = false;
	} else if (cancellable) {
		body += '<p class="buttonspace">';
		body += '<a href="#" id="pepseqwaitcancel" class="button ui-state-default ui-corner-all">Cancel</a>';
		body += '</p>';
	}

	pepSeqWait.dialog('option', 'title', title);
	pepSeqWait.html(body);

	var cancelbutton = jQuery('#pepseqwaitcancel');
	cancelbutton.hover(
		function () {
			jQuery(this).addClass('ui-state-hover');
		},
		function () {
			jQuery(this).removeClass('ui-state-hover');
		}
	);
	cancelbutton.click(function(){
		// increment counter to invalidate responses to old queries
		pepSeqAjaxCounter++;

		// close window
		pepSeqWait.dialog('close');
	});

	pepSeqWait.dialog('open');
}

/**
 * Send database query to local server interface. (sandboxed proxy)
 */
function pepSeqSendQuery() {
	var myRequestID = ++pepSeqAjaxCounter;

	// show wait dialog
	pepSeqWaitDialogOpen('Query in progress','Please stand by while database is being queried...',true);
	
	// send query to PHP interface
	jQuery.post(pepSeqInterfaceURL, {'request': 'dbquery', 'token': pepSeqSecurityToken, 'provider': pepSeqProvider, 'query': pepSeqQuery, 'limit': pepSeqPagingStep, 'offset': pepSeqPagingPage*pepSeqPagingStep}, function (data,status) {
		// don't handle cancelled requests
		if (myRequestID == pepSeqAjaxCounter) {
			pepSeqQueryResult(data, status);
		}
	}, 'json');
	
	return false;
}

/**
 * Process JSON result received from server on database query.
 * Assembles HTML code for whole result dialog.
 * @param data {Object} JSON object from server
 * @param status {String} status message from jQuery/AJAX
 */
function pepSeqQueryResult(data, status) {
	var results = data.results;
	pepSeqSelectedResult = null;

	// prepare paging HTML code
	var paging = '';
	paging += '<div class="pepseqresultspaging '+(!data.metadata.moreResultsAvailable ? 'paging-nonext' : '')+' '+(pepSeqPagingPage<1 ? 'paging-noprev' : '')+'">';
	paging += '<a href="#" class="pagingnext">next '+pepSeqPagingStep+'</a>';
	paging += '<a href="#" class="pagingprev">previous '+pepSeqPagingStep+'</a>';
	paging += '</div>';
	
	// prepare result dialog
	var html = '';
	
	html += paging;
	
	html += '<table>';
	html += '<tr>';
	html += '<th class="dbselection-selector">&nbsp;</th>';
	html += '<th class="dbselection-reviewed">&nbsp;</th>';
	html += '<th class="dbselection-entryname">Entry name</th>';
	html += '<th class="dbselection-proteinname">Protein name</th>';
	html += '</tr>';
	html += '</table>';
	
	html += '<div id="dbselection">';
	html += '<table>';
	for (var i=0; i<results.length; i++) {
		html += '<tr>';
		html += '<td class="dbselection-selector"><input name="dbselection" type="radio" value="'+i+'" /></td>';
		html += '<td class="dbselection-reviewed dbselection-reviewed-'+(results[i].reviewed ? 'true' : 'false')+'">&nbsp;</td>';
		html += '<td class="dbselection-entryname">'+results[i].entryname+'</td>';
		html += '<td class="dbselection-proteinname">'+results[i].proteinname+'</td>';
		html += '</tr>';
	}
	html += '</table>';
	html += '</div>';
	
	html += paging;
	
	html += '<div class="legend">';
	html += '<div class="legend-item">';
	html += '<div class="dbselection-reviewed dbselection-reviewed-true">&nbsp;</div>&nbsp;=&nbsp;reviewed&nbsp;(SwissProt)';
	html += '</div>';
	html += '<div class="legend-item">';
	html += '<div class="dbselection-reviewed dbselection-reviewed-false">&nbsp;</div>&nbsp;=&nbsp;unreviewed&nbsp;(TrEMBL)';
	html += '</div>';
	html += '</div>';
	
	html += '<p>';
	html += '<a href="#" id="pepseqresultscancel" class="button ui-state-default ui-corner-all">Cancel</a>';
	html += '<a href="#" id="pepseqresultsok" class="button ui-state-default ui-corner-all">Copy selected sequence</a>';
	html += '</p>';
	
	// inject to DOM
	pepSeqResults.html(html);
	
	// assign functions
	(new jQuery('td', pepSeqResults)).click(function() {
		var jthis = new jQuery(this);
		var selector = jthis;
		
		// get selector (radio button)
		if (!jthis.hasClass('dbselection-selector')) {
			selector = jthis.prevAll('td.dbselection-selector');
		}
		
		// check radio button
		(jQuery('input', selector))[0].checked = true;
		
		// set selection background
		jQuery('tr.selected', pepSeqResults).removeClass('selected');
		selector.parent().addClass('selected');
		
		// get ID
		var selectedResultID = jQuery('input', selector).val();
		
		// copy result data
		pepSeqSelectedResult = pepSeqLastData.results[selectedResultID];
	});
	jQuery('.pepseqresultspaging .pagingnext').click(function() {
		// skip if no next page available
		if (!pepSeqLastData.metadata.moreResultsAvailable) {
			return false;
		}
		
		// increment page counter
		pepSeqPagingPage++;
		
		// close window because we will overwrite it soon
		//pepSeqResults.dialog('close');
		
		// query server
		pepSeqSendQuery();
		
		return false;
	});
	jQuery('.pepseqresultspaging .pagingprev').click(function() {
		// skip if no previous page available
		if (pepSeqPagingPage<1) {
			return false;
		}
		
		// decrement page counter
		pepSeqPagingPage--;
		
		// close window because we will overwrite it soon
		//pepSeqResults.dialog('close');
		
		// query server
		pepSeqSendQuery();
		
		return false;
	});
	jQuery('#pepseqresultscancel').click(function() {
		pepSeqResults.dialog('close');
		
		return false;
	});
	jQuery('#pepseqresultsok').click(function() {
		// abort if no result has been selected
		if (pepSeqSelectedResult == null) {
			pepSeqModalMessageDialog('No result selected', 'Please select a result or abort the query by clicking cancel or closing the result dialog.');
			return false;
		}

		// remember retrieved sequence and its name
		pepSeqLastRetrievedSequence = pepSeqCleanSequence(pepSeqSelectedResult.sequence);
		pepSeqLastRetrievedName = pepSeqSelectedResult.entryname;

		// copy result data into sequence textarea
		jQuery('textarea.sequence', pepSeqBody).val(pepSeqSelectedResult.sequence);
		pepSeqGenerateFromField();

		// close the result dialog
		pepSeqResults.dialog('close');

		// slide query panel up
		jQuery('#pepseqlookuppanel').panel('toggle');

		return false;
	});
	
	// remember data
	pepSeqLastData = data;
	
	// initialize buttons for hover
	pepSeqInitButtonHover(pepSeqResults);
	
	// change modal dialogs
	pepSeqWaitDialogClose();
	pepSeqResults.dialog('open');
	
	// unfocus (blur) all links
	// this has to be AFTER the dialog opens or it will have no effect
	jQuery('a', pepSeqResults).blur();
	jQuery('a', pepSeqResults).click(function(){this.blur();});
}

/**
 * Writes export content to a new browser window.
 * @param content {String} html content to display inside html body
 * @param exporttype {String} type of export (html, plain etc.)
 * @param title {String} window title
 */
function pepSeqExportNewWindow(content, exporttype, title, addSurroundingHTML) {
	var out = '';

	if (addSurroundingHTML) {
		out += '<html>';
		out += '<head>';
		out += '<title>'+title+'</title>';
		out += '<link rel="stylesheet" type="text/css" href="/additions/pepsequencer.css" />';
		out += '<link rel="stylesheet" type="text/css" href="/additions/pepsequencer-print.css" media="print" />';
		out += '</head>';
		out += '<body class="pepsequencer export export'+exporttype+'">';

		out += content;

		out += '</body>';
		out += '</html>';
	} else {
		out = content;
	}

   // display modal wait message
   pepSeqWaitDialogOpen('Exporting', 'Please wait while export is being prepared...',true);

   // store content on server side and continue in callback function
   pepSeqServerStoragePut(out, function(data){
	   var storageid = data.storageid;
	   var iv = data.iv;
	   pepSeqWaitDialogClose();

	   // prepare retrieval script
	   var retrieval = '';
	   retrieval += '<html>';
	   retrieval += '<head>';
	   retrieval += '<title>PepSequencer Export</title>';
	   retrieval += '</head>';
	   retrieval += '<body>';

	   var url = location.protocol+'//'+location.host+pepSeqInterfaceURL;
	   retrieval += '<form id="protection" action="'+url+'" method="POST">';
	   if (pepSeqExportType=='csv') {
			retrieval += '<input type="hidden" name="request" value="getattachment" />';
			retrieval += '<input type="hidden" name="filename" value="pepsequencer-export.csv" />';
			retrieval += '<input type="hidden" name="mimetype" value="text/comma-separated-values" />';
	   } else {
			retrieval += '<input type="hidden" name="request" value="getinline" />';
	   }
	   retrieval += '<input type="hidden" name="token" value="'+pepSeqSecurityToken+'" />';
	   retrieval += '<input type="hidden" name="sequence" value="'+pepSeqSecuritySequence+'" />';
	   retrieval += '<input type="hidden" name="storageid" value="'+storageid+'" />';
	   retrieval += '<input type="hidden" name="iv" value="'+iv+'" />';
	   retrieval += '<input type="submit" name="go" value="Click here if page does not load." />';
	   retrieval += '</form>';
	   retrieval += '<script type="text/javascript">';
	   retrieval += 'document.getElementById("protection").submit();';
	   retrieval += '</script>';

	   retrieval += '</body>';
	   retrieval += '</html>';

	   // open export window
	   var exportwindow = window.open('','');
	   exportwindow.document.write(retrieval);
	   exportwindow.focus();
   });
}

/**
 * Temporarily stores the given content encrypted in database on server side.
 * @param content {String} content to be stored
 * @param callback {function} callback to run after storage completed; will get the storage id
 */
function pepSeqServerStoragePut(content, callback) {
   var myRequestID = ++pepSeqAjaxCounter;

	pepSeqServerStorageCallback = callback;
	
	jQuery.post(pepSeqInterfaceURL, {'request': 'put', 'token': pepSeqSecurityToken, 'content': content, 'sequence': pepSeqSecuritySequence}, function(data,status){
			// don't handle cancelled requests
			if (myRequestID == pepSeqAjaxCounter) {
				pepSeqServerStorageCallback(data);
			}
		}, 'json');
}

/**
 * Initialize a modal waiting message without close button for running query.
 */
function pepSeqInitModalWaitMessage() {
	jQuery('body').append('<div id="pepseqwait" title="" style="display: none;"></div>');
	pepSeqWait = jQuery('#pepseqwait');
	pepSeqWait.dialog({ autoOpen: false, closeOnEscape: false, draggable: false, modal: true, resizable: false, minHeight: 0, width: 400});
	pepSeqWait.parent().find('.ui-dialog-titlebar-close').remove();
}

/**
 * Initialize the query result dialog (content is assembled on retrieval in pepSeqQueryResult).
 */
function pepSeqInitModalResultDialog() {
	(new jQuery('body')).append('<div id="pepseqresults" title="Search results" style="display: none;"></div>');
	pepSeqResults = new jQuery('#pepseqresults');
	pepSeqResults.dialog({ autoOpen: false, closeOnEscape: false, draggable: true, modal: true, resizable: false, width: 675, minHeight: 500 });
}

/**
 * Stores data on server and requests generation and download of a PDF file.
 * @param data {Object} output of pepSeqGenerate
 */
function pepSeqExportPDF(data) {
	if ((navigator.appName == 'Microsoft Internet Explorer') && (navigator.appVersion.match(/MSIE 6\..*/) != null)) {
		// IE 6 does not work because we require a JS popup dialog but our procedure is blocked by security restrictions in IE
		pepSeqModalMessageDialog('Function unavailable', 'Due to security restrictions PDF export is unavailable in Internet Explorer 6.');
		return;
	}

   // display modal wait message
   pepSeqWaitDialogOpen('Exporting', 'Please wait while export is being prepared...',true);

   // store content on server side and continue in callback function
   pepSeqServerStoragePut(jQuery.toJSON(data), function(data){
	   var storageid = data.storageid;
	   var iv = data.iv;
	   pepSeqWaitDialogClose();

	   // prepare retrieval script
	   var retrieval = '';
	   retrieval += '<html>';
	   retrieval += '<head>';
	   retrieval += '<title>PepSequencer Export</title>';
	   retrieval += '</head>';
	   retrieval += '<body>';

	   var url = location.protocol+'//'+location.host+pepSeqInterfaceURL;
	   retrieval += '<form id="protection" action="'+url+'" method="POST">';
	   retrieval += '<input type="hidden" name="request" value="generatepdf" />';
	   retrieval += '<input type="hidden" name="token" value="'+pepSeqSecurityToken+'" />';
	   retrieval += '<input type="hidden" name="sequence" value="'+pepSeqSecuritySequence+'" />';
	   retrieval += '<input type="hidden" name="storageid" value="'+storageid+'" />';
	   retrieval += '<input type="hidden" name="iv" value="'+iv+'" />';
	   retrieval += '<input type="submit" name="go" value="Click here if page does not load." />';
	   retrieval += '</form>';
	   retrieval += '<script type="text/javascript">';
	   retrieval += 'document.getElementById("protection").submit();';
	   retrieval += '</script>';

	   retrieval += '</body>';
	   retrieval += '</html>';

	   // open export window
	   var exportwindow = window.open('','');
	   exportwindow.document.write(retrieval);
	   exportwindow.focus();
   });
}

/**
 * Initialize export dialog.
 */
function pepSeqInitModalExportDialog() {
	(new jQuery('body')).append('<div id="pepseqexport" class="pepsequencer" title="Export options" style="display: none;"></div>');
	pepSeqExport = new jQuery('#pepseqexport');
	pepSeqExport.dialog({ autoOpen: false, closeOnEscape: false, draggable: true, modal: true, resizable: false, width: 460, minHeight: 230 });
	
	// prepare HTML
	var html = '';
	html += '<form>';
	html += '<fieldset>';
	html += '<p>Please select the export format:</p>';
	html += '<input type="radio" name="exporttype" id="pepseqexporttype_html" checked /> <label for="pepseqexporttype_html">new window (printable)</label><br />';
	html += '<input type="radio" name="exporttype" id="pepseqexporttype_plain" /> <label for="pepseqexporttype_plain">plain text (same output as in quote requests)</label><br />';
	html += '<input type="radio" name="exporttype" id="pepseqexporttype_pdf" /> <label for="pepseqexporttype_pdf">PDF</label><br />';
	html += '<input type="radio" name="exporttype" id="pepseqexporttype_csv" /> <label for="pepseqexporttype_csv">CSV ("comma" separated values; can be opened in spreadsheet programs)</label><br />';
	html += '<div id="pepseqexport_csv_more">';
	html += 'String delimiter: " (quotation mark)<br />';
	html += 'Separator: ; (semicolon, not comma, to be compatible to MS Excel)<br />';
	html += '</div>';
	html += '</fieldset>';
	html += '</form>';
	
	// inject to DOM
	pepSeqExport.append(html);
	
	// add click listener to input elements to grab export type
	jQuery('input', pepSeqExport).click(function(){
		pepSeqExportType = this.id.replace(/.*_/,'');
	});
	
	// add buttons
	html  = '<p class="buttonspace">';
	html += '<a href="#" class="button ui-state-default ui-corner-all" id="pepseqexportbuttonclose">Close</a>';
	html += '<a href="#" class="button ui-state-default ui-corner-all" id="pepseqexportbuttonexport">Export</a>';
	html += '</p>';
	pepSeqExport.append(html);
	
	// initialize button hover
	pepSeqInitButtonHover(pepSeqExport);
	
	// add event listeners to buttons
	jQuery('#pepseqexportbuttonclose', pepSeqBody).click(function(){
		pepSeqExport.dialog('close');
		return false;
	});
	jQuery('#pepseqexportbuttonexport', pepSeqBody).click(function(){
		var html = '';
		if (pepSeqExportType == 'html') {
			html += '<a href="#" onclick="window.print();return false;">click here to print</a>';
			html += '<img src="/img/header_logo.jpg" />';
			html += '<h1>PepSequencer Result</h1>';
			html += pepSeqGeneratedData.output.html;
			pepSeqExportNewWindow(html, 'html', 'PepSequencer Export', true);
			return false;
		} else if (pepSeqExportType == 'plain') {
			html += '<h1>PepSequencer Result</h1>';
			html += '<pre>';
			html += pepSeqGeneratedData.output.plaintext;
			html += '</pre>';
			pepSeqExportNewWindow(html, 'plain', 'PepSequencer Export', true);
			return false;
		} else if (pepSeqExportType == 'csv') {
			pepSeqExportNewWindow(pepSeqGeneratedData.output.csv, 'csv', 'PepSequencer Export', false);
			return false;
		} else if (pepSeqExportType == 'pdf') {
			pepSeqExportPDF(pepSeqGeneratedData);
			return false;
		} else {
			alert('export type '+pepSeqExportType+' not yet implemented');
			return false;
		}
		pepSeqExport.dialog('close');
		return false;
	});
}

/**
 * Show a simple modal message dialog containing title and message with a button
 * "OK" to close.
 */
function pepSeqModalMessageDialog(title, message) {
	(new jQuery('body')).append('<div id="pepseqmodalmsgdialog" title="'+title+'" style="display: none;"><p>'+message+'</p></div>');
	var msgdialog = new jQuery('#pepseqmodalmsgdialog');
	msgdialog.dialog({
		autoOpen: true,
		draggable: false,
		resizable: false,
		modal: true,
		buttons: {
			'OK': function(){
				jQuery(this).dialog('close');
				return false;
			}
		},
		close: function() {
			jQuery(this).remove();
		}
	});
}

function pepSeqInitHelpWindow() {
	(new jQuery('body')).append('<div id="pepseqhelp" class="pepsequencer" title="Help" style="display: none;"></div>');
	pepSeqHelp = new jQuery('#pepseqhelp');
	pepSeqHelp.dialog({ autoOpen: false, closeOnEscape: false, draggable: true, modal: true, resizable: false, width: 460, minHeight: 230 });

	// prepare HTML
	var html = '';
	html += '<div class="list">';
	html += '<div class="listlabel">1)</div><div class="listcontent">Enter the length of the peptides under "peptide length"</div>';
	html += '<div class="listlabel">2)</div><div class="listcontent">Enter the desired offset (not overlap) under "peptide offset"</div>';
	html += '<div class="listlabel">3)</div><div class="listcontent">&ndash; if you have the protein sequence available type or copy it under "insert your protein sequence" and jump to position 4<br />&ndash; if you do not have the protein sequence, but only the protein name or protein ID, click "retrieve data from UniProt" at the bottom of the PepSequencer. Enter the protein name or ID under "Query" and press "query database". A list of results will appear. Choose the correct result and click on "copy selected sequence"</div>';
	html += '<div class="listlabel">4)</div><div class="listcontent">The list of peptides will appear automatically</div>';
	html += '<div class="listlabel">5)</div><div class="listcontent">If you wish to print the list of peptides, click on "Export/print" and choose the format in which you want to export the list</div>';
	html += '<div class="listlabel">6)</div><div class="listcontent">To delete the protein sequence and the peptides list, click the "reset" button</div>';
	html += '<div class="conditionalonlyonquoterequest">';
	html += '<div class="listlabel">7)</div><div class="listcontent">To enter the list into the quote request, click the button "copy to quote request". The peptide list will appear in the quote request and the PepSequencer will close itself automatically</div>';
	html += '<div class="listlabel">8)</div><div class="listcontent">You can enter several peptide lists into the quote request by repeating the procedure</div>';
	html += '</div>';
	html += '</div>';
	html += '<p>If you have any trouble using this PepSequencer, please feel free to contact us via email at <a href="mailto:peptide@jpt.com">peptide@jpt.com</a>.</p>';

	// inject to DOM
	pepSeqHelp.append(html);

	// add buttons
	html  = '<p class="buttonspace">';
	html += '<a href="#" class="button ui-state-default ui-corner-all" id="pepseqhelpbuttonclose">Close</a>';
	html += '</p>';
	pepSeqHelp.append(html);

	// initialize button hover
	pepSeqInitButtonHover(pepSeqHelp);

	// add event listeners to buttons
	jQuery('#pepseqhelpbuttonclose').click(function(){
		pepSeqHelp.dialog('close');
		return false;
	});
}

/**
 * Initialize the PepSequencer main window.
 */
function pepSeqInitMainWindow() {
	(new jQuery('body')).append('<div id="pepsequencer" title="PepSequencer" class="pepsequencer" style="display: none;"></div>');
	pepSequencer = new jQuery('#pepsequencer');
	pepSequencer.dialog({ autoOpen: false, resizable: false, minHeight: 300, width: 400, close: pepSeqClosed });
	
	pepSequencer.append('<div class="windowbody"></div>');
	pepSeqBody = new jQuery('.windowbody', pepSequencer);
	
	// construct window body
	/*
	pepSeqBody.append('<div id="pepseq-tabs"><ul><li><a href="#pepseq-tab-sequencer">Sequencer</a></li><li><a href="#pepseq-tab-lookup">Lookup</a></li></ul><div id="pepseq-tab-sequencer"></div><div id="pepseq-tab-lookup"></div></div>');
	(new jQuery('#pepseq-tabs')).tabs();
	
	var tabSequencer = new jQuery('#pepseq-tab-sequencer', pepSeqBody);
	var tabLookup = new jQuery('#pepseq-tab-lookup', pepSeqBody);
	
	tabSequencer.append('<p>Sequencer</p>');
	tabLookup.append('<p>Lookup</p>');
	*/
	
	/*
	// checkbox to toggle lookup
	pepSeqBody.append('<input type="checkbox" id="pepseqperformlookup" value="1" /> <label for="pepseqperformlookup">Retrieve sequence from online databases</label>');
	(new jQuery('#pepseqperformlookup').click(function() {
		(new jQuery('#pepseqlookupframe')).slideToggle();
	}));
	
	// create lookup frame
	pepSeqBody.append('<div id="pepseqlookupframe" class="ui-corner-all" style="display: none;"></div>');
	pepSeqLookUpFrame = new jQuery('#pepseqlookupframe');
	*/
	
	// sequence parameters
	html  = '<form action="#" class="parameters">';
	html += '<fieldset class="parameters">';
	html += '<b>Parameters</b><br />';
	html += '<label for="pepseqlength">Peptide length:</label><input class="textbox" type="text" id="pepseqlength" value="'+pepSeqDefaultLength+'" /><br />';
	html += '<label for="pepseqoffset">Peptide offset:</label><input class="textbox" type="text" id="pepseqoffset" value="'+pepSeqDefaultOffset+'" /><br />';
	html += '<label for="pepseqsequence">Insert your<br />protein<br />sequence:</label>';
	html += '<textarea class="sequence" id="pepseqsequence"></textarea>';
	html += '<div class="pepseqinputlength"><label title="Your input is being cleaned so all irrelevant characters like numbers and spaces are removed before processing.">Length after cleaning:</label><span>0</span></div>';
	html += '</fieldset>';
	html += '</form>';
	
	// inject to DOM
	pepSeqBody.append(html);
	
	// get objects from DOM
	pepSeqInputSequence = jQuery('textarea#pepseqsequence', pepSeqBody);
	pepSeqInputOffset = jQuery('input#pepseqoffset', pepSeqBody);
	pepSeqInputLength = jQuery('input#pepseqlength', pepSeqBody);
	pepSeqLabelLength = jQuery('div.pepseqinputlength span', pepSeqBody);
	
	// add event listeners to input
	jQuery('form.parameters input, form.parameters textarea', pepSeqBody).keyup(function() {
		var jForm = jQuery('form.parameters', pepSeqBody);

		// calculation is very slow in IE 6 so we don't update unless user stops typing
		jForm.stopTime('pepseqsubmit');
		jForm.oneTime('250ms', 'pepseqsubmit', function(){
			jForm.submit();
		});
	});
	jQuery('form.parameters', pepSeqBody).submit(pepSeqGenerateFromField);
	
	// add div for generator output
	pepSeqBody.append('<div class="generatorResult"></div>');
	pepSeqGeneratorResult = jQuery('.generatorResult', pepSeqBody);
	
	// create lookup panel
	// first element becomes headline and second element container, so we need to have ids on both and ONLY append content to the second one
	pepSeqBody.append('<div class="jpt-ui-alternatecolor"><div id="pepseqlookuppanel"><h3 style="font-size: 0.8em;">Retrieve data from UniProt (incl. Swiss-Prot and TrEMBL)</h3><div class="outerframe"><div id="pepseqlookupframe"></div></div></div></div>');
	pepSeqLookUpFrame = new jQuery('#pepseqlookupframe');
	jQuery('#pepseqlookuppanel').panel({'collapsed': true});
	//jQuery('#pepseqlookuppanel').panel({'collapsed': false});

	// add tooltip to titlebar
	jQuery('#pepseqlookuppanel div.ui-panel-title').attr('title', 'Click here to toggle form to query protein databases.');

	// append content to lookup frame
	pepSeqLookUpFrame.append('<div class="lookup"><label for="pepseqlookupsearch">Query:</label> <input class="textbox" type="text" id="pepseqlookupsearch" /></div>');
	/*
	pepSeqLookUpFrame.append('<div id="pepseqlookupproviders">'+
							 '<input type="radio" name="pepseqlookupprovider" id="pepseqlookupuniprot" value="uniprot" checked> <label for="pepseqlookupuniprot">Retrieve data from UniProt (incl. Swiss-Prot and TrEMBL)</label><br />'+
							 '<input type="radio" name="pepseqlookupprovider" id="pepseqlookupncbi" value="ncbi"> <label for="pepseqlookupncbi">Retrieve data from <acronym title="National Center for Biotechnology Information">NCBI</acronym></label>'+
							 '</div>');
	*/
	pepSeqLookUpFrame.append('<p class="buttonspace"><a href="#" class="button ui-state-default ui-corner-all" id="pepseqlookupbutton">Query database</a></p>');

	pepSeqLookUpFrame.append('<div id="pepseqlookuplicenses">'+
							 '<div class="licensehead">License information:</div>'+
							 '<div class="license" id="pepseqlookupuniprotlicense"><a href="http://www.uniprot.org/" target="_blank">UniProt</a> database is licensed under terms of <a href="http://creativecommons.org/licenses/by-nd/3.0/" target="_blank" >Creative Commons Attribution-NoDerivs</a>. Retrieved data is under copyright by <i>The UniProt Consortium</i>, The Universal Protein Resource (UniProt), where applicable.</div>'+
							 //'<div class="license" id="pepseqlookupncbilicense" style="display: none;"><a href="http://www.ncbi.nlm.nih.gov/" target="_blank">NCBI</a> peptide database is offered under public domain. Explicit permission for use on this website has been granted in case PD does not apply in your country.</div>'+
							 '</div>');

	/*
	(new jQuery('#pepseqlookupproviders input')).click(function() {
		var oldProvider = pepSeqProvider;
		pepSeqProvider = this.value;

		// change license notice on provider change
		if (pepSeqProvider != oldProvider) {
			var id = (new jQuery(this)).attr('id');
			(new jQuery('.license')).slideUp();
			(new jQuery('#'+id+'license')).slideDown();
		}
	});
	*/

	(new jQuery('#pepseqlookupbutton')).click(function() {
		// remember query and page globally (we start a new search)
		pepSeqQuery = (new jQuery('#pepseqlookupsearch'))[0].value;
		pepSeqPagingPage = 0;

		// send query to server
		pepSeqSendQuery();

		return false;
	});

	jQuery('#pepseqlookupsearch').keypress(function (e) {
		if (e.which == 13) {
			// enter = click lookup button
			jQuery('#pepseqlookupbutton').trigger('click');
			return false;
		}
		return true;
	});

	// add buttons
	html  = '<p class="buttonspace">';
	html += '<a href="#" class="button ui-state-default ui-corner-all" id="pepseqmainbuttonclose">Close</a>';
	html += '<a href="#" class="button ui-state-default ui-corner-all" id="pepseqmainbuttonreset">Reset</a>';
	html += '<a href="#" class="button ui-state-default ui-corner-all" id="pepseqmainbuttonexport">Export/Print</a>';
	html += '<a href="#" class="button ui-state-default ui-corner-all" id="pepseqmainbuttoncopy">Copy to Quote Request</a>';
	html += '<a href="#" class="button ui-state-default ui-corner-all" id="pepseqmainbuttonhelp">Help</a>';
	html += '</p>';
	pepSeqBody.append(html);
	
	// initialize button hover
	pepSeqInitButtonHover(pepSeqBody);
	
	// remember copy button for direct access
	pepSeqCopyButton = jQuery('#pepseqmainbuttoncopy', pepSeqBody);
	
	// add event listeners to buttons
	jQuery('#pepseqmainbuttonclose', pepSeqBody).click(function(){
		pepSequencer.dialog('close');
		return false;
	});
	jQuery('#pepseqmainbuttonexport', pepSeqBody).click(function(){
		if (pepSeqGeneratedData==null) {
			pepSeqModalMessageDialog('Export','Cannot export yet - please enter data to generate sequences.');
		} else if (pepSeqGeneratedData.errors.length > 0) {
			pepSeqModalMessageDialog('Export','Cannot export yet due to errors. Please check your input.');
		} else {
			pepSeqExport.dialog('open');
		}
		return false;
	});
	jQuery('#pepseqmainbuttonhelp', pepSeqBody).click(function(){
		pepSeqHelp.dialog('open');
		return false;
	});
	pepSeqCopyButton.click(function(){
		if (pepSeqGeneratedData==null) {
			pepSeqModalMessageDialog('Copy','Cannot copy yet - please enter data to generate sequences.');
		} else if (pepSeqGeneratedData.errors.length > 0) {
			pepSeqModalMessageDialog('Copy','Cannot copy yet due to errors. Please check your input.');
		} else if (pepSeqCopyCallback != null) {
			pepSeqCopyCallback(pepSeqGeneratedData, pepSeqCopyCallbackOption);
		}
		return false;
	});
	jQuery('#pepseqmainbuttonreset', pepSeqBody).click(function(){
		(new jQuery('body')).append('<div id="pepseqconfirmation" title="PepSequencer Reset Confirmation" style="display: none;"><p>Do you really want to reset your PepSequencer input? (sequence, length and offset)</p></div>');
		var confirmation = new jQuery('#pepseqconfirmation');
		confirmation.dialog({
			autoOpen: true,
			draggable: false,
			resizable: false,
			modal: true,
			buttons: {
				'Reset': function(){
					pepSeqInputLength.val(pepSeqDefaultLength);
					pepSeqInputOffset.val(pepSeqDefaultOffset);
					pepSeqInputSequence.val('');
					pepSeqGenerateFromField();
					jQuery(this).dialog('close');
					return false;
				},
				'Cancel': function(){
					jQuery(this).dialog('close');
					return false;
				}
			},
			close: function() {
				jQuery(this).remove();
			}
		});
		
		return false;
	});
}

/**
 * Initializes button hover.
 * @param parentobj {Object} parent to hover .button children (deep)
 */
function pepSeqInitButtonHover(parentobj) {
	jQuery('.button', parentobj).hover(
		function () {
			jQuery(this).addClass('ui-state-hover');
		},
		function () {
			jQuery(this).removeClass('ui-state-hover');
		}
	);
}

/**
 * Initializes the internal clientside security string sequence used as a simple storage chiffre.
 */
function pepSeqInitSecuritySequence() {
	pepSeqSecuritySequence = '';
	var tmp = 0;
	var ch = '';
	for (var i=0; i<32; i++) {
		// get a random number (two times alphabet + 10 digits, minus one because index starts at 0)
		tmp = Math.round(Math.random() * (26 * 2 + 10 - 1));
		
		// map to character ranges
		if (tmp < 10) {
			// digit
			ch = String.fromCharCode(tmp + 48);
		} else if (tmp < 36) {
			// upper case alphabet
			ch = String.fromCharCode((tmp-10) + 65);
		} else {
			// lower case alphabet
			ch = String.fromCharCode((tmp-36) + 97);
		}
		
		// append to sequence
		pepSeqSecuritySequence += ch;
	}
}

/**
 * Query server for security token.
 */
function pepSeqAcquireSecurityToken() {
	jQuery.post(pepSeqInterfaceURL, {'request': 'acquiretoken'}, function(data,status){
			pepSeqSecurityToken = data;
		}, 'json');
}

/**
 * Initialize the whole PepSequencer application.
 */
function pepSeqInit() {
	pepSeqInitSecuritySequence();
	
	pepSeqInitModalExportDialog();
	pepSeqInitModalWaitMessage();
	pepSeqInitModalResultDialog();
	pepSeqInitHelpWindow();
	pepSeqInitMainWindow();
	
	// insert links/buttons to open window
	//(new jQuery('.sequences')).append('<a href="#" class="pepseqopen">Open PepSequencer</a>');
	//(new jQuery('.sequences legend')).after('<a href="#" class="pepseqopen"><img src="/img/system/pepsequencer/pepsequencer-icon-dummy-24.png" /></a>');
	//(new jQuery('.sequences legend')).after('<a href="#" class="pepseqopen" alt="PepSequencer" title="PepSequencer"></a>');
	/*
	(new jQuery('a.pepseqopen')).click(function() {
		pepSeqOpen({modal: true, callback: });
	});
	*/
}

(new jQuery(document)).ready(pepSeqInit);

