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

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

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

The volume to use in the pool can now be set by selecting the wells and clicking the 'Separate mix volume' button.

As a side-effect the mix factor is now calculated dynamically so that around 2µl is used from the original lib. The previous implementation with a fixed mix factor could still yield lower than 1µl for libraries with very high contentration (>20nM).

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