source: trunk/www/biomaterials/wizards/place_on_plate.jsp @ 5557

Last change on this file since 5557 was 5557, checked in by Nicklas Nordborg, 13 years ago

References #1570: Use JSON for data transport in AJAX calls

Implemented this for various plate/well releated AJAX calls.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Id
File size: 21.4 KB
Line 
1<%-- $Id: place_on_plate.jsp 5557 2011-01-28 11:48:15Z nicklas $
2  ------------------------------------------------------------------
3  Copyright (C) 2010 Nicklas Nordborg
4
5  This file is part of BASE - BioArray Software Environment.
6  Available at http://base.thep.lu.se/
7
8  BASE is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License
10  as published by the Free Software Foundation; either version 3
11  of the License, or (at your option) any later version.
12
13  BASE is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with BASE. If not, see <http://www.gnu.org/licenses/>.
20  ------------------------------------------------------------------
21
22  @author Nicklas
23--%>
24<%@ page pageEncoding="UTF-8" session="false"
25  import="net.sf.basedb.core.BioPlateEvent"
26  import="net.sf.basedb.core.BioPlateEventType"
27  import="net.sf.basedb.core.MeasuredBioMaterial"
28  import="net.sf.basedb.core.Hardware"
29  import="net.sf.basedb.core.Protocol"
30  import="net.sf.basedb.core.DbControl"
31  import="net.sf.basedb.core.Item"
32  import="net.sf.basedb.core.ItemContext"
33  import="net.sf.basedb.core.ItemQuery"
34  import="net.sf.basedb.core.SessionControl"
35  import="net.sf.basedb.core.query.Restrictions"
36  import="net.sf.basedb.core.query.Hql"
37  import="net.sf.basedb.util.Values"
38  import="net.sf.basedb.util.formatter.Formatter"
39  import="net.sf.basedb.clients.web.formatter.FormatterFactory"
40  import="net.sf.basedb.clients.web.formatter.FormatterSettings"
41  import="net.sf.basedb.clients.web.Base"
42  import="net.sf.basedb.clients.web.util.HTML"
43  import="java.util.List"
44  import="java.util.Date"
45  import="java.util.Collections"
46%>
47<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
48<%@ taglib prefix="tbl" uri="/WEB-INF/table.tld" %>
49<%
50final Item itemType = Item.valueOf(request.getParameter("itemType"));
51//final String subContext = Values.getString(request.getParameter("subcontext"), "");
52final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
53//final ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, subContext, null, null);
54final ItemContext cc = sc.getCurrentContext(Item.BIOPLATEEVENT, BioPlateEventType.PLACE_ON_PLATE);
55final String ID = sc.getId();
56final float scale = Base.getScale(sc);
57final DbControl dc = sc.newDbControl();
58try
59{
60  // Load recently used items
61  List<Hardware> recentHardware = (List<Hardware>)cc.getRecent(dc, Item.HARDWARE);
62  List<Protocol> recentProtocols = (List<Protocol>)cc.getRecent(dc, Item.PROTOCOL);
63 
64  ItemQuery<MeasuredBioMaterial> query = (ItemQuery<MeasuredBioMaterial>)cc.getQuery();
65  List<MeasuredBioMaterial> bioMaterial = Collections.emptyList();
66  if (query != null)
67  {
68    query.restrict(Restrictions.eq(Hql.property("bioWell"), null));
69    query.setMaxResults(-1);
70    query.setFirstResult(0);
71    bioMaterial = query.list(dc);
72    cc.setQuery(null);
73  }
74  final String clazz = "class=\"text\"";
75  final String requiredClazz = "class=\"text required\"";
76  final Formatter<Date> dateFormatter = FormatterFactory.getDateFormatter(sc);
77  final String dateFormat = FormatterSettings.getDateFormat(sc);
78  final String jsDateFormat = HTML.javaScriptEncode(dateFormat);
79  final String htmlDateFormat = HTML.encodeTags(dateFormat);
80  final boolean autoSelectUnmappedItem = Values.getBoolean(sc.getUserClientSetting("place-on-plate.auto-select-unmapped-item"), true);
81  %>
82  <base:page type="popup" title="Place biomaterial on plate">
83  <base:head scripts="ajax.js,plate.js,js-draw.js,json2.js" styles="plate.css,toolbar.css">
84    <script language="javascript">
85 
86    // We have one list of items and one bioplate
87    var itemList;
88    var plate;
89   
90    // We can select one item and one well
91    var selectedItem;
92    var selectedWell;
93
94    // For drawing
95    var graphics;
96    var pen;
97    var selectedPen;
98   
99    function init()
100    {
101      // Initialize graphics
102      graphics = new jsGraphics(document.getElementById('canvas'));
103      pen = new jsPen();
104      selectedPen = new jsPen(new jsColor('445577'), 2);
105      // Create the list of items
106      itemList = new ItemList('itemlist');
107      <%
108      for (MeasuredBioMaterial bm : bioMaterial)
109      {
110        String itemId = Integer.toString(bm.getId());
111        %>
112        itemList.addItem(<%=itemId%>, '<%=HTML.javaScriptEncode(bm.getName())%>');
113        <%
114      }
115      %>
116    }
117
118    /**
119      Draw a link between the item and it's mapped well.
120    */
121    function itemOnMouseOver(itemId)
122    {
123      var item = itemList.getItem(itemId);
124      if (!item) return;
125      item.drawLink(graphics, pen, false);
126    }
127
128    /**
129      Hide the link between the item and it's mapped well,
130      unless the item (or it's mapped well) is selected.
131    */
132    function itemOnMouseOut(itemId)
133    {
134      var item = itemList.getItem(itemId);
135      if (!item) return;
136      if (!item.selected && !(item.mappedWell && item.mappedWell.selected))
137      {
138        item.hideLink(graphics);
139      }
140    }
141   
142    /**
143      Draw a link between the well and it's mapped item.
144    */
145    function wellOnMouseOver(row, column)
146    {
147      var well = plate.getWell(row, column);
148      if (!well || !well.mappedItem) return;
149      well.mappedItem.scrollIntoView();
150      well.mappedItem.drawLink(graphics, pen, false);
151    }
152 
153    /**
154      Hide the link between the well and it's mapped item,
155      unless the well (or it's mapped item) is selected.
156    */
157    function wellOnMouseOut(row, column)
158    {
159      var well = plate.getWell(row, column);
160      if (!well || !well.mappedItem) return;
161      if (!well.selected && !(well.mappedItem && well.mappedItem.selected))
162      {
163        well.hideLink(graphics);
164      }
165    }
166
167    /**
168      Select the item that is clicked on. The currently selected item
169      is de-selected. If the clicked item is the same as the currently
170      selected item no new item is selected. If a well is already selected
171      a link is made between the well and the newly selected item.
172    */
173    function itemOnClick(itemId)
174    {
175      var item = itemList.getItem(itemId);
176
177      // De-select the currently selected item
178      if (selectedItem)
179      {
180        selectedItem.setSelected(false);
181        if (item == selectedItem)
182        {
183          // Re-draw link with regular pen (eg. same as onmouseover)
184          selectedItem.drawLink(graphics, pen, true);
185          selectedItem = null;
186          return;
187        }
188        else
189        {
190          selectedItem.hideLink(graphics);
191        }
192      }
193     
194      // Select the new item and draw link to mapped well
195      selectedItem = item;
196      selectedItem.setSelected(true);
197      selectedItem.drawLink(graphics, selectedPen, true);
198
199      // Map the item to the selected well
200      if (selectedWell && selectedItem)
201      {
202        placeSelectedOnPlate();
203      }
204    }
205   
206    /**
207      Select the well that is clicked on. The currently selected well is
208      de-selected. If the clicked well is the same as the currently
209      selected well no new well is selected. If an item is already selected
210      a link is made between the item and the newly selected well.
211    */
212    function wellOnClick(row, column)
213    {
214      var well = plate.getWell(row, column);
215
216      // De-select the currently selected well
217      if (selectedWell)
218      {
219        selectedWell.setSelected(false);
220        if (well == selectedWell)
221        {
222          // Re-draw link with regular pen (eg. same as onmouseover)
223          selectedWell.drawLink(graphics, pen, true);
224          selectedWell = null;
225          return;
226        }
227        else
228        {
229          selectedWell.hideLink(graphics);
230        }
231      }
232     
233      selectedWell = well;
234      selectedWell.setSelected(true);
235      selectedWell.drawLink(graphics, selectedPen, true);
236
237      if (selectedWell && selectedItem)
238      {
239        var nextItem = document.forms['main'].autoSelectUnmappedItem.checked ?
240            itemList.nextUnmappedItem(selectedItem) : null;
241        placeSelectedOnPlate();
242        if (nextItem)
243        {
244          nextItem.setSelected(true);
245          nextItem.scrollIntoView();
246          selectedItem = nextItem;
247        }
248      }
249    }
250   
251    /**
252      Redraw the link to the selected item/well when the item list scrolls.
253    */
254    function onItemListScroll()
255    {
256      var item = selectedItem;
257      if (!item && selectedWell) item = selectedWell.mappedItem;
258      if (item) item.drawLink(graphics, selectedPen, true);
259    }
260   
261    /**
262      Map the selected item with the selected well. Hide and
263      redraw links as needed.
264    */
265    function placeSelectedOnPlate()
266    {
267      // Hide any links that are currently displayed
268      selectedItem.hideLink(graphics);
269      selectedWell.hideLink(graphics);
270     
271      // Map to the new well and draw link
272      if (selectedItem.mappedWell != selectedWell)
273      {
274        selectedItem.mapToWell(selectedWell);
275        selectedItem.scrollIntoView();
276        selectedItem.drawLink(graphics, pen, true);
277      }
278      else
279      {
280        selectedItem.unmapWell();
281      }
282     
283      // De-select everything
284      selectedItem.setSelected(false);
285      selectedWell.setSelected(false);
286      selectedItem = null;
287      selectedWell = null;
288    }
289   
290    /**
291      Remove all mappings that have been made so far.
292    */
293    function clearMapping()
294    {
295      if (!plate)
296      {
297        alert('No destination plate has been selected');
298        return;
299      }
300      if (!confirm('This will remove all placed biomaterial. Continue?')) return;
301      itemList.unmapAll(graphics);
302      if (selectedItem)
303      {
304        selectedItem.setSelected(false);
305        selectedItem = null;
306      }
307      if (selectedWell)
308      {
309        selectedWell.setSelected(false);
310        selectedWell = null;
311      }
312    }
313 
314    /**
315      Automatically place unplaced biomaterial on the plate filling rows first.
316      Biomaterial and wells that have already been mapped are skipped.
317    */
318    function placeByRow()
319    {
320      if (!plate)
321      {
322        alert('No destination plate has been selected');
323        return;
324      }
325      var index = 0;
326      var numItems = itemList.items.length;
327      for (var row = 0; row < plate.rows; row++)
328      {
329        for (var column = 0; column < plate.columns; column++)
330        {
331          var well = plate.getWell(row, column);
332          if (!well.mappedItem && !well.locked)
333          {
334            var mapped = false;
335            while (index < itemList.items.length && !mapped)
336            {
337              var item = itemList.items[index];
338              index++;
339              if (!item.mappedWell)
340              {
341                item.mapToWell(well);
342                mapped = true;
343              }
344            }
345          }
346        }
347      }
348    }
349   
350    /**
351      Automatically place unplaced biomaterial on the plate filling columns first.
352      Biomaterial and wells that have already been mapped are skipped.
353    */
354    function placeByColumn()
355    {
356      if (!plate)
357      {
358        alert('No destination plate has been selected');
359        return;
360      }
361      var index = 0;
362      var numItems = itemList.items.length;
363      for (var column = 0; column < plate.columns; column++)
364      {
365        for (var row = 0; row < plate.rows; row++)
366        {
367          var well = plate.getWell(row, column);
368          if (!well.mappedItem && !well.locked)
369          {
370            var mapped = false;
371            while (index < itemList.items.length && !mapped)
372            {
373              var item = itemList.items[index];
374              index++;
375              if (!item.mappedWell)
376              {
377                item.mapToWell(well);
378                mapped = true;
379              }
380            }
381          }
382        }
383      }
384    }
385   
386    function selectBioPlateOnClick()
387    {
388      if (plate && !confirm('This will remove all placed biomaterial. Continue?')) return;
389     
390      var url = '../bioplates/index.jsp?ID=<%=ID%>&cmd=UpdateContext&mode=selectone&callback=setBioPlateCallback';
391      url += '&resetTemporary=1&tmpfilter:INT:bioPlateType.bioMaterialType=|<%=itemType.getValue()%>'
392      Main.openPopup(url, 'SelectBioPlate', 1000, 700);
393    }
394
395    function setBioPlateCallback(bioPlateId, name)
396    {
397      var request = Ajax.getXmlHttpRequest();
398      var url = '../bioplates/ajax.jsp?ID=<%=ID%>&cmd=GetFullPlateInfo&item_id=' + bioPlateId;
399      request.open("GET", url, false); 
400      // NOTE! 'false' causes code to wait for the response. aka. 'Synchronous AJAX' or SJAX.
401      request.send(null);
402      var plateInfo = Ajax.parseJsonResponse(request.responseText);
403      if (plateInfo.status != 'ok')
404      {
405        alert(plateInfo.message);
406        return false;
407      }
408     
409      // Get plate and well information from the AJAX response
410      var rows = plateInfo.rows;
411      var columns = plateInfo.columns;
412     
413      var bigPlate = rows > 12 || columns > 18;
414      var plateClass = bigPlate ? 'plate bigplate' : 'plate';
415
416      // Remove existing mapping
417      itemList.unmapAll(graphics);
418
419      // Create plate and wells
420      plate = new Plate('dest', plateInfo.id, plateInfo.name, rows, columns);
421      for (var i = 0; i < plateInfo.wells.length; i++)
422      {
423        var wellInfo = plateInfo.wells[i];
424        var bmInfo = wellInfo.bioMaterial;
425        var row = parseInt(wellInfo.row);
426        var col = parseInt(wellInfo.column);
427        var well = plate.getWell(row, col);
428        well.locked = !wellInfo.canAdd || bmInfo;
429        well.name = bmInfo ? bmInfo.name : '';
430      }
431     
432      document.getElementById('plate.name').innerHTML = Main.encodeTags(name);
433     
434      // Create html table representing the bioplate
435      var html = '<table class="'+plateClass+'" cellspacing="0" cellpadding="0" onmouseout="event.cancelBubble=true">';
436      html += '<tr><td></td>';
437      for (var c = 0; c < plate.columns; c++)
438      {
439        html += '<td class="columnheader">' + (c+1) + '</td>';
440      }
441      html += '</tr>';
442      for (var r = 0; r < plate.rows; r++)
443      {
444        html += '<tr><td class="rowheader">' + Plates.toAlphaCoordinate[r] + '</td>';
445        for (var c = 0; c < plate.columns; c++)
446        {
447          var well = plate.getWell(r, c);
448          var cls = 'well';
449          var onclick = '';
450          var onmouseover = '';
451          var onmouseout = '';
452          var title = '';
453          if (well.locked) 
454          {
455            cls+= ' unmappable';
456            if (well.name) 
457            {
458              title = 'This well is used by: ' + Main.encodeTags(well.name);
459            }
460            else
461            {
462              title = 'This well has already been used';
463            }
464          }
465          else
466          {
467            cls += ' empty editable';
468            onclick = ' onclick="wellOnClick('+r+','+c+')""';
469            onmouseover = ' onmouseover=wellOnMouseOver('+r+','+c+')';
470            onmouseout = ' onmouseout=wellOnMouseOut('+r+','+c+')';
471          }
472          html += '<td id="dest.'+r+'.'+c+'" class="' + cls + '"' + onclick + onmouseover + onmouseout+' title="'+title+'"></td>';
473        }
474        html += '</tr>';
475      }
476      html += '</table>';
477      document.getElementById('plate').innerHTML = html;
478      Main.show('toolbar.mappings');
479    }
480   
481    /**
482      Save the event.
483    */
484    function doPlaceOnPlate()
485    {
486      if (!plate)
487      {
488        alert('No plate has been selected');
489        return;
490      }
491      var frm = document.forms['main'];
492      if (Main.trimString(frm.name.value) == '')
493      {
494        alert("You must enter a name for the event.");
495        frm.name.focus();
496        return;
497      }
498     
499      frm.plate_id.value = plate.id;
500      frm.rows.value = plate.rows;
501      frm.columns.value = plate.columns;
502     
503      var numMapped = 0;
504      for (var row = 0; row < plate.rows; row++)
505      {
506        for (var column = 0; column < plate.columns; column++)
507        {
508          var well = plate.getWell(row, column);
509         
510          if (well.mappedItem)
511          {
512            Forms.createHidden(frm, 'well.' + row + '.' + column, well.mappedItem.id);
513            numMapped++;
514          }
515        }
516      }
517     
518      if (numMapped == 0)
519      {
520        alert('No biomaterial has been mapped to wells');
521        return;
522      }
523     
524      frm.submit();
525    }
526   
527    function selectHardwareOnClick()
528    {
529      var frm = document.forms['main'];
530      var url = '../../admin/hardware/index.jsp?ID=<%=ID%>&cmd=UpdateContext&mode=selectone&callback=setHardwareCallback';
531      if (frm.hardware_id.length > 1)
532      {
533        var id = Math.abs(parseInt(frm.hardware_id[1].value));       
534        url += '&item_id='+id;
535      }
536      url += '&resetTemporary=1';
537      Main.openPopup(url, 'SelectHardware', 1000, 700);
538    }
539    function setHardwareCallback(id, name)
540    {
541      var frm = document.forms['main'];
542      var list = frm.hardware_id;
543      if (list.length < 2 || list[1].value == '0') // >
544      {
545        Forms.addListOption(list, 1, new Option());
546      }
547      list[1].value = id;
548      list[1].text = name;
549      list.selectedIndex = 1;
550    }
551 
552    function selectProtocolOnClick()
553    {
554      var frm = document.forms['main'];
555      var url = '../../admin/protocols/index.jsp?ID=<%=ID%>&cmd=UpdateContext&mode=selectone&callback=setProtocolCallback';
556      if (frm.protocol_id.length > 1)
557      {
558        var id = Math.abs(parseInt(frm.protocol_id[1].value));       
559        url += '&item_id='+id;
560      }
561      url += '&resetTemporary=1';
562      Main.openPopup(url, 'SelectProtocol', 1000, 700);
563    }
564    function setProtocolCallback(id, name)
565    {
566      var frm = document.forms['main'];
567      var list = frm.protocol_id;
568      if (list.length < 2 || list[1].value == '0') // >
569      {
570        Forms.addListOption(list, 1, new Option());
571      }
572      list[1].value = id;
573      list[1].text = name;
574      list.selectedIndex = 1;
575    }
576    </script>
577  </base:head>
578  <base:body onload="init()">
579    <div id="canvas"></div>
580    <h3>Place biomaterial on plate</h3>
581    <div class="boxedbottom" style="height: <%=(int)(scale*500)%>px; padding: 0px;">
582      <form name="main" action="index.jsp" method="post">
583      <input type="hidden" name="ID" value="<%=ID%>">
584      <input type="hidden" name="cmd" value="PlaceOnPlate">
585      <input type="hidden" name="itemType" value="<%=itemType.name()%>">
586      <input type="hidden" name="plate_id" value="">
587      <input type="hidden" name="rows" value="">
588      <input type="hidden" name="columns" value="">
589     
590    <%
591    if (bioMaterial.size() == 0)
592    {
593      %>
594      <div class="error">
595      No biomaterial selected. Please close this window and select one or more items that
596      are not already placed on a plate.
597      </div>
598      <%
599    }
600    else
601    {
602      %>
603      <table cellspacing="0" border="0" width="100%" style="background: #e0e0e0; padding-bottom: 6px;">
604      <tr valign="top">
605        <td style="width: 50%;">
606          <table class="form" cellspacing=0>
607          <tr>
608            <td class="prompt" style="width: 90px;">Event name</td>
609            <td><input <%=requiredClazz%> type="text" name="name" 
610              value="Place biomaterial on plate" 
611              size="40" maxlength="<%=BioPlateEvent.MAX_NAME_LENGTH%>"></td>
612          </tr>
613          <tr>
614            <td class="prompt">Event date</td>
615            <td>
616              <table border="0" cellspacing="0" cellpadding="0">
617              <tr>
618              <td>
619                <input <%=clazz%> type="text" name="event_date" 
620                  value="<%=HTML.encodeTags(dateFormatter.format(new Date()))%>" 
621                  size="20" maxlength="20" title="Enter date in format: <%=htmlDateFormat%>">
622                &nbsp;
623              </td>
624              <td>
625              <base:button 
626                onclick="<%="Dates.selectDate('Event date', 'main', 'event_date', null, '"+jsDateFormat+"')"%>"
627                image="calendar.png"
628                title="Calendar&hellip;" 
629                tooltip="Select a date from a calendar" 
630              />
631              </td>
632              </tr>
633              </table>
634            </td>
635          </tr>
636          <tr>
637            <td class="prompt">Protocol</td>
638            <td>
639              <base:select 
640                id="protocol_id"
641                clazz="selectionlist"
642                required="false"
643                current="<%=null%>"
644                recent="<%=recentProtocols%>"
645                onselect="selectProtocolOnClick()"
646              />
647            </td>
648          </tr>
649          <tr>
650            <td class="prompt">Hardware</td>
651            <td>
652              <base:select 
653                id="hardware_id"
654                clazz="selectionlist"
655                required="false"
656                current="<%=null%>"
657                recent="<%=recentHardware%>"
658                onselect="selectHardwareOnClick()"
659              />
660            </td>
661          </tr>
662          </table>
663        </td>
664        <td style="width: 50%;">
665          <b>Description</b><br>
666          <textarea <%=clazz%> rows="4" cols="40" name="description" wrap="virtual"
667            ></textarea>
668          <a href="javascript:Main.zoom('Description', 'main', 'description')"
669            title="Edit in larger window"><base:icon image="zoom.gif" /></a>
670        </td>
671      </tr>
672      </table>
673     
674      <tbl:toolbar id="toolbar.mappings" style="border-left: 0px; border-right: 0px;">
675        <tbl:button title="Select plate&hellip;" 
676          onclick="javascript:selectBioPlateOnClick()" 
677          image="place_on_plate.png" 
678          tooltip="Select the destination plate"
679        />
680        <tbl:button title="Clear" 
681          onclick="clearMapping()" 
682          image="cancel.gif" 
683          tooltip="Clear all mapped wells"
684        />
685        <tbl:button title="Place by row" 
686          onclick="placeByRow()" 
687          image="place_by_row.png" 
688          tooltip="Place remaining items; start with rows"
689        />
690        <tbl:button title="Place by column" 
691          onclick="placeByColumn()" 
692          image="place_by_column.png" 
693          tooltip="Place remaining items; start with columns"
694        />
695      </tbl:toolbar>
696     
697      <table cellspacing="0" cellpadding="0" border="0" width="100%" style="padding: 4px;">
698      <tr valign="top">
699        <td style="width: 280px;">
700          <b>Items to place</b>
701          <div id="itemlist" class="biomateriallist" style="height: <%=(int)(scale*320)%>px; width:270px;" onscroll="onItemListScroll()">
702          <table border="0" cellspacing="0" cellpadding="0" width="100%">
703          <%
704          for (MeasuredBioMaterial bm : bioMaterial)
705          {
706            String itemId = Integer.toString(bm.getId());
707            String fullLabel = bm.getName();
708            String label = Values.trimString(fullLabel, 30);
709            %>
710            <tr class="item" id="itemlist.<%=itemId%>" title="<%=fullLabel == label ? "" : HTML.encodeTags(fullLabel)%>"
711              onclick="itemOnClick(<%=itemId%>)" onmouseover="itemOnMouseOver(<%=itemId%>)" onmouseout="itemOnMouseOut(<%=itemId%>)">
712              <td class="info" id="itemlist.<%=itemId%>.info"></td>
713              <td class="label" id="itemlist.<%=itemId%>.label"><%=HTML.encodeTags(label)%></td>
714            </tr>
715            <%
716          }
717          %>
718          </table>
719          </div>
720          <input type="checkbox" name="autoSelectUnmappedItem" 
721            <%=autoSelectUnmappedItem ? "checked" : ""%> value="1">Auto-select next unmapped item
722        </td>
723        <td>
724          <div style="max-height: <%=(int)(scale*350)%>px; overflow: auto;">
725            <b>Destination plate:</b> <span id="plate.name"><i>no plate selected</i></span>
726            <div id="plate"></div>
727          </div>
728        </td>
729      </tr>
730      </table>
731      <%
732    }
733    %>
734      </form>
735    </div>
736    <table align="center">
737    <tr>
738      <td width="50%"><base:button disabled="<%=bioMaterial.size() == 0 %>" onclick="doPlaceOnPlate()" title="Save" /></td>
739      <td width="50%"><base:button onclick="window.close()" title="Cancel" /></td>
740    </tr>
741    </table>
742   
743  </base:body>
744  </base:page>
745  <%
746}
747finally
748{
749  if (dc != null) dc.close();
750}
751%>
Note: See TracBrowser for help on using the repository browser.