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

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

Merging Reggie 2.15.2 to the trunk.

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