source: extensions/net.sf.basedb.reggie/trunk/resources/libprep/create_pools.jsp @ 2184

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

Fixes #553: Set different volume and mixing strategy for pools on a plate

Added a section in the plate view so that the "Average volume/lib" and "Mixing strategy" can be selected for each pool. Only minor changes required in the protocol generation since most of it was already working per pool.

File size: 28.4 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  <link rel="stylesheet" type="text/css" href="../css/plate.css">
30  <script language="JavaScript" src="../reggie.js" type="text/javascript" charset="UTF-8"></script>
31  <script language="JavaScript" src="pools.js" type="text/javascript" charset="UTF-8"></script>
32  <script language="JavaScript" src="plate.js" type="text/javascript" charset="UTF-8"></script>
33
34<script language="JavaScript">
35var debug = 0;
36var currentStep = 1;
37
38var targetVolumePerLibIsValid = [];
39var POOL_DATA = [];
40
41// Loaded from servlet when getting Library information
42var TARGET_MOLARITY_IN_POOL;
43var LIMIT_FOR_AUTO_EXCLUDE = 0.25;
44var LIMIT_FOR_EXTRA_LARGE_MIX;
45var EXTRA_LARGE_MIX_FACTOR;
46
47function init()
48{
49  var frm = document.forms['reggie'];
50  var bioplates = getLibraryBioPlates();
51 
52  var plates = frm.bioplate;
53  if (bioplates != null && bioplates.length > 0)
54  {
55    for (var i=0; i < bioplates.length; i++)
56    {
57      var bioplate = bioplates[i];
58      var option = new Option(bioplate.name, bioplate.id);
59      option.bioplate = bioplate;
60      plates.options[plates.length] = option;
61    }
62    bioplateIsValid = true;
63    setInputStatus('bioplate', '', 'valid');
64  }
65  else
66  {
67    var msg = 'No Library bioplates available for processing.';
68    setFatalError(msg);
69    return;
70  }
71}
72
73
74function getLibraryBioPlates()
75{
76  var frm = document.forms['reggie']; 
77 
78  var request = Ajax.getXmlHttpRequest();
79  try
80  {
81    showLoadingAnimation('Loading Library bioplates...');
82    var url = '../Pool.servlet?ID=<%=ID%>&cmd=GetLibraryPlatesForPooling';
83    request.open("GET", url, false); 
84    request.send(null);
85  }
86  finally
87  {
88    hideLoadingAnimation();
89  }
90 
91  if (debug) Main.debug(request.responseText);
92  var response = JSON.parse(request.responseText); 
93  if (response.status != 'ok')
94  {
95    setFatalError(response.message);
96    return false;
97  }
98  return response.bioplates;
99}
100
101function createManualPool()
102{
103  location.replace('create_manual_pool.jsp?ID=<%=ID%>');
104}
105
106function goNext(manual)
107{
108  setInnerHTML('gonext.message', '');
109  if (currentStep == 1)
110  {   
111    gotoStep2();
112  }
113  setCurrentStep(currentStep);
114}
115
116function gotoStep2()
117{
118  var frm = document.forms['reggie'];
119  frm.bioplate.disabled = true;
120  Main.addClass(document.getElementById('step.1.section'), 'disabled');
121 
122  loadPoolNames();
123  loadLibraryInformation();
124
125  currentStep = 2;
126  Main.hide('step.1.section');
127  Main.show('step.2.section');
128  Main.hide('gonext');
129  Main.show('gocancel');
130  Main.show('gocreate');
131}
132
133function goCreate()
134{
135 
136  var submitInfo = {};
137  submitInfo.pools = [];
138  submitInfo.flagged = [];
139  var frm = document.forms['reggie'];
140  var libPlate = frm.bioplate[frm.bioplate.selectedIndex].bioplate;
141
142  var numPools = Plate.getPools();
143  for (var poolNo = 0; poolNo < numPools; poolNo++)
144  {
145    if (!targetVolumePerLibIsValid[poolNo]) return;
146
147    var wells = Plate.getPool(poolNo);
148   
149    var poolInfo = {};
150    poolInfo.name = POOL_NAMES[poolNo];
151    poolInfo.targetPoolMolarity = TARGET_MOLARITY_IN_POOL;
152    poolInfo.targetVolumeInPoolPerLib = parseFloat(frm['target_volume.'+poolNo].value);
153    poolInfo.mixingStrategy = Forms.getCheckedRadio(frm['mixing_strategy.'+poolNo]).value;
154    poolInfo.comment = frm['comment.'+poolNo].value;
155    poolInfo.ebVolumeExtra = POOL_DATA[poolNo].ebVolumeExtra;
156    poolInfo.libPlate = libPlate.id;
157    poolInfo.libs = [];
158    poolInfo.excluded = [];
159   
160    for (var wellNo = 0; wellNo < wells.length; wellNo++)
161    {
162      var lib = wells[wellNo].extract;
163      if (lib)
164      {
165        // We send library id, name and mixin volumes
166        var tmp = {};
167        tmp.id = lib.id;
168        tmp.name = lib.name;
169        tmp.volume = lib.actualVolume; 
170        tmp.eb = lib.actualEb;
171        tmp.mixFactor = lib.mixFactor;
172        tmp.comment = lib.comment;
173       
174        if (lib.excludeFromPool)
175        {
176          poolInfo.excluded[poolInfo.excluded.length] = tmp;
177        }
178        else
179        {
180          poolInfo.libs[poolInfo.libs.length] = tmp;
181        }
182       
183        if (lib.flag)
184        {
185          tmp.flag = lib.flag;
186          submitInfo.flagged[submitInfo.flagged.length] = tmp;
187        }
188      }
189    }
190   
191    submitInfo.pools[submitInfo.pools.length] = poolInfo;
192  }
193 
194  Main.addClass(document.getElementById('step.2.section'), 'disabled');
195  Main.hide('gocancel');
196  Main.hide('gocreate');
197
198  if (debug) Main.debug(JSON.stringify(submitInfo));
199  var request = Ajax.getXmlHttpRequest();
200  try
201  {
202    showLoadingAnimation('Creating pools...');
203    var url = '../Pool.servlet?ID=<%=ID%>';
204    url += '&cmd=CreatePools';
205    request.open("POST", url, false);
206    request.setRequestHeader("Content-Type", "application/json");
207    request.send(JSON.stringify(submitInfo));
208  }
209  finally
210  {
211    hideLoadingAnimation();
212  }
213
214  if (debug) Main.debug(request.responseText);
215
216  var response = JSON.parse(request.responseText);
217  if (response.status != 'ok')
218  {
219    setFatalError(response.message);
220    return false;
221  }
222
223  var msg = '<ul>';
224  for (var i = 0; i < response.messages.length; i++)
225  {
226    msg += '<li>' + response.messages[i];
227  }
228  msg += '</ul>';
229  setInnerHTML('done', msg);
230  Main.show('done');
231  Main.show('gorestart');
232  scrollToBottom(document.getElementById('content'));
233
234}
235
236function loadLibraryInformation()
237{
238  var frm = document.forms['reggie'];
239  var libPlate = frm.bioplate[frm.bioplate.selectedIndex].bioplate;
240
241  // Load Libraries and related info from the selected bioplate
242  var request = Ajax.getXmlHttpRequest();
243  try
244  {
245    showLoadingAnimation('Loading information about libraries...');
246    var url = '../Pool.servlet?ID=<%=ID%>&cmd=GetLibraryInfoForPlate&bioplate='+libPlate.id;   
247    request.open("GET", url, false);
248    request.send(null);
249  }
250  finally
251  {
252    hideLoadingAnimation();
253  }
254 
255  if (debug) Main.debug(request.responseText);
256  var response = JSON.parse(request.responseText);
257  if (response.status != 'ok')
258  {
259    setFatalError(response.message);
260    return false;
261  }
262
263 
264  var schema = PoolSchema.getById(libPlate.poolSchema);
265  var barcodeVariant = PoolSchema.getBarcodeVariantByName(schema, libPlate.barcodeVariant);
266  Plate.setPoolSchema(schema);
267  WellPainter.barcodeVariant = barcodeVariant;
268  Plate.init(8, 12, schema, WellPainter);
269
270  var poolInfo = response.poolInfo;
271  TARGET_MOLARITY_IN_POOL = poolInfo.targetMolarity;
272  EXTRA_LARGE_MIX_FACTOR = poolInfo.extraLargeMixFactor;
273  LIMIT_FOR_EXTRA_LARGE_MIX = poolInfo.limitForExtraLargeMix;
274 
275  //setInnerHTML('pool_schema', schema.name);
276  //setInnerHTML('barcode_variant', barcodeVariant.name);
277  setInnerHTML('target_molarity', Numbers.formatNumber(TARGET_MOLARITY_IN_POOL, 1, ' nM'));
278
279  var libs = response.libraries;
280  var molarityLimit = TARGET_MOLARITY_IN_POOL * LIMIT_FOR_AUTO_EXCLUDE;
281  for (var i = 0; i < libs.length; i++)
282  {
283    var lib = libs[i];
284    var well = lib.bioWell;
285    lib.excludeFromPool = false;
286   
287    if (lib.molarity != null && lib.molarity < molarityLimit)
288    {
289      lib.excludeFromPool = true;
290      lib.flag = 'ExcludedFromPool';
291      lib.comment = 'Not enough DNA';
292    }
293
294    Plate.getWell(well.row, well.column).setExtract(lib);
295  }
296
297  PoolSchema.buildPoolTableRow(schema, 12);
298  initPoolData(poolInfo.targetVolumePerLib);
299  updatePoolData();
300}
301
302function loadPoolNames()
303{
304  var frm = document.forms['reggie'];
305  var libPlate = frm.bioplate[frm.bioplate.selectedIndex].bioplate;
306
307  var schema = PoolSchema.getById(libPlate.poolSchema);
308
309  // Load Libraries and related info from the selected bioplate
310  var request = Ajax.getXmlHttpRequest();
311  try
312  {
313    showLoadingAnimation('Loading Library bioplate information...');
314    var url = '../Pool.servlet?ID=<%=ID%>&cmd=GetNextAutoGeneratedPoolNames&numNames='+schema.numPools;   
315    request.open("GET", url, false);
316    request.send(null);
317  }
318  finally
319  {
320    hideLoadingAnimation();
321  }
322 
323  if (debug) Main.debug(request.responseText);
324  var response = JSON.parse(request.responseText);
325  if (response.status != 'ok')
326  {
327    setFatalError(response.message);
328    return false;
329  }
330 
331  var list = response.names;
332  for (var i = 0; i < list.length; i++)
333  {
334    POOL_NAMES[i] = list[i];
335  }
336
337}
338
339function initPoolData(targetVolumePerLib)
340{
341  for (var poolNo = 0; poolNo < Plate.getPools(); poolNo++)
342  {
343    var poolDiv = document.getElementById('pool.'+poolNo);
344    var html = '<div class="pool-name">'+POOL_NAMES[poolNo]+'</div>';
345    html += '<div class="pool-data">';
346    html += '<table>';
347    html += '<tr><td class="prompt">Volume/lib</td>';
348    html += '<td><input type="text" style="width: 4em;" name="target_volume.'+poolNo+'" value="'+targetVolumePerLib+'"';
349    html += ' onblur="targetVolumeOnBlur(event)" onkeypress="doOnEnter(event, targetVolumeOnBlur); return Numbers.numberOnly(event)">';
350    html += 'µl (2--10)</td>';
351    html += '<td id="target_volume.'+poolNo+'.status"></td></tr>';
352    html += '<tr><td class="prompt">Mixing strategy</td>';
353    html += '<td colspan="2">';
354    html += '<label><input type="radio" name="mixing_strategy.'+poolNo+'" value="dynamic" checked';
355    html += ' onclick="mixingStrategyOnChange(event)">Dynamic</label> ';
356    html += '<label><input type="radio" name="mixing_strategy.'+poolNo+'" value="fixed"';
357    html += ' onclick="mixingStrategyOnChange(event)">Fixed</label>';
358    html += '</td></tr>';
359    html += '</table>';
360   
361    html += '<textarea name="comment.'+poolNo+'"></textarea>';
362    html += '</div>';
363    html += '<div class="pool-summary" id="pool-summary.'+poolNo+'"></div>';
364    poolDiv.innerHTML = html;
365    setTargetVolumePerLib(poolNo, targetVolumePerLib);
366  }
367 
368}
369
370
371function setTargetVolumePerLib(poolNo, targetVolumePerLib)
372{
373  var frm = document.forms['reggie'];
374  if (targetVolumePerLib)
375  {
376    frm['target_volume.'+poolNo].value = targetVolumePerLib;
377  }
378  else
379  {
380    targetVolumePerLib = parseInt(frm['target_volume.'+poolNo].value);
381  }
382 
383  targetVolumePerLibIsValid[poolNo] = false;
384  if (targetVolumePerLib < 2 || targetVolumePerLib > 10)
385  {
386    setInputStatus('target_volume.'+poolNo, 'Must be between 2 and 10µl', 'invalid');
387    return;
388  }
389  setInputStatus('target_volume.'+poolNo, '', 'valid');
390  targetVolumePerLibIsValid[poolNo] = true;
391
392  // Pre-process the Library items
393  var mixingStrategy = Forms.getCheckedRadio(frm['mixing_strategy.'+poolNo]).value;
394  var wells = Plate.getPool(poolNo);
395  for (var i = 0; i < wells.length; i++)
396  {
397    var lib = wells[i].extract;
398    if (lib)
399    {
400      if (lib.remainingQuantity == null)
401      {
402        lib.excludeFromPool = true;
403      }
404      else
405      {
406        PoolMix.calculateLibVolume(lib, TARGET_MOLARITY_IN_POOL, targetVolumePerLib, mixingStrategy);
407        PoolMix.calculateEbVolume(lib, TARGET_MOLARITY_IN_POOL, targetVolumePerLib, mixingStrategy);
408      }
409    }
410  }
411 
412}
413
414function calculateRemarks(lib, schema, barcodeVariant)
415{
416  var well = lib.bioWell;
417  var remarks = [];
418
419  // Check if default barcode has been modified
420  var indexSet = barcodeVariant.indexSets[well.column];
421  if (indexSet)
422  {
423    var defaultBarcode = indexSet.barcodes[well.row];
424    if (defaultBarcode && lib.barcode.name != defaultBarcode)
425    {
426      remarks[remarks.length] = 'Modified barcode';
427      lib.barcode.modified = true;
428    }
429  }
430 
431  if (lib.molarity != null)
432  {
433    if (lib.volume != lib.actualVolume)
434    {
435      if (lib.volume > lib.remainingVolume)
436      {
437        remarks[remarks.length] = 'Low quantity';
438      }
439      else
440      {
441        remarks[remarks.length] = 'Low molarity';
442      }
443    }
444
445    if (lib.speedVacConc != null)
446    {
447      remarks[remarks.length] = 'SpeedVac';
448    }
449   
450    if (lib.mixFactor > 1)
451    {
452      // Larger mix than default
453      remarks[remarks.length] = 'Use ' + Numbers.formatNumber(lib.actualVolume+lib.actualEb, 1, 'µl') + ' in pool';
454    }
455   
456  }
457  lib.stratagene = lib.name.indexOf(GENERIC_STRATAGENE_NAME) == 0;
458  lib.external = lib.name.indexOf(EXTERNAL_RNA_NAME) == 0;
459  lib.remarks = remarks;
460}
461
462
463function updatePoolData()
464{
465  var frm = document.forms['reggie'];
466
467  var libPlate = frm.bioplate[frm.bioplate.selectedIndex].bioplate;
468  var schema = PoolSchema.getById(libPlate.poolSchema);
469  var barcodeVariant = PoolSchema.getBarcodeVariantByName(schema, libPlate.barcodeVariant);
470
471  for (var poolNo = 0; poolNo < Plate.getPools(); poolNo++)
472  {
473    var numSeparateMix = 0;
474    var numLowQuantity = 0;
475    var numExcluded = 0;
476   
477    // Get all libs into an array
478    var libs = [];
479    var wells = Plate.getPool(poolNo);
480    for (var wellNo = 0; wellNo < wells.length; wellNo++)
481    {
482      var well = wells[wellNo];
483      var lib = well.extract;
484      if (lib)
485      {
486        if (lib.excludeFromPool)
487        {
488          numExcluded++;
489        }
490        else
491        {
492          libs[libs.length] = lib;
493        }
494      }
495    }
496   
497    // Calculate final pool volumes, molarity, etc.
498    var targetVolumePerLib = parseFloat(frm['target_volume.'+poolNo].value);
499    var mixingStrategy = Forms.getCheckedRadio(frm['mixing_strategy.'+poolNo]).value;
500    var poolInfo = PoolMix.calculateFinalPoolInfo(libs, TARGET_MOLARITY_IN_POOL, targetVolumePerLib, mixingStrategy);
501    POOL_DATA[poolNo] = poolInfo;
502   
503    // Make remarks for each lib
504    for (var libNo = 0; libNo < libs.length; libNo++)
505    {
506      var lib = libs[libNo];
507      calculateRemarks(lib, schema, barcodeVariant);
508      if (lib.eb != 0 || mixingStrategy == 'fixed') numSeparateMix++;
509      if (lib.volume > lib.actualVolume) numLowQuantity++;
510    }
511   
512    WellPainter.mixingStrategy = mixingStrategy;
513    Plate.paint(wells);
514
515    var warnMsg = null;
516    if (poolInfo.ebVolumeExtra < 0) 
517    {
518      warnMsg = 'Too many libs with low molarity';
519    }
520   
521    var poolDiv = document.getElementById('pool.'+poolNo);
522    var summaryDiv = document.getElementById('pool-summary.'+poolNo);
523    var poolData = '';
524    poolData += libs.length + ' libs  ';
525    poolData += Numbers.formatNumber(poolInfo.molarity, 2, 'nM')+'  ';
526    poolData += Numbers.formatNumber(poolInfo.totalVolume, 1, 'µl');
527    if (mixingStrategy == 'dynamic')
528    {
529      poolData += '  <span class="pool-eb">'+Numbers.formatNumber(poolInfo.ebVolumeExtra, 1, 'µl')+'</span>';
530    }
531    if (numSeparateMix > 0 || numLowQuantity)
532    {
533      poolData += '<br>Separate mix: ' + numSeparateMix;
534      poolData += ' • Low quantity: ' + numLowQuantity;
535      poolData += '';
536    }
537   
538    if (warnMsg)
539    {
540      Main.addClass(poolDiv, 'warning');
541      poolData += '<div class="warning-message">'+warnMsg+'</div>';
542    }
543    else
544    {
545      Main.removeClass(poolDiv, 'warning');
546    }
547    summaryDiv.innerHTML = poolData;
548  }
549 
550}
551
552function targetVolumeOnBlur(event)
553{
554  var frm = document.forms['reggie'];
555  var poolNo = parseInt(event.target.name.substr(-1));
556  setTargetVolumePerLib(poolNo);
557  updatePoolData();
558}
559
560function mixingStrategyOnChange(event)
561{
562  targetVolumeOnBlur(event);
563}
564
565var WellPainter = function()
566{
567  var painter = {};
568 
569  painter.getClassNameForWell = function(well, schema)
570  {
571    var indexSet = painter.barcodeVariant.indexSets[well.column];
572    var cls = '';
573    var lib = well.extract;
574   
575    if (lib)
576    {
577      if (lib.excludeFromPool)
578      {
579        cls += ' exclude-from-pool';
580      }
581      else if (indexSet)
582      {
583        cls += ' ' + (lib && lib.barcode.modified ? 'bg-modified' : indexSet.color);
584      }
585      if (lib.flag)
586      {
587        cls += ' flagged';
588      }
589      if (!lib.flag && lib.actualVolume < lib.volume)
590      {
591        cls += ' low-volume';
592      }
593    }
594    else if (indexSet)
595    {
596      cls += indexSet.color;
597    }
598    return cls;
599  }
600 
601  painter.getWellText = function(well, schema)
602  {
603    var text = '';
604    var lib = well.extract;
605    if (lib)
606    {
607      var name = lib.name;
608      var i = name.indexOf('.m');
609      var mixFactor = lib.mixFactor;
610      var displayName = name.substring(0, i)+'.<br>&nbsp;'+name.substring(i);
611      text += '<div class="lib">'+displayName+'</div>';
612      text += '<div>';
613      text += '<span class="barcode">'+lib.barcode.name+'</span>';
614      if (lib.molarity != null)
615      {
616        text += '<span class="molarity">'+Numbers.formatNumber(lib.molarity, 2, 'nM')+'</span>';
617      }
618      text += '</div>';
619      if (!lib.excludeFromPool)
620      {
621        mark = (lib.volume > lib.actualVolume) ? '*' : '';
622        text += '<div>';
623        text += '<span class="volume">'+(isFinite(lib.volume) ? Numbers.formatNumber(lib.volume*mixFactor, 1, 'µl'+mark) : '∞')+'</span>';
624        if (painter.mixingStrategy == 'fixed' || lib.mixFactor > 1)
625        {
626          text += '<span class="eb">'+(isFinite(lib.eb) ? Numbers.formatNumber(lib.eb*mixFactor, 1, 'µl') : '-∞')+'</span>';
627        }
628        else
629        {
630          text += '<span class="eb"></span>';
631        }
632        text += '</div>';
633      }
634      if (lib.remarks) text += '<div class="remarks">'+ lib.remarks.join('; ') + '</div>';
635      if (lib.comment)
636      {
637        text += '<div class="remarks">'+ lib.comment + '</div>';
638      }
639    }
640    return text;
641  }
642
643  return painter;
644}();
645
646//Toggle the selected status of a single well
647function toggleWell(row, column)
648{
649  var well = Plate.getWell(row, column);
650  Plate.toggleSelected([well]);
651}
652
653var lastComment = '';
654function excludeSelected()
655{
656  var wells = Plate.getSelected();
657 
658  if (wells.length == 0)
659  {
660    alert('No wells have been selected');
661    return;
662  }
663
664  // See if there is an existing comment
665  var comment;
666  for (var i = 0; i < wells.length; i++)
667  {
668    var lib = wells[i].extract;
669    if (lib && lib.comment) 
670    {
671      comment = lib.comment;
672    }
673  }
674
675  comment = prompt('Comment on reason for exclusion', comment || lastComment);
676  if (comment == null) return;
677  lastComment = comment;
678 
679  if (comment == '') comment = null;
680 
681  for (var i = 0; i < wells.length; i++)
682  {
683    var well = wells[i];
684    var lib = well.extract;
685    if (lib)
686    {
687      lib.excludeFromPool = true;
688      if (!lib.stratagene && !lib.external)
689      {
690        lib.flag = 'ExcludedFromPool';
691        lib.comment = comment;
692      }
693    }
694    well.selected = false;
695  }
696 
697  updatePoolData();
698  Plate.paint(Plate.getWells());
699}
700
701function includeSelected()
702{
703  var wells = Plate.getSelected();
704 
705  if (wells.length == 0)
706  {
707    alert('No wells have been selected');
708    return;
709  }
710
711  for (var i = 0; i < wells.length; i++)
712  {
713    var well = wells[i];
714    var lib = well.extract;
715    if (lib)
716    {
717      lib.excludeFromPool = false;
718      lib.flag = null;
719      lib.oldComment = lib.comment;
720      lib.comment = null;
721    }
722    well.selected = false;
723  }
724 
725  updatePoolData();
726  Plate.paint(Plate.getWells());
727}
728
729//Flag the selected RNA
730function toggleFlag()
731{
732  var wells = Plate.getSelected();
733 
734  if (wells.length == 0)
735  {
736    alert('No wells have been selected');
737    return;
738  }
739
740  var flag = null;
741  var firstLib = true;
742  for (var i = 0; i < wells.length; i++)
743  {
744    var well = wells[i];
745    var lib = well.extract;
746    if (lib)
747    {
748      if (firstLib && !lib.excludeFromPool)
749      {
750        flag = lib.flag ? null : 'ManualFlag';
751        firstLib = false;
752      }
753      lib.flag = lib.excludeFromPool ? 'ExcludedFromPool' : flag;
754    }
755    //well.selected = false;
756  }
757 
758  Plate.paint(wells);
759}
760
761//Set a comment on the selected wells
762function commentSelected(comment)
763{
764  var wells = Plate.getSelected();
765 
766  if (comment == undefined)
767  {
768    if (wells.length == 0)
769    {
770      alert('No wells have been selected');
771      return;
772    }
773   
774    var count = 0;
775    for (var i = 0; i < wells.length; i++)
776    {
777      var well = wells[i];
778      if (well.extract) 
779      {
780        if (well.extract.comment) comment = well.extract.comment;
781        count++;
782      }
783    }
784   
785    if (count == 0)
786    {
787      alert('Only empty wells have been selected');
788      return;
789    }
790   
791    comment = prompt('Comment', comment || lastComment);
792    if (comment == null) return;
793  }
794 
795  lastComment = comment;
796 
797  if (comment == '') comment = null;
798  for (var i = 0; i < wells.length; i++)
799  {
800    var well = wells[i];
801    if (well.extract) 
802    {
803      well.extract.comment = comment;
804    }
805    well.selected = false;
806  }
807  Plate.paint(wells);
808}
809
810
811</script>
812<style>
813.lib
814{
815  font-weight: bold;
816  margin-bottom: 0.25em;
817  overflow: hidden;
818  text-overflow: ellipsis;
819}
820.barcode
821{}
822.molarity
823{
824  float: right;
825}
826
827.volume
828{
829  color: #C80000;
830}
831.eb
832{
833  color: #0000C8;
834  float: right;
835}
836.pool-eb
837{
838  color: #0000C8;
839}
840.remarks
841{
842  color: #C80000;
843  font-style: italic;
844}
845
846#pool-row th
847{
848  border-top: 0;
849  border-bottom: 1px solid #808080;
850  vertical-align: top;
851  padding: 0;
852}
853
854.pool.warning
855{
856  background-image: url('../images/warning.png');
857  background-position: 2% 98%;
858  background-repeat: no-repeat;
859}
860
861.pool-name
862{
863  padding: 2px;
864}
865
866.pool-data
867{
868  font-weight: normal;
869  padding: 2px;
870  border-top: 1px solid #808080;
871  border-bottom: 1px solid #808080;
872  background-color: #FFFFFF;
873}
874
875.pool-data td
876{
877  text-align: left;
878  white-space: nowrap;
879}
880
881.pool-data td.prompt
882{
883  width: 30%;
884}
885
886.pool-data textarea
887{
888  width: 95%;
889  height: 3.5em;
890}
891
892
893.pool-summary
894{
895  font-weight: normal;
896  padding: 2px;
897  font-style: italic;
898}
899
900.pool-summary .warning-message
901{
902  color: #C80000;
903}
904
905.well.exclude-from-pool
906{
907  background-color: #FFFFFF !important;
908}
909
910.well.exclude-from-pool.selected
911{
912  background-color: #D8D8D8 !important;
913}
914
915.well.exclude-from-pool *
916{
917  color: #999999 !important;
918}
919.well.flagged
920{
921  background-image: url('../images/flag.png');
922  background-position: 98% 5%;
923  background-repeat: no-repeat;
924}
925
926.well.low-volume
927{
928  background-image: url('../images/warning.png') !important;
929  background-position: 98% 5%;
930  background-repeat: no-repeat;
931}
932</style>
933</base:head>
934<base:body onload="init()">
935
936  <p:path><p:pathelement 
937    title="Reggie" href="<%="../index.jsp?ID="+ID%>" 
938    /><p:pathelement title="Create pooled libraries" 
939    /></p:path>
940
941  <div class="content" id="content">
942  <%
943  if (sc.getActiveProjectId() == 0)
944  {
945    %>
946    <div class="messagecontainer note" style="width: 950px; margin-left: 20px; margin-bottom: 20px; margin-right: 0px; font-weight: bold; color: #cc0000;">
947      No project has been selected. You may proceed with the registration but
948      created items will not be shared.
949    </div>
950    <%
951  }
952  %>
953
954  <div class="allsteps">
955    <div class="step current" id="step.1">1</div>
956    ›
957    <div class="step future" id="step.2">2</div>
958  </div>
959
960  <form name="reggie" onsubmit="return false;">
961 
962  <div id="step.1.section">
963  <table class="stepform">
964  <tr>
965    <td rowspan="3" class="stepno">1</td>
966    <td class="steptitle">Select source for pool</td>
967  </tr>
968  <tr>
969    <td class="stepfields">
970      <table>
971      <tr valign="top">
972        <td class="prompt">Library bioplate</td>
973        <td class="input"><select style="width:90%" 
974            name="bioplate" id="bioplate" onchange="bioPlateOnChange()"></select>
975        </td>
976        <td class="status" id="bioplate.status"></td>
977        <td class="help"><span id="bioplate.message" class="message" style="display: none;"></span>
978          Select an existing Library bioplate. The list contain all Library bioplates that
979          contain libraries with no child items.
980        </td>
981      </tr>
982      <tr valign="top">
983        <td class="prompt"></td>
984        <td class="input"> - or -</td>
985        <td class="status"></td>
986        <td class="help"></td>
987      </tr>
988      <tr valign="top">
989        <td class="prompt"></td>
990        <td class="input">
991          <table><tr><td>
992            <base:button title="Create manual pool" onclick="createManualPool()" image="<%=home+"/images/gonext.png"%>" />
993          </td></tr></table>
994       
995        </td>
996        <td class="status"></td>
997        <td class="help"></td>
998      </tr>
999      </table>
1000    </td>
1001  </tr>
1002  </table>
1003  </div>
1004 
1005  <div id="step.2.section" style="display: none;">
1006  <table class="stepform" style="width: auto;">
1007  <tr>
1008    <td rowspan="2" class="stepno">2</td>
1009    <td class="steptitle">Calculate pool mixing volumes</td>
1010  </tr>
1011  <tr>
1012    <td class="stepfields">
1013
1014      <table class="bottomborder" style="width: 100%; display: none;">
1015      <tr valign="top">
1016        <td class="prompt">Pool layout</td>
1017        <td class="input">
1018          <span id="pool_schema"></span>; <span id="barcode_variant"></span>
1019        </td>
1020        <td class="status"></td>
1021        <td class="help" rowspan="2">
1022        </td>
1023      </tr>
1024      <tr valign="top">
1025        <td class="prompt">Target molarity</td>
1026        <td class="input">
1027          <span id="target_molarity2"></span>
1028        </td>
1029        <td class="status"></td>
1030      </tr>
1031      </table>
1032      <tbl:toolbar subclass="bottomborder">
1033        <tbl:button 
1034          title="Exclude from pool&hellip;" 
1035          image="<%=home+"/images/delete.png"%>" 
1036          onclick="excludeSelected()" 
1037          tooltip="Exclude the selected libraries from the pool"
1038        />
1039        <tbl:button 
1040          title="Include in pool" 
1041          image="<%=home+"/images/new.png"%>" 
1042          onclick="includeSelected()" 
1043          tooltip="Include the selected libraries in the pool"
1044        />
1045        <tbl:button 
1046          title="Toggle flag" 
1047          image="<%=home+"/images/flag.png"%>" 
1048          onclick="toggleFlag()" 
1049          tooltip="Flag the parent RNA for the selected libraries"
1050        />
1051        <tbl:button 
1052          title="Comments&hellip;" 
1053          image="<%=home+"/images/comment.png"%>" 
1054          onclick="commentSelected()" 
1055          tooltip="Add a comment to the selected libraries" 
1056        />
1057      </tbl:toolbar>
1058       
1059        <div class="messagecontainer note" style="margin: 1em auto 1em auto; width: 75%; ">
1060          Select a target volume when mixing the libraries to a pool with <span id="target_molarity"></span> 
1061          concentration.
1062          Use the <b>Dynamic strategy</b> to prioritize final pool molarity by mixing
1063          different volumes for each library. Use the <b>Fixed strategy</b> to mix all
1064          libraries to the given volume. For each pool a summary is calculated:
1065          <i>Number of libraries</i> • <i>Final molarity</i> • <i>Total volume</i> • <i>EB volume (dynamic mixing only)</i>
1066        </div>
1067     
1068      <table class="plate" style="margin: 1em 1em 1em 1em;" id="plate">
1069      <tr id="pool-row">
1070        <th colspan="13">&nbsp;</th>
1071      </tr>
1072      <%
1073      int columns = 12;
1074      int rows = 8;
1075      WellCoordinateFormatter rowF = new WellCoordinateFormatter(true);
1076      WellCoordinateFormatter colF = new WellCoordinateFormatter(false);
1077      %>
1078      <tr class="header">
1079        <th></th>
1080        <%
1081        for (int c = 0; c < columns; ++c)
1082        {
1083          %>
1084          <th id="col.<%=c%>"><%=colF.format(c)%></th>
1085          <%
1086        }
1087        %>
1088      </tr>
1089      <tbody>
1090      <%
1091      for (int r = 0; r < rows; ++r)
1092      {
1093        String row = rowF.format(r);
1094        %>
1095        <tr class="row-<%=r%>">
1096          <th id="row.<%=r%>" class="rowheader"><%=row%></th>
1097          <%
1098          for (int c = 0; c < columns; ++c)
1099          {
1100            %>
1101            <td class="well col-<%=c%>" id="well.<%=r%>.<%=c%>"
1102              onclick="toggleWell(<%=r%>,<%=c%>)"
1103              title="Select/deselect this well"
1104              ></td>
1105            <%
1106          }
1107          %>
1108        </tr>
1109        <%
1110      }
1111      %>
1112      </tbody>
1113      </table>
1114      <div style="margin: 1em;">
1115      Low molarity = The concentration of this library too low to mix to target molarity for the pool.<br>
1116      </div>
1117    </td>
1118  </tr>
1119  </table>
1120  </div>
1121 
1122  <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>
1123 
1124  <div class="messagecontainer error" id="errorMessage" style="display: none; width: 950px; margin-left: 20px; margin-bottom: 0px;"></div>
1125 
1126  <div id="done" class="success" style="display: none; width: 950px; margin-left: 20px; margin-top: 20px;"></div>
1127 
1128  <table style="margin-left: 20px; margin-top: 10px; margin-bottom: 3em;" class="navigation">
1129    <tr>
1130      <td><base:button id="gocancel" title="Cancel" onclick="goRestart(false)" /></td>
1131      <td><base:button id="gonext" title="Next" image="<%=home+"/images/gonext.png"%>" onclick="goNext(true)"/></td>
1132      <td><base:button id="gocreate" title="Create" image="<%=home+"/images/gonext.png"%>" onclick="goCreate()" style="display: none;"/></td>
1133      <td><base:button id="gorestart" title="Restart" image="<%=home+"/images/goback.png"%>" onclick="goRestart(true)" style="display: none;"/></td>
1134      <td id="gonext.message" class="message"></td>
1135    </tr>
1136  </table>
1137 
1138 
1139  </form>
1140  </div>
1141 
1142</base:body>
1143</base:page>
1144<%
1145}
1146finally
1147{
1148  if (dc != null) dc.close();
1149}
1150%>
Note: See TracBrowser for help on using the repository browser.