source: extensions/net.sf.basedb.reggie/trunk/resources/libprep/create_flowcells.jsp @ 2028

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

References #502: Design flow cells

Added "ReadString?" field and cleaned up code somewhat. The default volume to use from the pool when clustering should now be easier to find (as a javascript constant defined near the top of the page).

File size: 19.3 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.clients.web.Base" 
9  import="net.sf.basedb.clients.web.util.HTML" 
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<%
15final SessionControl sc = Base.getExistingSessionControl(request, true);
16final String ID = sc.getId();
17final float scale = Base.getScale(sc);
18final String home = ExtensionsControl.getHomeUrl("net.sf.basedb.reggie");
19DbControl dc = null;
20try
21{
22  dc = sc.newDbControl();
23  final User user = User.getById(dc, sc.getLoggedInUserId());
24%>
25<base:page type="default" >
26<base:head scripts="ajax.js" styles="path.css">
27  <link rel="stylesheet" type="text/css" href="../css/reggie.css">
28  <link rel="stylesheet" type="text/css" href="../css/plate.css">
29  <script language="JavaScript" src="../reggie.js" type="text/javascript" charset="UTF-8"></script>
30
31<script language="JavaScript">
32var debug = true;
33var currentStep = 1;
34
35var DEFAULT_READ_STRING = 'Y54N1I6J1Y53N1';
36var VOLUME_TO_USE_FROM_POOL = 10.0; // µl
37
38var poolsAreValid = false;
39var flowCellsAreValid = false;
40var readStringisValid = false;
41
42var flowCells = [];
43var selectedPools = [];
44
45function init()
46{
47  var frm = document.forms['reggie'];
48  var pools = getUnusedPools();
49 
50  var poolsList = frm.pools;
51  if (pools != null && pools.length > 0)
52  {
53    for (var poolNo=0; poolNo < pools.length; poolNo++)
54    {
55      var pool = pools[poolNo];
56      poolsList[poolsList.length] = new Option(pool.name, pool.id, poolNo < 4);
57    }
58  }
59 
60  Main.show('step.1.section');
61  Main.show('gocreate');
62  Main.show('navigation');
63  poolsOnChange();
64}
65
66function getUnusedPools()
67{
68  var frm = document.forms['reggie']; 
69 
70  var request = Ajax.getXmlHttpRequest();
71  try
72  {
73    showLoadingAnimation('Loading pools...');
74    var url = '../FlowCell.servlet?ID=<%=ID%>&cmd=GetUnusedPools';   
75    request.open("GET", url, false); 
76    request.send(null);
77  }
78  finally
79  {
80    hideLoadingAnimation();
81  }
82 
83  if (debug) Main.debug(request.responseText);
84  var response = JSON.parse(request.responseText); 
85  if (response.status != 'ok')
86  {
87    setFatalError(response.message);
88    return false;
89  }
90  return response.pools;
91}
92
93function loadFlowCellNames(numNames)
94{
95  var frm = document.forms['reggie'];
96
97  // Load Libraries and related info from the selected bioplate
98  var request = Ajax.getXmlHttpRequest();
99  try
100  {
101    showLoadingAnimation('Loading flow cell information...');
102    var url = '../FlowCell.servlet?ID=<%=ID%>&cmd=GetNextAutoGeneratedFlowCellNames&numNames='+numNames;   
103    request.open("GET", url, false);
104    request.send(null);
105  }
106  finally
107  {
108    hideLoadingAnimation();
109  }
110 
111  if (debug) Main.debug(request.responseText);
112  var response = JSON.parse(request.responseText);
113  if (response.status != 'ok')
114  {
115    setFatalError(response.message);
116    return false;
117  }
118 
119  var names = response.names;
120  for (var i = 0; i < names.length; i++)
121  {
122    var fc = {};
123    fc.name = names[i];
124    flowCells[i] = fc;
125    setInnerHTML('fc.'+i, fc.name);
126  }
127}
128
129function poolsOnChange()
130{
131  poolsAreValid = false;
132 
133  var frm = document.forms['reggie'];
134  var poolsList = frm.pools;
135 
136  var numSelected = 0;
137  for (var i = 0; i < poolsList.length; i++)
138  {
139    if (poolsList[i].selected) numSelected++;
140  }
141
142  setInnerHTML('numSelected', numSelected + ' selected');
143 
144  if (numSelected == 0)
145  {
146    setInputStatus('pools', 'Must select at least 1 pool.', 'invalid');
147    return;
148  }
149 
150  poolsAreValid = true;
151  setInputStatus('pools', '', 'valid');
152}
153
154
155var subtypePooledLibrary = null;
156function selectPools()
157{
158  var frm = document.forms['reggie'];
159  if (frm.pools.disabled) return;
160 
161  if (subtypePooledLibrary == null)
162  {
163    var request = Ajax.getXmlHttpRequest();
164    var url = '../Subtype.servlet?ID=<%=ID%>&cmd=GetSubtypeInfo&name=POOLED_LIBRARY';
165    request.open("GET", url, false); 
166    request.send(null);
167
168    if (debug) Main.debug(request.responseText);
169    var response = JSON.parse(request.responseText); 
170    if (response.status != 'ok')
171    {
172      setFatalError(response.message);
173      return false;
174    }
175    subtypePooledLibrary = response.subtype;
176  }
177 
178  var url = getRoot() + 'biomaterials/extracts/index.jsp?ID=<%=ID%>&cmd=UpdateContext&mode=selectmultiple&callback=setPoolCallback';
179  url += '&tmpfilter:INT:itemSubtype='+subtypePooledLibrary.id;
180  url += '&tmpfilter:DATE:creationEvent.eventDate='+encodeURIComponent('<>');
181  url += '&resetTemporary=1';
182  Main.openPopup(url, 'SelectPooledLibraries', 1050, 700);
183}
184
185function setPoolCallback(id, name)
186{
187  var frm = document.forms['reggie'];
188  var poolsList = frm.pools;
189  for (var i = 0; i < poolsList.length; i++)
190  {
191    if (poolsList[i].value == id)
192    {
193      poolsList[i].selected = true;
194      poolsOnChange();
195      return;
196    }
197  }
198
199  var option = new Option(name, id, true, true);
200  poolsList[poolsList.length] = option;
201  poolsOnChange();
202}
203
204
205function goNext(manual)
206{
207  setInnerHTML('gonext.message', '');
208  if (currentStep == 1)
209  {   
210    gotoStep2();
211  }
212  setCurrentStep(currentStep);
213}
214
215function gotoStep2()
216{
217  if (!poolsAreValid) return;
218 
219  var frm = document.forms['reggie'];
220  frm.pools.disabled = true;
221  frm.num_flowcells[0].disabled = true;
222  frm.num_flowcells[1].disabled = true;
223  frm.num_lanes[0].disabled = true;
224  frm.num_lanes[1].disabled = true;
225 
226  var numFlowCells = Forms.getCheckedRadio(frm.num_flowcells).value;
227  loadFlowCellNames(numFlowCells);
228  var plate = document.getElementById('plate');
229  if (numFlowCells == 1)
230  {
231    Main.addClass(plate, 'hide-fc-1');
232  }
233 
234  var numLanes = Forms.getCheckedRadio(frm.num_lanes).value;
235  for (var i = numLanes; i < 8; i++)
236  {
237    Main.hide('lane.'+i);
238  }
239
240  var selectPoolHtml = ''; 
241  for (var i = 0; i < frm.pools.length; i++)
242  {
243    if (frm.pools[i].selected)
244    {
245      var pool = {};
246      pool.id = parseInt(frm.pools[i].value, 10);
247      pool.name = frm.pools[i].text;
248      var index = selectedPools.length;
249      selectedPools[index] = pool;
250     
251      selectPoolHtml += '<div class="menuitem enabled" id="pool-'+pool.id+'" data-index="'+index+'">';
252      selectPoolHtml += pool.name+'</div>';
253    }
254  }
255  setInnerHTML('select-pool-all', selectPoolHtml);
256
257  // Initialize flowCell->lane->pool array
258  for (var fcNo = 0; fcNo < numFlowCells; fcNo++)
259  {
260    var flowCell = flowCells[fcNo];
261    flowCell.lanes = [];
262    for (var laneNo = 0; laneNo < numLanes; laneNo++)
263    {
264      flowCell.lanes[laneNo] = {};
265    }
266  }
267 
268  // Case 1: 4 pools on 2+2 lanes
269  if (numFlowCells == 2 && numLanes == 8 && selectedPools.length == 4)
270  {
271    // Assign all pools to 2 lanes on each flow cell
272    for (var poolNo = 0; poolNo < 4; poolNo++)
273    {
274      var pool = selectedPools[poolNo];
275      flowCells[0].lanes[poolNo] = {'pool': pool, 'defaultPool': pool};
276      flowCells[0].lanes[poolNo+4] = {'pool': pool, 'defaultPool': pool};
277      flowCells[1].lanes[3-poolNo] = {'pool': pool, 'defaultPool': pool};
278      flowCells[1].lanes[7-poolNo] = {'pool': pool, 'defaultPool': pool};
279    }
280  }
281 
282  // Case 2: A single pool on all lanes
283  if (selectedPools.length == 1)
284  {
285    var pool = selectedPools[0];
286    for (var fcNo = 0; fcNo < numFlowCells; fcNo++)
287    {
288      for (var laneNo = 0; laneNo < numLanes; laneNo++)
289      {
290        flowCells[fcNo].lanes[laneNo] = {'pool': pool, 'defaultPool': pool};
291      }
292    }
293  }
294 
295  checkFlowCells();
296  readStringOnChange(DEFAULT_READ_STRING);
297 
298  currentStep = 2;
299  Main.show('step.2.section');
300  Main.addClass(document.getElementById('step.1.section'), 'disabled');
301  Main.hide('gonext');
302  Main.show('gocancel');
303  Main.show('goregister');
304  frm.readString.focus();
305
306}
307
308function readStringOnChange(readString)
309{
310  var frm = document.forms['reggie'];
311  if (readString) frm.readString.value = readString;
312 
313  readStringIsValud = false;
314  setInputStatus('readString', '', '');
315 
316  var readString = frm.readString.value;
317  if (readString == '')
318  {
319    setInputStatus('readString', 'Missing', 'invalid');
320    return;
321  }
322
323  if (!readString.match(/^([YNIJ]\d+)+$/))
324  {
325    setInputStatus('readString', 'Invalid format', 'invalid');
326    return;
327  }
328 
329  setInputStatus('readString', '', 'valid');
330  readStringIsValid = true;
331
332}
333
334function checkFlowCells()
335{
336  setInputStatus('flowCells', '', '');
337  flowCellsAreValid = false;
338 
339  for (var fcNo = 0; fcNo < flowCells.length; fcNo++)
340  {
341    for (var laneNo = 0; laneNo < flowCells[fcNo].lanes.length; laneNo++)
342    {
343      var lane = flowCells[fcNo].lanes[laneNo];
344      var pool = lane.pool;
345     
346      var name = pool ? pool.name : '';
347      if (lane.defaultPool && pool != lane.defaultPool)
348      {
349        name += ' *';
350      }
351     
352      setInnerHTML('lane.'+laneNo+'.'+fcNo, name);
353     
354      if (!pool)
355      {
356        setInputStatus('flowCells', 'Missing', 'invalid');
357        return;
358      }
359    }
360  }
361 
362 
363  setInputStatus('flowCells', '', 'valid');
364  flowCellsAreValid = true;
365}
366
367function goRegister()
368{
369  if (!flowCellsAreValid || !readStringIsValid) return;
370 
371  var frm = document.forms['reggie'];
372  frm.readString.disabled = true;
373  Main.hide('goregister');
374  Main.hide('gocancel');
375  Main.addClass(document.getElementById('step.2.section'), 'disabled');
376
377  var submitInfo = {};
378  submitInfo.readString = frm.readString.value;
379  submitInfo.flowCells = flowCells;
380 
381  // Calculate volume to use for each pool
382  for (var fcNo = 0; fcNo < flowCells.length; fcNo++)
383  {
384    for (var laneNo = 0; laneNo < flowCells[fcNo].lanes.length; laneNo++)
385    {
386      var lane = flowCells[fcNo].lanes[laneNo];
387      lane.defaultPool = null; // Do not need to submit this information
388      var pool = lane.pool;
389      pool.count = pool.count ? pool.count+1 : 1;
390    }
391  }
392 
393  for (var poolNo = 0; poolNo < selectedPools.length; poolNo++)
394  {
395    var pool = selectedPools[poolNo];
396    pool.usedVolume = VOLUME_TO_USE_FROM_POOL / pool.count;
397  }
398 
399  if (debug) Main.debug(JSON.stringify(submitInfo));
400 
401  var url = '../FlowCell.servlet?ID=<%=ID%>&cmd=CreateFlowCells';
402 
403  var request = Ajax.getXmlHttpRequest();
404  try
405  {
406    showLoadingAnimation('Performing registration...');
407    request.open("POST", url, false);
408    request.send(JSON.stringify(submitInfo));
409  }
410  finally
411  {
412    hideLoadingAnimation();
413  }
414 
415  if (debug) Main.debug(request.responseText);
416  var response = JSON.parse(request.responseText);
417 
418  if (response.messages && response.messages.length > 0)
419  {
420    var msg = '<ul>';
421    for (var i = 0; i < response.messages.length; i++)
422    {
423      var msgLine = response.messages[i];
424      if (msgLine.indexOf('[Warning]') >= 0)
425      {
426        msg += '<li class="warning">' + msgLine.replace('[Warning]', '');
427      }
428      else
429      {
430        msg += '<li>' + msgLine;
431      }
432    }
433    msg += '</ul>';
434    setInnerHTML('messages', msg);
435    Main.show('messages');
436  }
437 
438  if (response.status != 'ok')
439  {
440    Main.addClass(document.getElementById('messages'), 'failure');
441    setFatalError(response.message);
442    return false;
443  }
444
445  Main.show('gorestart');
446
447 
448}
449
450var currentFcNo;
451var currentLaneNo;
452function selectPool(event, fcNo, laneNo)
453{
454  currentFcNo = fcNo;
455  currentLaneNo = laneNo;
456 
457  // Reset 'current' selection
458  var menu = document.getElementById('select-pool');
459  var selectAll = document.getElementById('select-pool-all');
460  for (var i = 0; i <  selectAll.childNodes.length; i++)
461  {
462    Main.removeClass(selectAll.childNodes[i], 'current');
463    Main.removeClass(selectAll.childNodes[i], 'default');
464  }
465  menu.style.display = 'block';
466 
467  var currentPool = flowCells[fcNo].lanes[laneNo].pool;
468  var defaultPool = flowCells[fcNo].lanes[laneNo].defaultPool;
469  if (currentPool) 
470  {
471    Main.addClass(document.getElementById('pool-'+currentPool.id), 'current');
472  }
473  if (defaultPool && defaultPool != currentPool)
474  {
475    Main.addClass(document.getElementById('pool-'+defaultPool.id), 'default');
476  }
477 
478
479  var x = event.clientX;
480  var halfHeight = Math.floor(selectAll.offsetHeight/2)
481  var y = event.clientY+2; //-halfHeight;
482
483  var scroll = 0;
484 
485  // Position the selection div
486  selectAll.scrollTop = scroll;
487  menu.style.left = (x)+'px';
488  menu.style.top = (y)+'px';
489  event.stopPropagation();
490}
491
492function poolSelected(event)
493{
494  var index = event.target.getAttribute('data-index');
495  var pool = selectedPools[index];
496  flowCells[currentFcNo].lanes[currentLaneNo].pool = pool;
497  setInnerHTML('lane.'+currentLaneNo+'.'+currentFcNo, pool ? pool.name : '');
498  checkFlowCells();
499}
500
501</script>
502<style>
503#numSelected
504{
505  font-style: italic;
506  margin-top: 0.25em;
507  margin-left: 1em;
508}
509
510.plate .header
511{
512  height: 3em;
513}
514
515.plate .header th.col-0, .plate .header th.col-1
516{
517  border-bottom: 1px solid #A0A0A0;
518}
519
520.plate .header th.lane
521{
522  padding: 0.5em;
523}
524
525.well
526{
527  height: 2em;
528  max-height: 2em;
529  min-height: 2em;
530  width: 10em;
531  max-width: 10em;
532  min-width: 10em;
533  font-size: 100%;
534  text-align: center;
535  vertical-align: middle;
536}
537
538.plate tbody .lane
539{
540  border-right: 1px solid #A0A0A0;
541}
542
543.well.col-1
544{
545  border-left-style: solid;
546}
547
548.hide-fc-1 .col-1
549{
550  display: none;
551}
552
553#select-pool-all
554{
555  max-height: 20em;
556  overflow: auto;
557}
558
559#select-pool .menuitem
560{
561  padding-left: 16px;
562}
563
564#select-pool div.current
565{
566  font-weight: bold;
567  background-image: url('../images/selected.gif');
568  background-position: 2px 50%;
569  background-repeat: no-repeat;
570}
571
572#select-pool div.default:after
573{
574  content: ' (default)';
575}
576
577#select-pool .menuitem:hover
578{
579  padding-left: 14px;
580  background-position: 0px 50%;
581}
582
583</style>
584</base:head>
585<base:body onload="init()">
586
587  <p:path><p:pathelement 
588    title="Reggie" href="<%="../index.jsp?ID="+ID%>" 
589    /><p:pathelement title="Create flow cells" 
590    /></p:path>
591
592  <div class="content" onclick="Main.hide('select-pool')">
593  <%
594  if (sc.getActiveProjectId() == 0)
595  {
596    %>
597    <div class="messagecontainer note" style="width: 950px; margin-left: 20px; margin-bottom: 20px; margin-right: 0px; font-weight: bold; color: #cc0000;">
598      No project has been selected. You may proceed with the registration but
599      created items will not be shared.
600    </div>
601    <%
602  }
603  %>
604 
605  <div id="select-pool" class="menu vertical" style="width: 150px; display: none;"
606    onclick="poolSelected(event)">
607    <div id="select-pool-all"></div>
608  </div>
609
610  <form name="reggie" onsubmit="return false;">
611 
612  <div id="step.1.section" style="display: none;">
613  <table class="stepform">
614  <tr>
615    <td rowspan="3" class="stepno">1</td>
616    <td class="steptitle">Select pools</td>
617  </tr>
618  <tr>
619    <td class="stepfields">
620      <table>
621      <tr valign="top">
622        <td class="prompt">Flow cells</td>
623        <td class="input">
624          <label><input type="radio" name="num_flowcells" value="1">1</label>
625          <label><input type="radio" name="num_flowcells" value="2" checked>2</label>
626        </td>
627        <td class="status" id="numFlowCells.status"></td>
628        <td class="help"><span id="numFlowCells.message" class="message" style="display: none;"></span>
629          Select how many flow cells to create.
630        </td>
631      </tr>
632      <tr valign="top">
633        <td class="prompt">Lanes/flow cell</td>
634        <td class="input" id="lanes">
635          <label><input type="radio" name="num_lanes" value="2">2</label>
636          <label><input type="radio" name="num_lanes" value="8" checked>8</label>
637        </td>
638        <td class="status" id="lanes.status"></td>
639        <td class="help"><span id="lanes.message" class="message" style="display: none;"></span>
640          Select the number of lanes on each flow cell.
641        </td>
642      </tr>
643      <tr valign="top">
644        <td class="prompt">Pools</td>
645        <td class="input"><select style="width:90%;" size="6" 
646            name="pools" id="pools" multiple onchange="poolsOnChange()"></select>
647          <div id="numSelected"></div>
648          <base:buttongroup style="margin-top: 0.5em;">
649            <base:button title="Select manually&hellip;" onclick="selectPools()" id="btnSelectPools" />
650          </base:buttongroup>
651        </td>
652        <td class="status" id="pools.status"></td>
653        <td class="help"><span id="pools.message" class="message" style="display: none;"></span>
654          Select the pools that should be used in the flow cells. The list contain pools that has
655          not yet been used (determined by comparing remaining and original quantity).
656        </td>
657      </tr>
658      </table>
659    </td>
660  </tr>
661  </table>
662  </div>
663 
664  <div id="step.2.section" style="display: none;">
665  <table class="stepform">
666  <tr>
667    <td rowspan="3" class="stepno">2</td>
668    <td class="steptitle">Flow cell information</td>
669  </tr>
670  <tr>
671    <td class="stepfields">
672
673      <table>
674      <tr valign="top">
675        <td class="prompt">Read string</td>
676        <td class="input">
677          <input type="text" class="required" name="readString" value="" size="20" 
678            onblur="readStringOnChange()">
679        </td>
680        <td class="status" id="readString.status"></td>
681        <td class="help"><span id="readString.message" class="message" style="display: none;"></span>
682          Read string used in the HiSeq. Letters <b>Y</b>, <b>N</b>, <b>I</b>, <b>J</b>
683          followed by digits.
684        </td>
685      </tr>
686      <tr valign="top">
687        <td class="prompt">Flow cell layout</td>
688        <td class="input">
689          <table class="plate" id="plate" style="margin: 1em 0 1em 0;">
690          <thead>
691          <tr class="header">
692            <th class="lane">Lane</th>
693            <%
694            for (int fcNo = 0; fcNo < 2; fcNo++)
695            {
696              %>
697              <th class="col-<%=fcNo%>" id="fc.<%=fcNo%>">FlowCell<%=fcNo%></th>
698              <%
699            }
700            %>
701          </tr>
702          </thead>
703          <tbody>
704          <%
705          for (int laneNo = 0; laneNo < 8; laneNo++)
706          {
707            %>
708            <tr class="row-<%=laneNo%>" id="lane.<%=laneNo%>">
709              <th class="lane"><%=laneNo+1%></th>
710              <%
711              for (int fcNo = 0; fcNo < 2; fcNo++)
712              {
713                %>
714                <td class="well col-<%=fcNo%>" id="lane.<%=laneNo%>.<%=fcNo%>" 
715                  onclick="selectPool(event, <%=fcNo%>, <%=laneNo%>)"></td>
716                <%
717              }
718              %>
719            </tr>
720            <%
721          }
722          %>
723          </tbody>
724          </table>
725       
726        </td>
727        <td class="status" id="flowCells.status"></td>
728        <td class="help"><span id="flowCells.message" class="message" style="display: none;"></span>
729          Assign a pool to each flow cell lane. Click on each lane to select a different pool.
730        </td>
731      </tr>
732      </table>
733    </td>
734  </tr>
735  </table> 
736  </div>
737 
738  <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>
739 
740  <div class="messagecontainer error" id="errorMessage" style="display: none; width: 950px; margin-left: 20px; margin-bottom: 0px;"></div>
741 
742  <div id="messages" class="success" style="display: none; width: 950px; margin-left: 20px; margin-top: 20px;"></div>
743
744  <table style="margin-left: 20px; margin-top: 10px; display: none;" class="navigation" id="navigation">
745    <tr>
746      <td><base:button id="gocancel" title="Cancel" onclick="goRestart(false)" style="display: none;"/></td>
747      <td><base:button id="gonext" title="Next" image="<%=home+"/images/gonext.png"%>" onclick="goNext(true)"/></td>
748      <td><base:button id="goregister" title="Register" image="<%=home+"/images/import.png"%>" onclick="goRegister()" style="display: none;"/></td>
749      <td><base:button id="gorestart" title="Restart" image="<%=home+"/images/goback.png"%>" onclick="goRestart(true)" style="display: none;"/></td>
750      <td id="gonext.message" class="message"></td>
751    </tr>
752  </table>
753 
754  </form>
755  </div>
756 
757</base:body>
758</base:page>
759<%
760}
761finally
762{
763  if (dc != null) dc.close();
764}
765%>
Note: See TracBrowser for help on using the repository browser.