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

Last change on this file since 2188 was 2188, checked in by Nicklas Nordborg, 8 years ago

References #555: Specify amount to use from *.dil items in pool

Implemented the same functionality in the "Create manual pool" wizard. Also discovered and fixed some bugs introduced by #553 (submitting mixingStrategy and target volume as properties of the pool).

File size: 26.0 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 MAX_TARGET_VOLUME = 10;
45var MIN_TARGET_VOLUME = 2;
46
47function init()
48{
49  var frm = document.forms['reggie'];
50
51  // Load some default pooling options and the auto-generated name for the next pool
52  var response = loadPoolInfo();
53  var poolInfo = response.poolInfo;
54  TARGET_MOLARITY_IN_POOL = poolInfo.targetMolarity;
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 < MIN_TARGET_VOLUME || targetVolumePerLib > MAX_TARGET_VOLUME)
466  {
467    setInputStatus('target_volume', 'Must be between '+MIN_TARGET_VOLUME+' and '+MAX_TARGET_VOLUME+'µ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  var mixingStrategy = Forms.getCheckedRadio(frm.mixing_strategy).value;
492 
493  var pool = {};
494  pool.name = frm.poolName.value;
495  pool.comment = frm.poolComments.value;
496  pool.ebVolumeExtra = Math.max(0, poolInfo.ebVolumeExtra);
497  pool.targetVolumeInPoolPerLib = parseFloat(frm.target_volume.value);
498  pool.targetPoolMolarity = TARGET_MOLARITY_IN_POOL;
499  pool.mixingStrategy = mixingStrategy;
500  pool.libs = [];
501  pool.excluded = [];
502 
503  for (var libNo = 0; libNo < selectedLibraries.length; libNo++)
504  {
505    var lib = selectedLibraries[libNo];
506    if (lib.hasError) return;
507   
508    // We send library id, name and mixing volumes
509    if (!lib.excludeFromPool)
510    {
511      var tmp = {};
512      tmp.id = lib.id;
513      tmp.name = lib.name;
514      tmp.volume = lib.actualVolume;
515      tmp.eb = lib.actualEb;
516      tmp.mixFactor = lib.mixFactor;
517      if (lib.mixFactor > 1 && lib.targetVolume && mixingStrategy == 'dynamic')
518      {
519        tmp.targetVolume = lib.targetVolume;
520      }
521      tmp.comment = lib.comment;
522      pool.libs[pool.libs.length] = tmp;
523    }
524  }
525  submitInfo.pools[submitInfo.pools.length] = pool;
526 
527  Main.addClass(document.getElementById('step.1.section'), 'disabled');
528  Main.hide('gocancel');
529  Main.hide('gocreate');
530
531  if (debug) Main.debug(JSON.stringify(submitInfo));
532  var request = Ajax.getXmlHttpRequest();
533  try
534  {
535    showLoadingAnimation('Creating pool...');
536    var url = '../Pool.servlet?ID=<%=ID%>';
537    url += '&cmd=CreatePools';
538    request.open("POST", url, false);
539    request.setRequestHeader("Content-Type", "application/json");
540    request.send(JSON.stringify(submitInfo));
541  }
542  finally
543  {
544    hideLoadingAnimation();
545  }
546
547  if (debug) Main.debug(request.responseText);
548
549  var response = JSON.parse(request.responseText);
550  if (response.status != 'ok')
551  {
552    setFatalError(response.message);
553    return false;
554  }
555
556  var msg = '<ul>';
557  for (var i = 0; i < response.messages.length; i++)
558  {
559    msg += '<li>' + response.messages[i];
560  }
561  msg += '</ul>';
562  setInnerHTML('done', msg);
563  Main.show('done');
564  Main.show('gorestart');
565  scrollToBottom(document.getElementById('content'));
566
567}
568
569//Set a comment on the checked libraries
570var lastComment = '';
571function commentChecked()
572{
573  var frm = document.forms['reggie'];
574 
575  // Get array with checked libs
576  var checked = [];
577  var comment;
578  for (var libNo = 0; libNo < selectedLibraries.length; libNo++)
579  {
580    var lib = selectedLibraries[libNo];
581    if (frm['check.'+lib.id].checked)
582    {
583      checked[checked.length] = lib;
584      if (!comment) comment = lib.comment;
585    }
586  }
587 
588  if (checked.length == 0)
589  {
590    alert('No libraries have been selected');
591    return;
592  }
593       
594  comment = prompt('Comment', comment || lastComment);
595  if (comment == null) return;
596
597  lastComment = comment;
598 
599  if (comment == '') comment = null;
600  for (var libNo = 0; libNo < checked.length; libNo++)
601  {
602    var lib = checked[libNo];
603    lib.comment = comment;
604  }
605 
606  updatePoolData();
607}
608
609//Set target volume on the selected wells
610var lastTargetVolume = null;
611function setTargetVolume()
612{
613  var frm = document.forms['reggie'];
614  var checked = [];
615  var targetVolume;
616  for (var libNo = 0; libNo < selectedLibraries.length; libNo++)
617  {
618    var lib = selectedLibraries[libNo];
619    if (frm['check.'+lib.id].checked && lib.mixFactor > 1)
620    {
621      checked[checked.length] = lib;
622      if (!targetVolume) targetVolume = lib.targetVolume;
623    }
624  }
625 
626  if (checked.length == 0)
627  {
628    alert('No libraries with separate mix have been selected');
629    return;
630  }
631
632 
633  targetVolume = parseFloat(prompt('Volume to use in pool ('+LIMIT_FOR_EXTRA_LARGE_MIX+'--'+MAX_TARGET_VOLUME+' µl)', targetVolume || lastTargetVolume));
634  if (isNaN(targetVolume))
635  {
636    targetVolume = null;
637  }
638  else if (targetVolume > MAX_TARGET_VOLUME)
639  {
640    targetVolume = MAX_TARGET_VOLUME;
641  }
642  else if (targetVolume < LIMIT_FOR_EXTRA_LARGE_MIX)
643  {
644    targetVolume = LIMIT_FOR_EXTRA_LARGE_MIX
645  }
646  lastTargetVolume = targetVolume;
647  if (targetVolume == '') targetVolume = null;
648
649  var targetVolumePerLib = parseFloat(frm.target_volume.value);
650  var mixingStrategy = Forms.getCheckedRadio(frm.mixing_strategy).value;
651  for (var libNo = 0; libNo < checked.length; libNo++)
652  {
653    var lib = checked[libNo];
654    lib.targetVolume = targetVolume;
655    PoolMix.calculateEbVolume(lib, TARGET_MOLARITY_IN_POOL, targetVolumePerLib, mixingStrategy);
656  }
657 
658  updatePoolData();
659}
660
661
662</script>
663<style>
664
665#pool-table
666{
667  width: 100%;
668}
669
670#pool-name
671{
672  font-size: 125%;
673}
674
675#pool-table th, #pool-table td
676{
677  text-align: left;
678  padding: 2px 0.5em 2px 0.5em;
679}
680
681#lib-table table
682{
683  width: 100%;
684  border-collapse: collapse;
685}
686
687#lib-table thead, #lib-table tbody
688{
689  border-bottom: 1px solid #A0A0A0;
690}
691
692#lib-table th, #lib-table td
693{
694  text-align: left;
695  padding: 2px 0.5em 2px 0.5em;
696  border-left: 1px solid #A0A0A0;
697}
698
699#lib-table thead
700{
701  background-color: #F0F0F0;
702}
703
704#lib-table tbody#lib-table-rows tr
705{
706  border-bottom: 1px dotted #A0A0A0;
707}
708
709#lib-table tbody#lib-table-rows tr.excluded
710{
711    font-style: italic;
712    color: #666666;
713}
714
715#lib-table .check
716{
717  width: 25px;
718  text-align: center;
719  border-left: 0;
720}
721
722#lib-table .name
723{
724  width: 12em;
725}
726
727#lib-table .barcode
728{
729  width: 6em;
730}
731
732#lib-table .plate
733{
734  width: 12em;
735}
736
737#lib-table .remain
738{
739  width: 4em;
740  text-align: center;
741}
742
743
744#lib-table .molarity
745{
746  width: 4em;
747  text-align: center;
748}
749
750#lib-table .volume
751{
752  width: 4em;
753  text-align: center;
754}
755
756#lib-table .eb
757{
758  width: 4em;
759  text-align: center;
760}
761
762td.volume, span.volume
763{
764  color: #C80000;
765}
766
767td.eb, span.eb
768{
769  color: #0000C8;
770}
771
772#lib-table td.remarks
773{
774  font-style: italic;
775  background-position: 2px 50%;
776  background-repeat: no-repeat;
777}
778
779#lib-table .warning .remarks
780{
781  background-image: url('../images/warning.png');
782  padding-left: 25px;
783 
784}
785
786#lib-table .lib-error .remarks
787{
788  background-image: url('../images/error.png');
789  padding-left: 25px;
790  color: #C80000;
791 
792}
793
794
795#lib-table-pool-summary td
796{
797  font-weight: bold;
798}
799</style>
800</base:head>
801<base:body onload="init()">
802
803  <p:path><p:pathelement 
804    title="Reggie" href="<%="../index.jsp?ID="+ID%>" 
805    /><p:pathelement title="Create manual pool" 
806    /></p:path>
807
808  <div class="content" id="content">
809  <%
810  if (sc.getActiveProjectId() == 0)
811  {
812    %>
813    <div class="messagecontainer note" style="width: 950px; margin-left: 20px; margin-bottom: 20px; margin-right: 0px; font-weight: bold; color: #cc0000;">
814      No project has been selected. You may proceed with the registration but
815      created items will not be shared.
816    </div>
817    <%
818  }
819  %>
820
821  <form name="reggie" onsubmit="return false;">
822  <input type="hidden" name="poolName" value="">
823 
824  <div id="step.1.section">
825  <table class="stepform">
826  <tr>
827    <td rowspan="3" class="stepno">1</td>
828    <td class="steptitle">Select pool options and libraries</td>
829  </tr>
830  <tr>
831    <td class="stepfields">
832      <table class="bottomborder" >
833      <tr valign="top">
834        <td class="prompt">Target molarity</td>
835        <td class="input">
836          <span id="target_molarity"></span>
837        </td>
838        <td class="status"></td>
839        <td class="help"></td>
840      </tr>
841      <tr valign="top">
842        <td class="prompt">Average volume / lib</td>
843        <td class="input">
844          <input type="text" class="text" name="target_volume" 
845            value="5" style="width: 4em;"
846            onblur="targetVolumeOnChange()"
847            onkeypress="doOnEnter(event, targetVolumeOnChange); return Numbers.numberOnly(event)"> µl (2--10)
848        </td>
849        <td class="status" id="target_volume.status"></td>
850        <td class="help" rowspan="2"><span id="target_volume.message" class="message" style="display: none;"></span>
851          Select a target volume when mixing each lib to 2nM before pooling.
852          Use the <b>Dynamic</b> strategy to prioritize final pool molarity by mixing
853          different volume for each library. Use the <b>Fixed</b> strategy to mix all
854          libraries to the given volume.
855        </td>
856      </tr>
857      <tr valign="top">
858        <td class="prompt">Mixing strategy</td>
859        <td class="input">
860          <label><input type="radio" name="mixing_strategy" value="dynamic" checked
861            onclick="mixingStrategyOnChange()"
862            >Dynamic</label>
863          <label><input type="radio" name="mixing_strategy" value="fixed"
864            onclick="mixingStrategyOnChange()"
865            >Fixed</label>
866        </td>
867        <td class="status" id="mixing_strategy.status"></td>
868      </tr>
869      </table>
870     
871      <tbl:toolbar subclass="bottomborder">
872        <tbl:button 
873          title="Select libraries&hellip;" 
874          image="<%=home+"/images/new.png"%>" 
875          onclick="selectLibraries()" 
876          tooltip="Select libraries to include in the pool"
877        />
878        <tbl:button 
879          title="Remove" 
880          image="<%=home+"/images/delete.png"%>" 
881          onclick="removeSelected()" 
882          tooltip="Remove the selected libraries from the pool"
883        />
884        <tbl:button 
885          title="Separate mix volume&hellip;" 
886          image="<%=home+"/images/specimen.png"%>" 
887          onclick="setTargetVolume()" 
888          tooltip="Set volume to use in the pool for separately mixed libraries (dynamic mixing only)" 
889        />
890        <tbl:button 
891          title="Comments&hellip;" 
892          image="<%=home+"/images/comment.png"%>" 
893          onclick="commentChecked()" 
894          tooltip="Add a comment to the selected libraries" 
895        />
896      </tbl:toolbar>
897
898      <div id="pool-table" class="bottomborder">
899      <table style="width: 100%;">
900      <tr>
901        <th id="pool-name" style="width: 25%;"></th>
902        <td style="border-left: 1px solid #A0A0A0;" rowspan="2"><textarea name="poolComments" style="width: 95%; height: 3.5em;"></textarea></td>
903      </tr>
904      <tr>
905        <td style="width: 25%;" id="pool-summary">0 libs • 0.00nM • 0.0µl</td>
906      </tr>
907      </table>
908      </div>
909     
910      <div id="lib-table" style="display: none;">
911      <div style="margin: 1em;" class="messagecontainer note">
912        <base:icon image="info.png"/> 
913        <b>Separate mix:</b> <span id="num_separate_mix"></span>,
914        <b>Low quantity:</b> <span id="num_low_quantity"></span>,
915        <b>Excluded:</b> <span id="num_excluded"></span>
916      </div>
917      <table class="bottomborder topborder">
918      <thead>
919        <tr>
920          <th class="check"></th>
921          <th class="name"></th>
922          <th class="barcode"></th>
923          <th class="plate"></th>
924          <th class="molarity">Remain</th>
925          <th class="molarity">DNA</th>
926          <th class="volume">Volume</th>
927          <th class="eb">EB</th>
928          <th class="remarks"></th>
929        </tr>
930        <tr>
931          <th class="check"><base:icon image="check_uncheck.png" tooltip="Check/uncheck all" onclick="checkAll()" /></th>
932          <th class="name">Library</th>
933          <th class="barcode">Barcode</th>
934          <th class="plate">Work plate</th>
935          <th class="molarity">(µl)</th>
936          <th class="molarity">(nM)</th>
937          <th class="volume">(µl)</th>
938          <th class="eb">(µl)</th>
939          <th class="remarks">Remarks</th>
940        </tr>
941      </thead>
942      <tbody id="lib-table-rows"></tbody>
943      <tbody style="border-bottom: 0;">
944        <tr id="lib-table-pool-summary">
945          <td colspan="7" style="text-align: right; border-left: 0;">EB volume to add</td>
946          <td class="eb" id="pool.eb"></td>
947          <td class="remarks" id="pool.remarks"></td>
948        </tr>
949      </tbody>
950      </table>
951      <div style="margin: 1em; font-style: italic;">
952      * Low quantity = The remaining quantity of this library too low to mix to target molarity for the pool.
953      </div>
954      </div>
955     
956      <div id="not-selected" class="messagecontainer note">No libraries have been selected</div>
957    </td>
958  </tr>
959  </table>
960  </div>
961 
962  <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>
963 
964  <div class="messagecontainer error" id="errorMessage" style="display: none; width: 950px; margin-left: 20px; margin-bottom: 0px;"></div>
965 
966  <div id="done" class="success" style="display: none; width: 950px; margin-left: 20px; margin-top: 20px;"></div>
967 
968  <table style="margin-left: 20px; margin-top: 10px; margin-bottom: 3em;" class="navigation">
969    <tr>
970      <td><base:button id="gocancel" title="Cancel" onclick="goRestart(false)" /></td>
971      <td><base:button id="gonext" title="Next" image="<%=home+"/images/gonext.png"%>" onclick="goNext(true)" style="display: none;" /></td>
972      <td><base:button id="gocreate" title="Create" image="<%=home+"/images/gonext.png"%>" onclick="goCreate()" /></td>
973      <td><base:button id="gorestart" title="Restart" image="<%=home+"/images/goback.png"%>" onclick="goRestart(true)" style="display: none;"/></td>
974      <td id="gonext.message" class="message"></td>
975    </tr>
976  </table>
977 
978 
979  </form>
980  </div>
981 
982</base:body>
983</base:page>
984<%
985}
986finally
987{
988  if (dc != null) dc.close();
989}
990%>
Note: See TracBrowser for help on using the repository browser.