source: extensions/net.sf.basedb.reggie/trunk/resources/libprep/create_manual_pool.jsp @ 2151

Last change on this file since 2151 was 2151, checked in by Nicklas Nordborg, 9 years ago

Fixes #538: Count and display number of libraries with low and high concentration in create pooled library wizards

Added three counters to the two pooling wizards:

  • Separate mix: the number of libraries that need to be mixed separately before pooling
  • Low quantity: the number of libraries with less remaining quantity that required to reach the target molarity/volume for the pool
  • Excluded: the number of libraries that has been excluded from the pool
File size: 24.1 KB
Line 
1<%@ page
2  pageEncoding="UTF-8"
3  session="false"
4  import="net.sf.basedb.core.User"
5  import="net.sf.basedb.core.DbControl"
6  import="net.sf.basedb.core.SessionControl"
7  import="net.sf.basedb.core.Application"
8  import="net.sf.basedb.util.formatter.WellCoordinateFormatter"
9  import="net.sf.basedb.clients.web.Base" 
10  import="net.sf.basedb.clients.web.extensions.ExtensionsControl"
11%>
12<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
13<%@ taglib prefix="p" uri="/WEB-INF/path.tld" %>
14<%@ taglib prefix="tbl" uri="/WEB-INF/table.tld" %>
15<%
16final SessionControl sc = Base.getExistingSessionControl(request, true);
17final String ID = sc.getId();
18final float scale = Base.getScale(sc);
19final String home = ExtensionsControl.getHomeUrl("net.sf.basedb.reggie");
20DbControl dc = null;
21try
22{
23  dc = sc.newDbControl();
24  final User user = User.getById(dc, sc.getLoggedInUserId());
25%>
26<base:page type="default" >
27<base:head scripts="ajax.js" styles="path.css,toolbar.css">
28  <link rel="stylesheet" type="text/css" href="../css/reggie.css">
29  <script language="JavaScript" src="../reggie.js" type="text/javascript" charset="UTF-8"></script>
30  <script language="JavaScript" src="pools.js" type="text/javascript" charset="UTF-8"></script>
31
32<script language="JavaScript">
33var debug = 0;
34var currentStep = 1;
35
36var selectedLibraries = [];
37var targetVolumePerLibIsValid = false;
38var poolInfo;
39
40// Loaded from servlet when getting Library information
41var TARGET_MOLARITY_IN_POOL;
42var LIMIT_FOR_AUTO_EXCLUDE = 0.25;
43var LIMIT_FOR_EXTRA_LARGE_MIX;
44var EXTRA_LARGE_MIX_FACTOR;
45
46function init()
47{
48  var frm = document.forms['reggie'];
49
50  // Load some default pooling options and the auto-generated name for the next pool
51  var response = loadPoolInfo();
52  var poolInfo = response.poolInfo;
53  TARGET_MOLARITY_IN_POOL = poolInfo.targetMolarity;
54  EXTRA_LARGE_MIX_FACTOR = poolInfo.extraLargeMixFactor;
55  LIMIT_FOR_EXTRA_LARGE_MIX = poolInfo.limitForExtraLargeMix;
56  setInnerHTML('pool-name', response.names[0]);
57  setInnerHTML('target_molarity', Numbers.formatNumber(TARGET_MOLARITY_IN_POOL, 1, ' nM'));
58 
59  frm.target_volume.value = poolInfo.targetVolumePerLib;
60  frm.poolName.value = response.names[0];
61  targetVolumeOnChange();
62}
63
64
65function loadPoolInfo()
66{
67  var frm = document.forms['reggie'];
68
69  // Load Libraries and related info from the selected bioplate
70  var request = Ajax.getXmlHttpRequest();
71  try
72  {
73    showLoadingAnimation('Loading pooling information...');
74    var url = '../Pool.servlet?ID=<%=ID%>&cmd=GetNextAutoGeneratedPoolNames&numNames=1';   
75    request.open("GET", url, false);
76    request.send(null);
77  }
78  finally
79  {
80    hideLoadingAnimation();
81  }
82 
83  if (debug) Main.debug(request.responseText);
84  var response = JSON.parse(request.responseText);
85  if (response.status != 'ok')
86  {
87    setFatalError(response.message);
88    return false;
89  }
90 
91  return response;
92}
93
94function checkAll()
95{
96  var frm = document.forms['reggie'];
97
98  var check = null;
99  for (var i = 0; i < frm.elements.length; i++)
100  {
101    var e = frm.elements[i];
102    if (e.type == 'checkbox')
103    {
104      if (check == null) check = !e.checked;
105      e.checked = check;
106    }
107  }
108}
109
110function removeSelected()
111{
112  var frm = document.forms['reggie'];
113  var tbody = document.getElementById('lib-table-rows');
114  var numRemoved = 0;
115  for (var libNo = selectedLibraries.length-1; libNo >= 0; libNo--)
116  {
117    var lib = selectedLibraries[libNo];
118    if (frm['check.'+lib.id].checked)
119    {
120      var tr = document.getElementById('lib.'+lib.id);
121      tbody.removeChild(tr);
122      selectedLibraries.splice(libNo, 1);
123      numRemoved++;
124    }
125  }
126 
127  if (numRemoved > 0)
128  {
129    updatePoolData();
130  }
131 
132  if (selectedLibraries.length == 0)
133  {
134    Main.show('not-selected');
135    Main.hide('lib-table');
136  }
137}
138
139var subtypeLibrary = null;
140
141function selectLibraries()
142{
143  var frm = document.forms['reggie'];
144 
145  if (subtypeLibrary == null)
146  {
147    var request = Ajax.getXmlHttpRequest();
148    var url = '../Subtype.servlet?ID=<%=ID%>&cmd=GetSubtypeInfo&name=LIBRARY';
149    request.open("GET", url, false); 
150    request.send(null);
151
152    if (debug) Main.debug(request.responseText);
153    var response = JSON.parse(request.responseText); 
154    if (response.status != 'ok')
155    {
156      setFatalError(response.message);
157      return false;
158    }
159    subtypeLibrary = response.subtype;
160  }
161 
162  var url = getRoot() + 'biomaterials/extracts/index.jsp?ID=<%=ID%>&cmd=UpdateContext&mode=selectmultiple&callback=setLibraryCallback';
163  url += '&resetTemporary=1';
164  url += '&tmpfilter:INT:itemSubtype='+subtypeLibrary.id;
165  url += '&tmpfilter:DATE:creationEvent.eventDate='+encodeURIComponent('<>');
166  url += '&tmpfilter:STRING:bioWell.bioPlate.name='+encodeURIComponent('<>');;
167 
168  var exclude = [];
169  for (var libNo = 0; libNo < selectedLibraries.length; libNo++)
170  {
171    exclude[exclude.length] = selectedLibraries[libNo].id;
172  }
173  if (exclude.length > 0) url += '&exclude='+exclude.join(',');
174 
175  Main.openPopup(url, 'SelectLibrary', 1000, 700);
176}
177
178function setLibraryCallback(id, name)
179{
180  var frm = document.forms['reggie'];
181
182  // Check if this library has already been selected
183  for (var i = 0; i < selectedLibraries.length; i++)
184  {
185    if (selectedLibraries[i].id == id)
186    {
187      return;
188    }
189  }
190 
191  // Disable timer while we are working -- will be re-enabled later
192  if (updateTimerId) clearTimeout(updateTimerId);
193 
194  // Get more information about the selected library
195  var request = Ajax.getXmlHttpRequest();
196  var url = '../Pool.servlet?ID=<%=ID%>&cmd=GetLibraryInfo&libraryId=' + id;   
197  request.open("GET", url, false); 
198  request.send(null);
199
200  if (debug) Main.debug(request.responseText);
201  var response = JSON.parse(request.responseText);
202  if (response.status != 'ok')
203  {
204    setFatalError(response.message);
205    return false;
206  }
207 
208  var lib = response.library;
209  selectedLibraries[selectedLibraries.length] = lib;
210
211  lib.excludeFromPool = lib.molarity > 0 && lib.remainingQuantity > 0 ? false : true;
212 
213  Main.hide('not-selected');
214  Main.show('lib-table');
215
216  addLibraryToTable(lib);
217  updatePoolDataDelayed();
218}
219
220function addLibraryToTable(lib)
221{
222  var tr = document.createElement('tr');
223  tr.id = 'lib.'+lib.id;
224  var className = '';
225  if (lib.excludeFromPool) className += ' excluded';
226  addColumn(tr, 'check', '<input type="checkbox" name="check.'+lib.id+'" value="'+lib.id+'">');
227  addColumn(tr, 'name', lib.name);
228  addColumn(tr, 'barcode', lib.barcode ? lib.barcode.name : '-');
229  addColumn(tr, 'location', lib.bioWell ? lib.bioWell.bioPlate.name + ' ' + lib.bioWell.location : '-');
230  addColumn(tr, 'remain', lib.remainingVolume ? Numbers.formatNumber(lib.remainingVolume, 1) : '-');
231  addColumn(tr, 'molarity', lib.molarity ? Numbers.formatNumber(lib.molarity, 2) : '-');
232  addColumn(tr, 'volume', '');
233  addColumn(tr, 'eb', '');
234  addColumn(tr, 'remarks', '');
235  tr.className = className;
236
237  // Rows should be sorted by barcode
238  // Locate existing row
239  tr.sortBy = lib.barcode ? lib.barcode.name : lib.name;
240  var tbody = document.getElementById('lib-table-rows');
241 
242  var target = null;
243  for (var i = 0; i < tbody.childNodes.length; i++)
244  {
245    var row = tbody.childNodes[i];
246    if (row.sortBy && (row.sortBy > tr.sortBy))
247    {
248      // Found it!
249      target = row;
250      break;
251    };
252  }
253 
254  tbody.insertBefore(tr, target);
255}
256
257function addColumn(tr, idSuffix, text)
258{
259  var td = document.createElement('td');
260  td.id = tr.id+'.'+idSuffix;
261  td.className = idSuffix;
262  td.innerHTML = text;
263  tr.appendChild(td);
264}
265
266
267function calculateRemarks(lib, mixingStrategy, duplicateBarcodes)
268{
269  var remarks = [];
270
271  // Check if default barcode has been modified
272  if (lib.molarity != null)
273  {
274    if (duplicateBarcodes[lib.barcode.name])
275    {
276      remarks[remarks.length] = 'Duplicate barcode: ' + lib.barcode.name;
277    }
278   
279    if (lib.volume != lib.actualVolume)
280    {
281      if (lib.volume > lib.remainingVolume)
282      {
283        remarks[remarks.length] = 'Low quantity';
284      }
285      else
286      {
287        remarks[remarks.length] = 'Low molarity';
288      }
289      if (mixingStrategy == 'fixed')
290      {
291        remarks[remarks.length] = 'Mix <span class="volume">'+Numbers.formatNumber(lib.actualVolume, 1) + '</span>+<span class="eb">' + Numbers.formatNumber(lib.actualEb, 1)+'µl</span>';
292      }
293      else
294      {
295        remarks[remarks.length] = 'Use <span class="volume">'+Numbers.formatNumber(lib.actualVolume, 1) + 'µl</span>';
296      }
297    }
298   
299    if (lib.speedVacConc != null)
300    {
301      remarks[remarks.length] = 'SpeedVac';
302    }
303   
304    if (lib.mixFactor > 1)
305    {
306      // Larger mix than default
307      remarks[remarks.length] = 'Mix <span class="volume">'+Numbers.formatNumber(lib.volume*lib.mixFactor, 1) + '</span>+<span class="eb">' + Numbers.formatNumber(lib.eb*lib.mixFactor, 1)+'µl</span>';
308      remarks[remarks.length] = 'Use <b>' + Numbers.formatNumber(lib.volume+lib.eb, 1) + 'µl</b> in pool';
309    }
310  }
311  if (lib.excludeFromPool)
312  {
313    remarks[remarks.length] = 'Excluded';
314  }
315  if (lib.comment)
316  {
317    remarks[remarks.length] = lib.comment;
318  }
319  lib.stratagene = lib.name.indexOf(GENERIC_STRATAGENE_NAME) == 0;
320  lib.external = lib.name.indexOf(EXTERNAL_RNA_NAME) == 0;
321  lib.remarks = remarks;
322}
323
324
325var updateTimerId = null;
326function updatePoolDataDelayed()
327{
328  // Cancel previous time
329  if (updateTimerId) clearTimeout(updateTimerId);
330  updateTimerId = setTimeout(updatePoolData, 200);
331}
332
333function updatePoolData()
334{
335  updateTimerId = null;
336 
337  if (!targetVolumePerLibIsValid || selectedLibraries.length == 0) return;
338 
339  var frm = document.forms['reggie'];
340  var targetVolumePerLib = parseFloat(frm.target_volume.value);
341  var mixingStrategy = Forms.getCheckedRadio(frm.mixing_strategy).value;
342
343  var numSeparateMix = 0;
344  var numLowQuantity = 0;
345  var numExcluded = 0;
346
347  // Get all libs into an array
348  var libs = [];
349  var usedBarcodes = [];
350  var duplicateBarcodes = [];
351  for (var selectedNo = 0; selectedNo < selectedLibraries.length; selectedNo++)
352  {
353    var lib = selectedLibraries[selectedNo];
354    if (!lib.excludeFromPool)
355    {
356      libs[libs.length] = lib;
357      if (usedBarcodes[lib.barcode.name])
358      {
359        duplicateBarcodes[lib.barcode.name] = 1;
360      }
361      else
362      {
363        usedBarcodes[lib.barcode.name] = 1;
364      }
365      PoolMix.calculateLibVolume(lib, TARGET_MOLARITY_IN_POOL, targetVolumePerLib, mixingStrategy);
366      PoolMix.calculateEbVolume(lib, TARGET_MOLARITY_IN_POOL, targetVolumePerLib, mixingStrategy);
367    }
368    else
369    {
370      numExcluded++;
371    }
372  }
373   
374  // Calculate final pool volumes, molarity, etc.
375  poolInfo = PoolMix.calculateFinalPoolInfo(libs, TARGET_MOLARITY_IN_POOL, targetVolumePerLib, mixingStrategy);
376 
377  // Make remarks for each lib
378  for (var libNo = 0; libNo < selectedLibraries.length; libNo++)
379  {
380    var lib = selectedLibraries[libNo];
381    if (!lib.excludeFromPool)
382    {
383      var mark = '';
384      var tr = document.getElementById('lib.'+lib.id);
385      if (duplicateBarcodes[lib.barcode.name])
386      {
387        Main.addClass(tr, 'lib-error');
388        lib.hasError = true;
389      }
390      else
391      {
392        Main.removeClass(tr, 'lib-error');
393        lib.hasError = false;
394      }
395      if (lib.volume != lib.actualVolume)
396      {
397        mark = '*';
398        Main.addClass(tr, 'warning');
399      }
400      else
401      {
402        Main.removeClass(tr, 'warning');
403      }
404      setInnerHTML('lib.'+lib.id+'.volume', lib.mixFactor > 1 ? '-' : Numbers.formatNumber(lib.volume, 1)+mark);
405      if (mixingStrategy == 'fixed' && lib.mixFactor < 1.001)
406      {
407        setInnerHTML('lib.'+lib.id+'.eb', Numbers.formatNumber(lib.eb, 1));
408      }
409      else
410      {
411        setInnerHTML('lib.'+lib.id+'.eb', '-');
412      }
413      if (lib.eb != 0 || mixingStrategy == 'fixed') numSeparateMix++;
414      if (lib.volume > lib.actualVolume) numLowQuantity++;
415    }
416   
417    calculateRemarks(lib, mixingStrategy, duplicateBarcodes);
418    setInnerHTML('lib.'+lib.id+'.remarks', lib.remarks.join('; '));
419  }
420 
421  var warnMsg = null;
422  if (poolInfo.ebVolumeExtra < 0) 
423  {
424    warnMsg = 'Too many libs with low molarity';
425  }
426 
427  setInnerHTML('pool.eb', Numbers.formatNumber(poolInfo.ebVolumeExtra, 1));
428
429  var poolDiv = document.getElementById('pool-summary');
430  var poolRow = document.getElementById('lib-table-pool-summary');
431  var poolData = '<div class="pool-data">';
432  poolData += libs.length + ' libs  ';
433  poolData += Numbers.formatNumber(poolInfo.molarity, 2, 'nM')+'  ';
434  poolData += Numbers.formatNumber(poolInfo.totalVolume, 1, 'µl');
435  if (mixingStrategy == 'dynamic')
436  {
437    poolData += '  <span class="eb">'+Numbers.formatNumber(Math.max(0, poolInfo.ebVolumeExtra), 1, 'µl')+'</span>';
438  }
439  if (warnMsg)
440  {
441    Main.addClass(poolRow, 'warning');
442    setInnerHTML('pool.remarks', warnMsg);
443  }
444  else
445  {
446    Main.removeClass(poolRow, 'warning');
447    setInnerHTML('pool.remarks', '');
448  }
449 
450  poolData += '</div>';
451  poolDiv.innerHTML = poolData;
452 
453  setInnerHTML('num_separate_mix', numSeparateMix);
454  setInnerHTML('num_low_quantity', numLowQuantity);
455  setInnerHTML('num_excluded', numExcluded);
456
457}
458
459function targetVolumeOnChange()
460{
461  var frm = document.forms['reggie'];
462  var targetVolumePerLib = parseFloat(frm.target_volume.value);
463 
464  targetVolumePerLibIsValid = false;
465  if (targetVolumePerLib < 2 || targetVolumePerLib > 10)
466  {
467    setInputStatus('target_volume', 'Must be between 2 and 10µl', 'invalid');
468    return;
469  }
470  setInputStatus('target_volume', '', 'valid');
471  targetVolumePerLibIsValid = true;
472
473  updatePoolData();
474}
475
476function mixingStrategyOnChange()
477{
478  updatePoolData();
479}
480
481function goCreate()
482{
483  if (!targetVolumePerLibIsValid) return;
484  if (selectedLibraries.length == 0) return;
485
486 
487  var submitInfo = {};
488  submitInfo.pools = [];
489  submitInfo.flagged = []; // Always empty
490  var frm = document.forms['reggie'];
491  submitInfo.targetVolumeInPoolPerLib = parseFloat(frm.target_volume.value);
492  submitInfo.targetPoolMolarity = TARGET_MOLARITY_IN_POOL;
493  submitInfo.mixingStrategy = Forms.getCheckedRadio(frm.mixing_strategy).value;
494 
495  var pool = {};
496  pool.name = frm.poolName.value;
497  pool.comment = frm.poolComments.value;
498  pool.ebVolumeExtra = Math.max(0, poolInfo.ebVolumeExtra);
499  pool.libs = [];
500  pool.excluded = [];
501 
502  for (var libNo = 0; libNo < selectedLibraries.length; libNo++)
503  {
504    var lib = selectedLibraries[libNo];
505    if (lib.hasError) return;
506   
507    // We send library id, name and mixing volumes
508    if (!lib.excludeFromPool)
509    {
510      var tmp = {};
511      tmp.id = lib.id;
512      tmp.name = lib.name;
513      tmp.volume = lib.actualVolume;
514      tmp.eb = lib.actualEb;
515      tmp.mixFactor = lib.mixFactor;
516      tmp.comment = lib.comment;
517      pool.libs[pool.libs.length] = tmp;
518    }
519  }
520  submitInfo.pools[submitInfo.pools.length] = pool;
521 
522  Main.addClass(document.getElementById('step.1.section'), 'disabled');
523  Main.hide('gocancel');
524  Main.hide('gocreate');
525
526  if (debug) Main.debug(JSON.stringify(submitInfo));
527  var request = Ajax.getXmlHttpRequest();
528  try
529  {
530    showLoadingAnimation('Creating pool...');
531    var url = '../Pool.servlet?ID=<%=ID%>';
532    url += '&cmd=CreatePools';
533    request.open("POST", url, false);
534    request.setRequestHeader("Content-Type", "application/json");
535    request.send(JSON.stringify(submitInfo));
536  }
537  finally
538  {
539    hideLoadingAnimation();
540  }
541
542  if (debug) Main.debug(request.responseText);
543
544  var response = JSON.parse(request.responseText);
545  if (response.status != 'ok')
546  {
547    setFatalError(response.message);
548    return false;
549  }
550
551  var msg = '<ul>';
552  for (var i = 0; i < response.messages.length; i++)
553  {
554    msg += '<li>' + response.messages[i];
555  }
556  msg += '</ul>';
557  setInnerHTML('done', msg);
558  Main.show('done');
559  Main.show('gorestart');
560  scrollToBottom(document.getElementById('content'));
561
562}
563
564//Set a comment on the checked libraries
565var lastComment = '';
566function commentChecked()
567{
568  var frm = document.forms['reggie'];
569 
570  // Get array with checked libs
571  var checked = [];
572  var comment;
573  for (var libNo = 0; libNo < selectedLibraries.length; libNo++)
574  {
575    var lib = selectedLibraries[libNo];
576    if (frm['check.'+lib.id].checked)
577    {
578      checked[checked.length] = lib;
579      if (!comment) comment = lib.comment;
580    }
581  }
582 
583  if (checked.length == 0)
584  {
585    alert('No libraries have been selected');
586    return;
587  }
588       
589  comment = prompt('Comment', comment || lastComment);
590  if (comment == null) return;
591
592  lastComment = comment;
593 
594  if (comment == '') comment = null;
595  for (var libNo = 0; libNo < checked.length; libNo++)
596  {
597    var lib = checked[libNo];
598    lib.comment = comment;
599  }
600 
601  updatePoolData();
602}
603
604</script>
605<style>
606
607#pool-table
608{
609  width: 100%;
610}
611
612#pool-name
613{
614  font-size: 125%;
615}
616
617#pool-table th, #pool-table td
618{
619  text-align: left;
620  padding: 2px 0.5em 2px 0.5em;
621}
622
623#lib-table table
624{
625  width: 100%;
626  border-collapse: collapse;
627}
628
629#lib-table thead, #lib-table tbody
630{
631  border-bottom: 1px solid #A0A0A0;
632}
633
634#lib-table th, #lib-table td
635{
636  text-align: left;
637  padding: 2px 0.5em 2px 0.5em;
638  border-left: 1px solid #A0A0A0;
639}
640
641#lib-table thead
642{
643  background-color: #F0F0F0;
644}
645
646#lib-table tbody#lib-table-rows tr
647{
648  border-bottom: 1px dotted #A0A0A0;
649}
650
651#lib-table tbody#lib-table-rows tr.excluded
652{
653    font-style: italic;
654    color: #666666;
655}
656
657#lib-table .check
658{
659  width: 25px;
660  text-align: center;
661  border-left: 0;
662}
663
664#lib-table .name
665{
666  width: 12em;
667}
668
669#lib-table .barcode
670{
671  width: 6em;
672}
673
674#lib-table .plate
675{
676  width: 12em;
677}
678
679#lib-table .remain
680{
681  width: 4em;
682  text-align: center;
683}
684
685
686#lib-table .molarity
687{
688  width: 4em;
689  text-align: center;
690}
691
692#lib-table .volume
693{
694  width: 4em;
695  text-align: center;
696}
697
698#lib-table .eb
699{
700  width: 4em;
701  text-align: center;
702}
703
704td.volume, span.volume
705{
706  color: #C80000;
707}
708
709td.eb, span.eb
710{
711  color: #0000C8;
712}
713
714#lib-table td.remarks
715{
716  font-style: italic;
717  background-position: 2px 50%;
718  background-repeat: no-repeat;
719}
720
721#lib-table .warning .remarks
722{
723  background-image: url('../images/warning.png');
724  padding-left: 25px;
725 
726}
727
728#lib-table .lib-error .remarks
729{
730  background-image: url('../images/error.png');
731  padding-left: 25px;
732  color: #C80000;
733 
734}
735
736
737#lib-table-pool-summary td
738{
739  font-weight: bold;
740}
741</style>
742</base:head>
743<base:body onload="init()">
744
745  <p:path><p:pathelement 
746    title="Reggie" href="<%="../index.jsp?ID="+ID%>" 
747    /><p:pathelement title="Create manual pool" 
748    /></p:path>
749
750  <div class="content" id="content">
751  <%
752  if (sc.getActiveProjectId() == 0)
753  {
754    %>
755    <div class="messagecontainer note" style="width: 950px; margin-left: 20px; margin-bottom: 20px; margin-right: 0px; font-weight: bold; color: #cc0000;">
756      No project has been selected. You may proceed with the registration but
757      created items will not be shared.
758    </div>
759    <%
760  }
761  %>
762
763  <form name="reggie" onsubmit="return false;">
764  <input type="hidden" name="poolName" value="">
765 
766  <div id="step.1.section">
767  <table class="stepform">
768  <tr>
769    <td rowspan="3" class="stepno">1</td>
770    <td class="steptitle">Select pool options and libraries</td>
771  </tr>
772  <tr>
773    <td class="stepfields">
774      <table class="bottomborder" >
775      <tr valign="top">
776        <td class="prompt">Target molarity</td>
777        <td class="input">
778          <span id="target_molarity"></span>
779        </td>
780        <td class="status"></td>
781        <td class="help"></td>
782      </tr>
783      <tr valign="top">
784        <td class="prompt">Average volume / lib</td>
785        <td class="input">
786          <input type="text" class="text" name="target_volume" 
787            value="5" style="width: 4em;"
788            onblur="targetVolumeOnChange()"
789            onkeypress="doOnEnter(event, targetVolumeOnChange); return Numbers.numberOnly(event)"> µl (2--10)
790        </td>
791        <td class="status" id="target_volume.status"></td>
792        <td class="help" rowspan="2"><span id="target_volume.message" class="message" style="display: none;"></span>
793          Select a target volume when mixing each lib to 2nM before pooling.
794          Use the <b>Dynamic</b> strategy to prioritize final pool molarity by mixing
795          different volume for each library. Use the <b>Fixed</b> strategy to mix all
796          libraries to the given volume.
797        </td>
798      </tr>
799      <tr valign="top">
800        <td class="prompt">Mixing strategy</td>
801        <td class="input">
802          <label><input type="radio" name="mixing_strategy" value="dynamic" checked
803            onclick="mixingStrategyOnChange()"
804            >Dynamic</label>
805          <label><input type="radio" name="mixing_strategy" value="fixed"
806            onclick="mixingStrategyOnChange()"
807            >Fixed</label>
808        </td>
809        <td class="status" id="mixing_strategy.status"></td>
810      </tr>
811      </table>
812     
813      <tbl:toolbar subclass="bottomborder">
814        <tbl:button 
815          title="Select libraries&hellip;" 
816          image="<%=home+"/images/new.png"%>" 
817          onclick="selectLibraries()" 
818          tooltip="Select libraries to include in the pool"
819        />
820        <tbl:button 
821          title="Remove" 
822          image="<%=home+"/images/delete.png"%>" 
823          onclick="removeSelected()" 
824          tooltip="Remove the selected libraries from the pool"
825        />
826        <tbl:button 
827          title="Comments&hellip;" 
828          image="<%=home+"/images/comment.png"%>" 
829          onclick="commentChecked()" 
830          tooltip="Add a comment to the selected libraries" 
831        />
832      </tbl:toolbar>
833
834      <div id="pool-table" class="bottomborder">
835      <table style="width: 100%;">
836      <tr>
837        <th id="pool-name" style="width: 25%;"></th>
838        <td style="border-left: 1px solid #A0A0A0;" rowspan="2"><textarea name="poolComments" style="width: 95%; height: 3.5em;"></textarea></td>
839      </tr>
840      <tr>
841        <td style="width: 25%;" id="pool-summary">0 libs • 0.00nM • 0.0µl</td>
842      </tr>
843      </table>
844      </div>
845     
846      <div id="lib-table" style="display: none;">
847      <div style="margin: 1em;" class="messagecontainer note">
848        <base:icon image="info.png"/> 
849        <b>Separate mix:</b> <span id="num_separate_mix"></span>,
850        <b>Low quantity:</b> <span id="num_low_quantity"></span>,
851        <b>Excluded:</b> <span id="num_excluded"></span>
852      </div>
853      <table class="bottomborder topborder">
854      <thead>
855        <tr>
856          <th class="check"></th>
857          <th class="name"></th>
858          <th class="barcode"></th>
859          <th class="plate"></th>
860          <th class="molarity">Remain</th>
861          <th class="molarity">DNA</th>
862          <th class="volume">Volume</th>
863          <th class="eb">EB</th>
864          <th class="remarks"></th>
865        </tr>
866        <tr>
867          <th class="check"><base:icon image="check_uncheck.png" tooltip="Check/uncheck all" onclick="checkAll()" /></th>
868          <th class="name">Library</th>
869          <th class="barcode">Barcode</th>
870          <th class="plate">Work plate</th>
871          <th class="molarity">(µl)</th>
872          <th class="molarity">(nM)</th>
873          <th class="volume">(µl)</th>
874          <th class="eb">(µl)</th>
875          <th class="remarks">Remarks</th>
876        </tr>
877      </thead>
878      <tbody id="lib-table-rows"></tbody>
879      <tbody style="border-bottom: 0;">
880        <tr id="lib-table-pool-summary">
881          <td colspan="7" style="text-align: right; border-left: 0;">EB volume to add</td>
882          <td class="eb" id="pool.eb"></td>
883          <td class="remarks" id="pool.remarks"></td>
884        </tr>
885      </tbody>
886      </table>
887      <div style="margin: 1em; font-style: italic;">
888      * Low quantity = The remaining quantity of this library too low to mix to target molarity for the pool.
889      </div>
890      </div>
891     
892      <div id="not-selected" class="messagecontainer note">No libraries have been selected</div>
893    </td>
894  </tr>
895  </table>
896  </div>
897 
898  <div class="loading" id="loading" style="display: none;"><table><tr><td><img src="../images/loading.gif"></td><td id="loading.msg">Please wait...</td></tr></table></div>
899 
900  <div class="messagecontainer error" id="errorMessage" style="display: none; width: 950px; margin-left: 20px; margin-bottom: 0px;"></div>
901 
902  <div id="done" class="success" style="display: none; width: 950px; margin-left: 20px; margin-top: 20px;"></div>
903 
904  <table style="margin-left: 20px; margin-top: 10px; margin-bottom: 3em;" class="navigation">
905    <tr>
906      <td><base:button id="gocancel" title="Cancel" onclick="goRestart(false)" /></td>
907      <td><base:button id="gonext" title="Next" image="<%=home+"/images/gonext.png"%>" onclick="goNext(true)" style="display: none;" /></td>
908      <td><base:button id="gocreate" title="Create" image="<%=home+"/images/gonext.png"%>" onclick="goCreate()" /></td>
909      <td><base:button id="gorestart" title="Restart" image="<%=home+"/images/goback.png"%>" onclick="goRestart(true)" style="display: none;"/></td>
910      <td id="gonext.message" class="message"></td>
911    </tr>
912  </table>
913 
914 
915  </form>
916  </div>
917 
918</base:body>
919</base:page>
920<%
921}
922finally
923{
924  if (dc != null) dc.close();
925}
926%>
Note: See TracBrowser for help on using the repository browser.