source: trunk/www/lims/plates/wells/list_wells.jsp @ 4866

Last change on this file since 4866 was 4866, checked in by Martin Svensson, 13 years ago

Fixes #1282 Change display-format of well coordinates.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Id
File size: 19.8 KB
Line 
1<%-- $Id: list_wells.jsp 4866 2009-03-31 07:36:06Z martin $
2  ------------------------------------------------------------------
3  Copyright (C) 2006 Johan Enell, Jari Hakkinen, Nicklas Nordborg, Martin Svensson
4  Copyright (C) 2007 Johan Enell, Nicklas Nordborg
5
6  This file is part of BASE - BioArray Software Environment.
7  Available at http://base.thep.lu.se/
8
9  BASE is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License
11  as published by the Free Software Foundation; either version 3
12  of the License, or (at your option) any later version.
13
14  BASE is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with BASE. If not, see <http://www.gnu.org/licenses/>.
21  ------------------------------------------------------------------
22
23  @author Nicklas
24  @version 2.0
25--%>
26<%@ page session="false"
27  import="net.sf.basedb.core.SessionControl"
28  import="net.sf.basedb.core.DbControl"
29  import="net.sf.basedb.core.Item"
30  import="net.sf.basedb.core.ItemContext"
31  import="net.sf.basedb.core.AnnotationType"
32  import="net.sf.basedb.core.AnnotationSet"
33  import="net.sf.basedb.core.Annotation"
34  import="net.sf.basedb.core.Plate"
35  import="net.sf.basedb.core.Well"
36  import="net.sf.basedb.core.ReporterType"
37  import="net.sf.basedb.core.Reporter"
38  import="net.sf.basedb.core.ItemQuery"
39  import="net.sf.basedb.core.ItemResultIterator"
40  import="net.sf.basedb.core.ItemResultList"
41  import="net.sf.basedb.core.Permission"
42  import="net.sf.basedb.core.PluginDefinition"
43  import="net.sf.basedb.core.ExtendedProperty"
44  import="net.sf.basedb.core.ExtendedProperties"
45  import="net.sf.basedb.core.PermissionDeniedException"
46  import="net.sf.basedb.core.query.Restrictions"
47  import="net.sf.basedb.core.query.Expressions"
48  import="net.sf.basedb.core.query.Orders"
49  import="net.sf.basedb.core.query.Hql"
50  import="net.sf.basedb.core.plugin.GuiContext"
51  import="net.sf.basedb.core.plugin.Plugin"
52  import="net.sf.basedb.core.data.ReporterData"
53  import="net.sf.basedb.util.Enumeration"
54  import="net.sf.basedb.clients.web.Base"
55  import="net.sf.basedb.clients.web.ModeInfo"
56  import="net.sf.basedb.clients.web.PermissionUtil"
57  import="net.sf.basedb.clients.web.util.HTML"
58  import="net.sf.basedb.util.Values"
59  import="net.sf.basedb.util.formatter.Formatter"
60  import="net.sf.basedb.util.formatter.WellCoordinateFormatter"
61  import="net.sf.basedb.clients.web.formatter.FormatterFactory"
62  import="java.util.ArrayList"
63  import="java.util.List"
64  import="java.util.Map"
65  import="java.util.Date"
66%>
67<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
68<%@ taglib prefix="tbl" uri="/WEB-INF/table.tld" %>
69<%@ taglib prefix="t" uri="/WEB-INF/tab.tld" %>
70<%@ taglib prefix="p" uri="/WEB-INF/path.tld" %>
71<%!
72  private static final Item itemType = Item.WELL;
73  private static final GuiContext guiContext = new GuiContext(itemType, GuiContext.Type.LIST);
74%>
75<%
76final int plateId = Values.getInt(request.getParameter("plate_id"));
77final SessionControl sc = Base.getExistingSessionControl(pageContext, Permission.DENIED, itemType);
78final String ID = sc.getId();
79final ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, null, null);
80
81final ModeInfo mode = ModeInfo.get(request.getParameter("mode"));
82final String callback = request.getParameter("callback");
83final String title = mode.generateTitle("well", "wells");
84final DbControl dc = sc.newDbControl();
85ItemResultIterator<Well> wells = null;
86ItemResultList<AnnotationType> annotationTypes = null;
87try
88{
89  final ItemQuery<AnnotationType> annotationTypeQuery = Base.getAnnotationTypesQuery(itemType);
90  annotationTypes = annotationTypeQuery.list(dc);
91  final Plate plate = Plate.getById(dc, plateId);
92  final boolean createPermission = plate.hasPermission(Permission.WRITE);
93  final boolean deletePermission = createPermission;
94
95  Map<Plugin.MainType, Integer> pluginCount = PluginDefinition.countPlugins(dc, guiContext);
96  final ItemQuery<ReporterType> typeQuery = ReporterType.getQuery();
97  typeQuery.order(Orders.asc(Hql.property("name")));
98  typeQuery.setCacheResult(true);
99  List<ExtendedProperty> reporterProperties = ExtendedProperties.getProperties("ReporterData");
100  Formatter<Date> dateTimeFormatter = FormatterFactory.getDateTimeFormatter(sc);
101  Formatter<Date> dateFormatter = FormatterFactory.getDateFormatter(sc);
102 
103  Enumeration<String,String> rows = new Enumeration<String, String>();
104  WellCoordinateFormatter rowFormatter = new WellCoordinateFormatter(true);
105  for (int i=0; i<plate.getPlateType().getPlateGeometry().getRows(); i++)
106  {
107    rows.add(Integer.toString(i), rowFormatter.format(i));
108  }
109 
110  try
111  {
112    final ItemQuery<Well> query = Base.getConfiguredQuery(dc, cc, true, plate.getWells(), mode);
113    query.join(Hql.leftJoin(null, "reporter", "rpt", null, false));
114    if (!"row".equals(cc.getSortProperty())) query.order(Orders.asc(Hql.property("row")));
115    if (!"column".equals(cc.getSortProperty())) query.order(Orders.asc(Hql.property("column")));
116    wells = query.iterate(dc);
117  }
118  catch (Throwable t)
119  {
120    cc.setMessage(t.getMessage());
121    t.printStackTrace();
122  }
123  int numListed = 0;
124  %>
125  <base:page title="<%=title%>" type="<%=mode.getPageType()%>">
126  <base:head scripts="table.js,tabcontrol.js" styles="table.css,headertabcontrol.css,path.css">
127    <script language="JavaScript">
128    var submitPage = 'index.jsp';
129    var formId = 'wells';
130    function editItem(itemId)
131    {
132      Main.viewOrEditItem('<%=ID%>', '<%=itemType.name()%>', itemId, true);
133    }
134    function viewItem(itemId)
135    {
136      Main.viewOrEditItem('<%=ID%>', '<%=itemType.name()%>', itemId, false);
137    }
138    function itemOnClick(evt, itemId)
139    {
140      Table.itemOnClick(formId, evt, itemId, '<%=mode.getName()%>', viewItem, editItem, returnSelected);
141    }
142    function configureColumns()
143    {
144      Table.configureColumns('<%=ID%>', formId, '<%=itemType.name()%>', '<%=(String)cc.getObject("defaultColumns")%>');
145    }
146    function runPlugin(cmd)
147    {
148      Table.submitToPopup(formId, cmd, 540, 460);
149    }
150    function returnSelected()
151    {
152      Table.returnSelected(formId, <%=callback != null ? "window.opener."+callback : "null" %>);
153      window.close();
154    }
155    function presetOnChange()
156    {
157      Table.presetOnChange('<%=ID%>', formId, '<%=itemType.name()%>', '<%=(String)cc.getObject("defaultColumns")%>');
158    }
159    function switchTab(tabControlId, tabId)
160    {
161      if (tabId == 'properties' || tabId == 'annotations')
162      {
163        location.href = '../index.jsp?ID=<%=ID%>&cmd=ViewItem&item_id=<%=plateId%>&tab='+tabId;
164      }
165      else if (tabId == 'events')
166      {
167        location.href = '../events/index.jsp?ID=<%=ID%>&plate_id=<%=plateId%>';
168      }
169      else
170      {
171        TabControl.setActiveTab(tabControlId, tabId);
172      }
173    }
174    function newReporterList()
175    {
176      Table.submitToPopup(formId, 'CreateReporterList', 540, 400);
177    }
178    </script>
179  </base:head>
180 
181  <base:body>
182    <p>
183    <p:path>
184      <p:pathelement title="Plates" href="<%="../index.jsp?ID="+ID%>" />
185      <p:pathelement title="<%=HTML.encodeTags(plate.getName())%>" />
186    </p:path>
187
188    <t:tabcontrol id="main" active="wells" switch="switchTab">
189    <t:tab id="properties" title="Properties" />
190    <t:tab id="annotations" title="Annotations" />
191    <t:tab id="events" title="Events" />
192   
193    <t:tab id="wells" title="Wells">
194   
195    <%
196    if (cc.getMessage() != null)
197    {
198      %>
199      <div class="error"><%=cc.getMessage()%></div>
200      <%
201      cc.setMessage(null);
202    }
203    %>
204    <tbl:table 
205      id="wells" 
206      clazz="itemlist" 
207      columns="<%=cc.getSetting("columns")%>"
208      sortby="<%=cc.getSortProperty()%>" 
209      direction="<%=cc.getSortDirection()%>"
210      title="<%=title%>"
211      action="index.jsp"
212      sc="<%=sc%>"
213      item="<%=itemType%>"
214      >
215      <tbl:hidden 
216        name="mode" 
217        value="<%=mode.getName()%>" 
218      />
219      <tbl:hidden 
220        name="plate_id" 
221        value="<%=String.valueOf(plateId)%>" 
222      />
223      <tbl:hidden 
224        name="callback" 
225        value="<%=callback%>" 
226        skip="<%=callback == null%>" 
227      />
228      <tbl:columndef 
229        id="row"
230        property="row"
231        datatype="int"
232        title="Row"
233        sortable="true" 
234        filterable="true"
235        exportable="true"
236        enumeration="<%=rows%>"
237        show="always"
238        formatter="<%=new WellCoordinateFormatter(true)%>" 
239      />
240      <tbl:columndef 
241        id="column"
242        property="column"
243        datatype="int"
244        title="Column"
245        sortable="true" 
246        filterable="true"
247        exportable="true"
248        show="always" 
249        formatter="<%=new WellCoordinateFormatter(false)%>" 
250      />
251      <tbl:columndef 
252        id="id"
253        clazz="uniquecol"
254        property="id"
255        datatype="int"
256        title="ID"
257        sortable="true"
258        filterable="true"
259        exportable="true"
260      />
261      <tbl:columndef 
262        id="parent"
263        title="Parent"
264      />
265      <tbl:columndef
266        id="reporterList"
267        property="$rpt.£reporterListScores"
268        datatype="int"
269        title="[Rep] Reporter list"
270        filterable="true"
271        enumeration="<%=Base.getReporterListsEnum(dc)%>"
272        multiple="false"
273      />
274      <tbl:columndef 
275        id="reporter.name"
276        property="reporter.name"
277        datatype="string"
278        title="[Rep] Name"
279        sortable="true" 
280        filterable="true"
281        exportable="true"
282      />
283      <tbl:columndef 
284        id="reporter.externalId"
285        clazz="uniquecol"
286        property="reporter.externalId"
287        datatype="string"
288        title="[Rep] ID"
289        sortable="true" 
290        filterable="true"
291        exportable="true"
292      />
293      <tbl:columndef 
294        id="reporter.symbol"
295        property="reporter.symbol"
296        datatype="string"
297        title="[Rep] Gene symbol"
298        sortable="true" 
299        filterable="true"
300        exportable="true"
301      />
302      <tbl:columndef 
303        id="reporter.description"
304        property="reporter.description"
305        datatype="string"
306        title="[Rep] Description" 
307        sortable="true" 
308        filterable="true" 
309        exportable="true"
310      />
311      <tbl:columndef 
312        id="reporter.entryDate"
313        property="reporter.entryDate"
314        datatype="date"
315        title="[Rep] Registered"
316        sortable="true" 
317        filterable="true"
318        exportable="true"
319        formatter="<%=dateFormatter%>"
320      />
321      <tbl:columndef 
322        id="reporter.lastUpdate"
323        property="reporter.lastUpdate"
324        datatype="date"
325        title="[Rep] Last update" 
326        sortable="true" 
327        filterable="true" 
328        exportable="true"
329        formatter="<%=dateTimeFormatter%>"
330      />
331      <%
332      Enumeration<String, String> types = new Enumeration<String, String>();
333      ItemResultList<ReporterType> reporterTypes = typeQuery.list(dc);
334      types.add("", "- none -");
335      for (ReporterType rt : reporterTypes)
336      {
337        types.add(Integer.toString(rt.getId()), HTML.encodeTags(rt.getName()));
338      }
339      %>
340      <tbl:columndef 
341        id="reporter.reporterType"
342        property="reporter.reporterType"
343        sortproperty="reporter.reporterType.name"
344        exportproperty="reporter.reporterType.name"
345        datatype="int"
346        enumeration="<%=types%>"
347        title="[Rep] Type"
348        sortable="true" 
349        filterable="true"
350        exportable="true"
351      />
352      <%
353      if (reporterProperties != null)
354      {
355        for (ExtendedProperty ep : reporterProperties)
356        {
357          String name = ep.getName();
358          %>
359          <tbl:columndef
360            id="<%="reporter."+name%>"
361            property="<%="reporter."+name%>"
362            datatype="<%=ep.getType().getStringValue()%>"
363            title="<%="[Rep] "+HTML.encodeTags(ep.getTitle())%>"
364            sortable="true"
365            filterable="true"
366            exportable="true"
367            formatter="<%=FormatterFactory.getExtendedPropertyFormatter(sc, ep)%>"
368          />
369          <%
370        }
371      }
372      %>
373      <%
374      for (AnnotationType at : annotationTypes)
375      {
376        Enumeration<String, String> annotationEnum = null;
377        Formatter formatter = FormatterFactory.getTypeFormatter(sc, at.getValueType());
378        if (at.isEnumeration())
379        {
380          annotationEnum = new Enumeration<String, String>();
381          List<?> values = at.getValues();
382          for (Object value : values)
383          {
384            String encoded = formatter.format(value);
385            annotationEnum.add(encoded, encoded);
386          }
387        }
388        %>
389        <tbl:columndef 
390          id="<%="at"+at.getId()%>"
391          title="<%=HTML.encodeTags(at.getName())+" [A]"%>" 
392          property="<%="#"+at.getId()%>"
393          annotation="true"
394          datatype="<%=at.getValueType().getStringValue()%>"
395          enumeration="<%=annotationEnum%>"
396          sortable="false" 
397          filterable="true" 
398          exportable="true"
399          formatter="<%=formatter%>"
400          unit="<%=at.getDefaultUnit()%>"
401        />
402        <%
403      }
404      %>
405      <tbl:columndef
406        id="permission"
407        title="Permission"
408      />
409      <tbl:toolbar
410        visible="<%=mode.hasToolbar()%>"
411        >
412        <tbl:button 
413          image="columns.gif" 
414          onclick="configureColumns()" 
415          title="Columns&hellip;" 
416          tooltip="Show, hide and re-order columns" 
417        />
418        <tbl:button
419          image="add.png"
420          onclick="newReporterList()"
421          title="New reporter list&hellip;"
422          tooltip="Create a new reporter list from matching wells"
423          visible="<%=sc.hasPermission(Permission.CREATE, Item.REPORTERLIST)%>"
424        />
425        <tbl:button 
426          image="import.gif" 
427          onclick="runPlugin('ImportItems')" 
428          title="Import&hellip;" 
429          tooltip="Import data" 
430          visible="<%=pluginCount.containsKey(Plugin.MainType.IMPORT)%>"
431        />
432        <tbl:button 
433          image="export.gif" 
434          onclick="runPlugin('ExportItems')" 
435          title="Export&hellip;" 
436          tooltip="Export data" 
437          visible="<%=pluginCount.containsKey(Plugin.MainType.EXPORT)%>"
438        />
439        <tbl:button 
440          image="runplugin.gif" 
441          onclick="runPlugin('RunListPlugin')" 
442          title="Run plugin&hellip;" 
443          tooltip="Run a plugin" 
444          visible="<%=pluginCount.containsKey(Plugin.MainType.OTHER)%>"
445        />
446      </tbl:toolbar>
447      <tbl:navigator
448        page="<%=cc.getPage()%>" 
449        rowsperpage="<%=cc.getRowsPerPage()%>" 
450        totalrows="<%=wells == null ? 0 : wells.getTotalCount()%>" 
451        visible="<%=mode.hasNavigator()%>"
452      />
453      <tbl:data>
454        <tbl:columns>
455        <tbl:presetselector 
456          clazz="columnheader"
457          colspan="3"
458          onchange="presetOnChange()"
459        />
460        </tbl:columns>
461         
462        <tr>
463          <tbl:header 
464            clazz="index"
465            >&nbsp;</tbl:header>
466          <tbl:header 
467            clazz="check" 
468            visible="<%=mode.hasCheck()%>"
469            ><base:icon 
470              image="check_uncheck.gif" 
471              tooltip="Check/uncheck all" 
472              onclick="Forms.checkUncheck(document.forms[formId])" style="align: left;"
473            /></tbl:header>
474          <tbl:header 
475            clazz="check" 
476            visible="<%=mode.hasRadio()%>"
477            />
478          <tbl:header 
479            clazz="icons" 
480            visible="<%=mode.hasIcons()%>"
481            >&nbsp;</tbl:header>
482          <tbl:propertyfilter />
483        </tr>
484   
485          <tbl:rows>
486          <%
487          int index = cc.getPage()*cc.getRowsPerPage();
488          int selectedItemId = cc.getId();
489          if (wells != null)
490          {           
491            while (wells.hasNext())
492            {
493              Well item = wells.next();
494              int itemId = item.getId();
495              boolean writePermission = item.hasPermission(Permission.WRITE);
496              String tooltip = mode.isSelectionMode() ? 
497                  "Select this item" : "View this item" + (writePermission ? " (use CTRL, ALT or SHIFT to edit)" : ""); 
498              index++;
499              numListed++;
500              %>
501              <tbl:row>
502                <tbl:header 
503                  clazz="index"
504                  ><%=index%></tbl:header>
505                <tbl:header 
506                  clazz="check" 
507                  visible="<%=mode.hasCheck()%>"
508                  ><input 
509                      type="checkbox" 
510                      name="<%=itemId%>" 
511                      value="<%=itemId%>" 
512                      title="TODO" 
513                      <%=cc.getSelected().contains(itemId) ? "checked" : ""%>
514                    ></tbl:header>
515                <tbl:header 
516                  clazz="check" 
517                  visible="<%=mode.hasRadio()%>"
518                  ><input 
519                      type="radio" 
520                      name="item_id" 
521                      value="<%=itemId%>" 
522                      title="TODO" 
523                      <%=selectedItemId == itemId ? "checked" : ""%>
524                    ></tbl:header>
525                <tbl:header 
526                  clazz="icons" 
527                  visible="<%=mode.hasIcons()%>"
528                  >&nbsp;</tbl:header>
529                <tbl:cell column="row"><div class="link" 
530                  onclick="itemOnClick(<%=writePermission ? "event" : null%>, <%=itemId%>)" 
531                  title="<%=tooltip%>"><tbl:cellvalue value="<%=item.getRow()%>" /></div></tbl:cell>
532                <tbl:cell column="column"><tbl:cellvalue value="<%=item.getColumn()%>"/></tbl:cell>
533                <tbl:cell column="id"><%=item.getId()%></tbl:cell>
534                <tbl:cell column="parent">
535                  <%
536                  try
537                  {
538                    Well parent = item.getParent();
539                    if (parent == null)
540                    {
541                      %>
542                      <i>- none -</i>
543                      <%
544                    }
545                    else
546                    {
547                      %>
548                      <%=Base.getLink(ID, 
549                        parent.getPlate().getName() + " [" + parent.getRow() + ", " + parent.getColumn() + "]", 
550                        Item.WELL, parent.getId(), true)%>
551                      <%
552                    }
553                  }
554                  catch (PermissionDeniedException ex)
555                  {
556                    %>
557                    <i>- denied -</i>
558                    <%
559                  }
560                  %>
561                </tbl:cell>
562                <%
563                ReporterData reporter = item.getReporter();
564                if (reporter == null)
565                {
566                  %>
567                  <tbl:cell column="reporter.name"><i>- none -</i></tbl:cell>
568                  <tbl:cell column="reporter.externalId"><i>- none -</i></tbl:cell>
569                  <%
570                }
571                else
572                {
573                  %>
574                  <tbl:cell column="reporter.name"><%=Base.getLink(ID, reporter.getName(), Item.REPORTER, reporter.getId(), true)%></tbl:cell>
575                  <tbl:cell column="reporter.externalId"><%=HTML.encodeTags(reporter.getExternalId())%></tbl:cell>
576                  <tbl:cell column="reporter.symbol"><%=HTML.encodeTags(reporter.getSymbol())%></tbl:cell>
577                  <tbl:cell column="reporter.description"><%=HTML.encodeTags(reporter.getDescription())%></tbl:cell>
578                  <tbl:cell column="reporter.entryDate" value="<%=reporter.getEntryDate()%>" />
579                  <tbl:cell column="reporter.lastUpdate" value="<%=reporter.getLastUpdate()%>" />
580                  <tbl:cell column="reporter.reporterType"
581                    ><base:propertyvalue 
582                      dbcontrol="<%=dc%>" 
583                      item="<%=reporter%>" 
584                      property="reporterType"
585                      enableEditLink="<%=mode.hasEditLink()%>" 
586                      enablePropertyLink="<%=mode.hasPropertyLink()%>"
587                    /></tbl:cell>
588                  <%
589                  if (reporterProperties != null)
590                  {
591                    for (ExtendedProperty ep : reporterProperties)
592                    {
593                      String name = ep.getName();
594                      %>
595                      <tbl:cell column="<%="reporter."+name%>"><tbl:cellvalue value="<%=reporter.getExtended(name)%>" /></tbl:cell>
596                      <%
597                    }
598                  }
599                }
600                AnnotationSet as = item.isAnnotated() ? item.getAnnotationSet() : null;
601                if (as != null)
602                {
603                  for (AnnotationType at : annotationTypes)
604                  {
605                    if (as.hasAnnotation(at))
606                    {
607                      Annotation a = as.getAnnotation(at);
608                      String suffix = a.getUnitSymbol(null);
609                      if (suffix != null) suffix = "&nbsp;" + suffix;
610                      %>
611                      <tbl:cell 
612                        column="<%="at"+at.getId()%>"
613                        ><tbl:cellvalue 
614                          list="<%=a.getValues(null)%>"
615                          suffix="<%=suffix%>"
616                      /></tbl:cell>
617                      <%
618                    }
619                  }
620                }
621                %>
622                <tbl:cell column="permission"><%=PermissionUtil.getShortPermissions(item)%></tbl:cell>
623              </tbl:row>
624              <%
625            }
626          }
627          %>
628          </tbl:rows>
629        </tbl:data>
630      <%
631      if (numListed == 0)
632      {
633        %>
634        <tbl:panel><%=wells == null || wells.getTotalCount() == 0 ? "No wells were found" : "No wells on this page. Please select another page!" %></tbl:panel>
635        <%
636      }
637      else
638      {
639        %>
640        <tbl:navigator
641          page="<%=cc.getPage()%>" 
642          rowsperpage="<%=cc.getRowsPerPage()%>" 
643          totalrows="<%=wells == null ? 0 : wells.getTotalCount()%>" 
644          visible="<%=mode.hasNavigator()%>"
645          locked="true"
646        />
647        <%
648      }
649      %>
650    </tbl:table>
651    <base:buttongroup align="center">
652      <base:button onclick="returnSelected();" title="Ok" visible="<%=mode.hasOkButton()%>" />
653      <base:button onclick="window.close();" title="Cancel" visible="<%=mode.hasCancelButton()%>" />
654      <base:button onclick="window.close();" title="Close" visible="<%=mode.hasCloseButton()%>" />
655    </base:buttongroup>
656 
657    </t:tab>
658    </t:tabcontrol>
659
660 
661  </base:body>
662  </base:page>
663  <%
664}
665finally
666{
667  if (wells != null) wells.close();
668  if (dc != null) dc.close();
669}
670%>
Note: See TracBrowser for help on using the repository browser.