source: extensions/net.sf.basedb.reggie/branches/2.15-stable/resources/libprep/create_pools.jsp @ 2450

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

References #597: Display adapter peak fraction in the pooling wizard

Replaced the barcode value with the adapter fraction value in the plate pooling wizard. The manual pooling wizard simply got an extra column.

A warning message and icon is displayed if the fraction is unknown or higher than 10%.

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.