CINXE.COM
Webclient
<?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="X-UA-Compatible" content="IE=Edge" /> <meta name="referrer" content="origin-when-crossorigin"> <link rel="stylesheet" href="/static/webgateway/css/reset.css?_5.26.0" type="text/css" /> <link rel="stylesheet" href="/static/webgateway/css/ome.body.css?_5.26.0" type="text/css" /> <link rel="stylesheet" href="/static/webclient/css/dusty.css?_5.26.0" type="text/css" media="screen"/> <link rel="stylesheet" href="/static/webgateway/css/ome.header.css?_5.26.0" type="text/css" /> <link rel="stylesheet" href="/static/webclient/css/layout.css?_5.26.0" type="text/css" media="screen"/> <link rel="stylesheet" href="/static/webgateway/css/ome.toolbar.css?_5.26.0" type="text/css" media="screen"/> <style type="text/css"> body { min-width: 1010px; overflow: hidden; } </style> <link rel="stylesheet" href="/static/3rdparty/jquery.chosen-1.8.7/chosen.css" type="text/css" media="screen"/> <link rel="stylesheet" href="/static/webgateway/css/ome.jstree_theme.css" type="text/css" /> <!-- extend here to add page title --> <title> Webclient </title> <!-- block for javascript <script> --> <script src="/static/3rdparty/jquery-3.6.2.min.js"></script> <script type="text/javascript" src="/static/webgateway/js/ome.csrf.js?_5.26.0"></script> <!--[if lte IE 8]> <script> $(function(){ $(".header_toolbar li:last-child").addClass('last-child'), $("#activities_panel table tr:last-child").addClass('last-child'), $("#groupsUsersButton span:last-child").addClass('last-child') }) </script> <![endif]--> <!-- Include our current version of jQuery-UI and an ome utility script used by many pages --> <link rel="stylesheet" href="/static/3rdparty/jquery-ui-1.13.2/css/jquery-ui.css" type="text/css" /> <script type="text/javascript" src="/static/3rdparty/jquery-ui-1.13.2/js/jquery-ui.js"></script> <!-- Disable JQuery UI Tabs Keyboard Navigation --> <script type="text/javascript"> $.widget("ui.tabs", $.ui.tabs, { _tabKeydown: function (event) { if (event.keyCode !== 37 && event.keyCode !== 38 && event.keyCode !== 39 && event.keyCode !== 40) { this._super(event); } } }); </script> <script type="text/javascript" src="/static/webgateway/js/ome.popup.js?_5.26.0"></script> <!-- Turn touch events into drag events for jquery-ui --> <script type="text/javascript" src="/static/3rdparty/jquery.ui.touch-punch-0.2.3.min.js"></script> <!-- Make the width fit the mobile viewport --> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <!-- Allow webclient to be 'added to home-screen' as a full-screen app on iOS --> <meta name="apple-mobile-web-app-capable" content="yes"> <script type="text/javascript" src="/static/webgateway/js/ome.nav.js?_5.26.0"></script> <!-- handles expand / collapse columns --> <script type="text/javascript"> $(document).ready(function(){ }); </script> <!-- required for the script_launch html below --> <script type="text/javascript"> $(document).ready(function(){ $('body').on('click', function(event) { if (!$(event.target).hasClass("scriptButton")) { OME.hideScriptList(); } }); // Loading Scripts with AJAX for drop-down menu, (only the first time we click) // handles clicking on a script (if href is not '#') $("#scriptList.sub_menu").on('click', 'a', OME.openScriptWindow); $("#scriptList.sub_menu").on('click', 'a', function(event) { var $a = $(event.target), script_url = $a.attr('href'); if (script_url != "#") { // Clicked on script - handled by OME.openScriptWindow above $("#scriptList").hide(); return false; } // we have clicked on <a> within a <li>, with sibling <ul> var $li = $a.parent(), $ul = $li.children('ul'); if ($li.hasClass('menu_back')) { $li.parent().parent().siblings().show(); $li.parent().hide(); $li.parent().siblings('a').show(); } else { $ul.show(); $li.siblings().hide(); $a.hide(); } }); $("#scriptButton").on('click', {webindex: "/webclient/"}, OME.showScriptList ); }); </script> <script type="text/javascript" src="/static/webclient/javascript/ome.webclient.actions.js?_5.26.0"></script> <script src="/static/webclient/javascript/jquery.infieldlabel-0.1.js" type="text/javascript"></script> <!-- Centre panel templating --> <script type="text/javascript" src="/static/3rdparty/underscore/underscore-1.13.6-umd-min.js"></script> <!-- Include the underscore html templates --> <script id="icon_thumbnails_template" type="text/template"> <ul id="dataIcons" class="element_sorter <%= layout %>Layout"> <li class="thead"> <div></div> <!-- 'sort-init' will perform sort on this column when element_sorter plugin inits --> <div class='sort-alpha sort-init'>Name</div> <div class='sort-date'>Date</div> <div class='sort-numeric'>Size X</div> <div class='sort-numeric'>Size Y</div> <div class='sort-numeric'>Size Z</div> <div class='sort-numeric'>Size T</div> </li> <% _.each(images, function(img) { %> <li class="row <% if(img.selected) {print ('ui-selected')} %> <% if(img.fsSelected) {print ('fs-selected')} %> " id="image_icon-<%- img.id %>" data-fileset="<%- img.data.obj.filesetId %>" data-type="image" data-id="<%- img.id %>" data-perms="<%= img.data.obj.permsCss %>" tabindex="0" <% if(img.shareId) { %> data-share="<%- img.shareId %>" <% }%> data-owned=""> <!-- This div is the first column in table layout --> <div class="image"> <!-- we wrap img with <a> so you can right-click -> open link in new tab --> <a href="<%= webindex %><% if(img.shareId) {print( img.shareId + '/')} %>img_detail/<%- img.id %>/<% if (dataset) print('?dataset=' + dataset) %>" > <img/> </a> </div> <!-- NB: '#image_icon-123 div.desc' etc is used to update name when changed in right panel via "editinplace" --> <div class="desc" valign="middle"> <%- img.name %> <span class="hidden_sort_text"> <%- img.name %> </span> </div> <div class="date" valign="middle"><%- img.date %></div> <div class="sizeX" valign="middle"><%- img.data.obj.sizeX %></div> <div class="sizeY" valign="middle"><%- img.data.obj.sizeY %></div> <div class="sizeZ" valign="middle"><%- img.data.obj.sizeZ %></div> <div class="sizeT" valign="middle"><%- img.data.obj.sizeT %></div> </li> <% }) %> </ul> <% if (paging) { %> <div class="clear"> </div> <div class="paging" id="page" data-page="<%= page %>"> <% if (page-1 > 0) { %> <input data-page="<%= page-1 %>" class="button_pagination" type="button" value="Previous" alt="Previous"/> <% } %> <% for (var p=1; p<=pageCount; p++) { %> <% if (page === p) { print (p) } else { %> <input data-page="<%= p %>" class="button_pagination" type="button" value="<%= p %>" alt="<%= p %>"/> <% } %> <% } %> <% if (page < pageCount) { %> <input data-page="<%= page+1 %>" class="button_pagination" type="button" value="Next" alt="Next"/> <% } %> </div> <div class="clear"> </div> <% } %> </script> <script id="icon_header_template" type="text/template"> <div id="center_toolbar" class="toolbar"> <div id="layout_chooser"> <button id="icon_layout" title="View as Thumbnails" class="<% if (layout === 'icon') print('checked') %>"> </button><button id="table_layout" title="View as List" class="<% if (layout === 'table') print('checked') %>"></button> </div> <div class="search filtersearch" id="filtersearch"> <select id="choosefilter"> <option value="addfilter">Add filter</option> <option value="rating">By Rating</option> <option value="unrated">Unrated</option> <option value="name">Name</option> <option value="tag">Tag</option> <option value="map">Key-Values</option> <option disabled>──────────</option> <option value="removeAll">Remove all filters</option> </select> <div id="filtersContainer" style="float:left"> <div id="filterrating" class="imagefilter"> <img title="Filter by rating (exact match)" src="<%= staticUrl %>image/rating0.png" /> <span title="Clear filter" class="removefilter" style="vertical-align:top">X</span> </div> <div id="filterunrated" class="imagefilter"> <span style="font-size: 15px">Unrated</span> <span title="Remove filter" class="removefilter" style="vertical-align:top">X</span> </div> <div id="filtername" class="imagefilter"> <label for="id_search"> Filter by name </label> <input id="id_search" type="text" size="25" /> </div> <span class="loading" style="display: none;"></span> </div> </div> <div id="filtertag" class="imagefilter filtersearch"> <select id="filter_by_tag"> <!-- Tags loaded here... --> </select> <span title="Remove all Tag filters" class="removefilter" style="float:left">X</span> <div id="currentFilterTags"> <!-- Current tags here --> </div> </div> </div> <div id="icon_table"></div> <div style="height:25px" class="toolbar"> <div style="position: absolute; right: 10px; bottom: 0; width: 200px; height: 25px"> <span class="zoomLabel">Zoom:</span> <div id="thumb_size_slider" class="thumb_size_slider" title="Zoom Thumbnails"></div> </div> </div> </script> <script id="tags_template" type="text/template"> <% _.each(tags, function(tag) { %> <span class="tag_annotation_wrapper" data-tag-id="<%- tag.id %>" data-added-by="<% print(_.escape(tag.addedBy.join(','))) %>"> <div class="tag"> <a class='tooltip tag_inner' href="?show=tag-<%- tag.id %>" target="_top"> <%- tag.textValue %> </a> <% if (tag.canRemove) { %> <span class='removeTag' title="Remove Tag" id="<%- tag.id %>-tag" url='<%= webindex %>action/remove/tag/<%- tag.id %>/'> - </span> <% } %> <span class="tooltip_html" style='display:none'> <!-- show different tooltip for batch_annotate panel --> <% if (tag.links) { %> Can remove Tag from <b><%- tag.canRemoveCount %> object<% if(tag.canRemoveCount !== 1) {print('s')} %></b>:<br/> <% _.each(tag.links, function(link, idx) { %> <div> <% if (idx < 20) { %> <b><%- link.parent.class %> <%- link.parent.id %></b> <%- link.parent.name.slice(0, 28) %> <% if (link.owner.id !== userId) { print("(" + link.owner.firstName.slice(0, 1) + " " + _.escape(link.owner.lastName) + ")") } %> <% } else if (idx === 20) { %> and <b><% print(tag.links.length - 20) %></b> other objects... <% } %> </div> <% }) %> <% } else { %> <b>ID:</b> <%- tag.id %><br /> <b>Owner:</b> <%- tag.owner.firstName %> <%- tag.owner.lastName %><br /> <b>Linked by:</b> <%- tag.link.owner.firstName %> <%- tag.link.owner.lastName %><br /> <b>On:</b> <% print(OME.formatDate(tag.link.date)) %><br /> <b>Description:</b> <%- tag.description %> <% } %> </span> </div> </span> <% }) %> </script> <script id="fileanns_template" type="text/template"> <% _.each(anns, function(ann) { %> <li class="file_ann_wrapper" id="file_ann-<%- ann.id %>" data-added-by="<% print(_.escape(ann.addedBy.join(','))) %>"> <a class='tooltip' href="<% if (ann.permissions.canDownload) { print(webindex + 'annotation/' + ann.id)} else {print('#') } %>"> <%- ann.file.name %> <% if (ann.file.size) { %> <span>(<%- ann.file.size %>)</span> <% } %> </a> <span class="tooltip_html" style='display:none'> <!-- show different tooltip for batch_annotate panel --> <% if (ann.links) { %> Can remove File from <b><%- ann.canRemoveCount %> object<% if(ann.canRemoveCount !== 1) {print('s')} %></b>:<br/> <% _.each(ann.links, function(link, idx) { %> <div> <% if (idx < 20) { %> <b><%- link.parent.class %> <%- link.parent.id %></b> <% print(_.escape(link.parent.name).slice(0, 28)) %> <% if (link.owner.id !== userId){ print("(" + link.owner.firstName.slice(0, 1) + " " + _.escape(link.owner.lastName) + ")") } %> <% } else if (idx === 20) { %> and <b><% print(ann.links.length - 20) %></b> other objects... <% } %> </div> <% }) %> <% } else { %> <b>Annotation ID:</b> <%- ann.id %><br /> <b>Owner:</b> <%- ann.owner.firstName %> <%- ann.owner.lastName %><br /> <b>Linked by:</b> <%- ann.link.owner.firstName %> <%- ann.link.owner.lastName %><br /> <b>On:</b> <% print(OME.formatDate(ann.link.date)) %> <br /> <b>Description:</b> <%- ann.description %> <% if (ann.ns){ %> <br/><b>Namespace:</b> <%- ann.ns %> <% } %> <% if (ann.file.mimetype){ %> <br/><b>Mimetype:</b> <%- ann.file.mimetype %> <% } %> <br/><b>File ID:</b> <%- ann.file.id %> <% } %> </span> <div class="attachment_actions"> <input type="checkbox" style="display:none;"/> <% if ((ann.ns && ann.ns === 'openmicroscopy.org/omero/bulk_annotations') || ann.file.mimetype == 'OMERO.tables' ){ %> <a class='action btn_view' title="View OMERO.table" target="_blank" href='<%= webindex %>omero_table/<%- ann.file.id %>/'> </a> <% } %> <% if (ann.link.permissions.canDelete) { %> <!-- and not ann.isOriginalMetadata --> <a class='removeFile action' id="<%- ann.id %>-file" title="Remove File" href='<%= webindex %>action/remove/file/<%- ann.id %>/'>–</a> <% } %> <% if (ann.permissions.canDelete) { %> <!-- and not ann.isOriginalMetadata --> <a id="<%- ann.id %>-file" type="image" class="deleteFile action" title="Delete File" href="<%= webindex %>action/delete/file/<%- ann.id %>/"> × </a> <% } %> </div> </li> <% }) %> </script> <script id="comments_template" type="text/template"> <% _.each(anns, function(ann) { %> <div class="ann_comment_wrapper" data-added-by="<% print(_.escape(ann.addedBy.join(','))) %>"> <div class="avatar"> <img src="<%= webindex %>avatar/<%- ann.owner.id %>/" alt="<%- ann.owner.firstName %> <%- ann.owner.lastName %>" title="<%- ann.owner.firstName %> <%- ann.owner.lastName %>" /> </div> <div class="ann_comment_text tooltip"> <div class="ann_comment_header"> <strong> <%- ann.owner.firstName %> <%- ann.owner.lastName %> </strong> at <% print(OME.formatDate(ann.link.date)) %> </div> <% if (ann.permissions.canDelete) { %> <img class='removeComment' id="<%- ann.id %>-comment" src="<%= static %>image/icon_basic_delete.png" url='<%= webindex %>action/remove/comment/<%- ann.id %>/' title="Delete comment"/> <% } %> <div class='commentText'><%- ann.textValue %></div> </div> <% if (ann.ns || ann.description) { %> <span class="tooltip_html" style='display:none'> <b>ID:</b> <%- ann.id %><br /> <% if (ann.ns) { %><b>Namespace:</b> <%- ann.ns %><br /><% } %> <% if (ann.description) { %><b>Description:</b> <%- ann.description %><br /><% } %> </span> <% } %> </div> <% }) %> </script> <script id="mapanns_template" type="text/template"> <% _.each(anns, function(ann) { %> <table <% if (ann.id && ann.addedBy) { %> data-annId="<%- ann.id %>" data-added-by="<% print(_.escape(ann.addedBy.join(','))) %>" <% } else { %> data-added-by="<%= WEBCLIENT.USER.id %>" <% } %> class="keyValueTable <% if (!ann.id || (ann.permissions.canEdit && clientMapAnn && !isInherited)){ %> editableKeyValueTable <% } %> "> <thead> <% if (showNs && ann.ns && !isInherited) { %> <tr title="<%- ann.ns %>"> <th colspan="2"> <%- ann.ns.slice(0, 50) %> </th> </tr> <% } %> <tr class="tooltip"> <th colspan="2"> <% if (ann.id) { %> <% if (isInherited) { %> Added on <%- ann.link.parent.class.substring(0, ann.link.parent.class.length - 1) %> <%- ann.link.parent.name.slice(0, 30) %> <% } else { %> Added by: <%- ann.owner.firstName %> <%- ann.owner.lastName %> <% if (showParent && ann.link.parent.name){ %> <br> <% if (ann.parentNames) { %> <%- ann.parentNames.length %> Annotations linked to: <% } else { %> To: <% } %> <%- ann.parentNames ? (ann.parentNames.length + " objects") : ann.link.parent.name %> <% } %> <% } %> <span class="tooltip_html" style='display:none'> <!-- ann.parentNames is a property of grouped annotations --> <% if (ann.parentNames) { %> You are <% print (ann.permissions.canEdit && clientMapAnn ? 'editing' : 'viewing') %> <b><%- ann.parentNames.length %></b> identical Key-Value annotations:<br /> <% } else if (ann.link) { %> <!-- If single object show e.g. Image ID: (slice ImageI -> Image) --> <b><%- ann.link.parent.class.slice(0, ann.link.parent.class.length-1) %> ID:</b> <%- ann.link.parent.id %><br /> <% } %> <b>Annotation ID<% if (ann.parentNames) { %>s<% } %>:</b> <%= ann.id %><br /> <% if (ann.parentNames) { %> <b>Linked to:</b><br> <% _.each(ann.parentNames, function(pName) { %>   <%- pName %><br /> <% }) %> <% } %> <% if (ann.owner) { %> <b>Owner:</b> <%- ann.owner.firstName %> <%- ann.owner.lastName %><br /> <% } %> <% if (ann.link) { %> <b>Linked by:</b> <%- ann.link.owner.firstName %> <%- ann.link.owner.lastName %><br /> <% if (ann.link.date) { %> <b>On:</b> <% print(OME.formatDate(ann.link.date)) %><br /> <% } %> <% } %> <% if (isInherited) { %> <% if (showParent) { %> <b>Inherited by:</b><br /> <% _.each(ann.childNames, function(cname) { %>   <%- cname %><br /> <% }) %> <% } %> <b>Namespace:</b> <%- ann.ns.slice(0, 50) %><br /> <% } %> </span> <% } else if (objCount && objCount > 1) { %> Add annotations to <%- objCount %> objects <span class="tooltip_html" style='display:none'> Identical Key-Value annotations will be added to each selected object. </span> <% } %> </th> </tr> <% if (showTableHead) { %> <tr> <th>Key</th> <th>Value</th> </tr> <% } %> </thead> <!-- Last row always has empty key & value fields --> <% if (ann.id) { %> <% _.each(ann.values, function(row) { %> <tr> <td><%- row['0'] %></td> <td><%- row['1'] %></td> </tr> <% }) %> <% } else { %> <tr class="placeholder"> <td>Add Key</td> <td>Add Value</td> </tr> <% } %> </table> <% }) %> </script> <script id="ratings_template" type="text/template"> <ul class="lnfiles"> <li class="rating myRating" style="position: relative"> <a><img <% if (canAnnotate) { %> title="Click to add rating" <% } else { %> title="You don't have permissions to add rating" <% } %> <% if (average) { %> src="<%= static %>image/rating<%= average %>.png" <% } else { %> src="<%= static %>image/rating0.png" <% } %> /> </a> <% if (canAnnotate) { %> <a href="#" class="removeRating removeTag" title="Delete rating">X</a> <% } %> </li> </ul> <div id="ratingsSummary" <% if (!average) { %>style="display:none"<% } %> > (avg: <span id="ratingsAverage"><%= average %></span> / <span id="ratingsCount"><%= count %></span> votes) </div> </script> <script id="customanns_template" type="text/template"> <% _.each(anns, function(ann) { %> <tr data-added-by="<% print(_.escape(ann.addedBy.join(','))) %>"> <th><%- ann.type %></th> <td> <div class="tooltip"> <% if (ann.type === 'Xml') { %> <div><% print(_.escape((ann.value + "").slice(0, 20))) %>...</div> <div class="show_xml">Open in new window</div> <div style="display: none"><%- ann.value %></div> <% } else if (ann.value != null) { %> <div><%- ann.value %></div> <% } else if (ann.type === 'List' && ann.name) { %> <div><%- ann.name %></div> <% } else { %> <div>(No data)</div> <% } %> </div> <span class="tooltip_html" style='display:none'> <b>ID:</b> <%- ann.id %><br /> <% if (ann.ns) { %><b>Namespace:</b> <%- ann.ns %><br /><% } %> <% if (ann.name) { %><b>Name:</b> <%- ann.name %><br /><% } %> <% if (ann.description) { %><b>Description:</b> <%- ann.description %><br /><% } %> <b>Owner:</b> <%- ann.owner.firstName %> <%- ann.owner.lastName %><br /> <b>Date:</b> <% print(OME.formatDate(ann.date)) %> <% if (ann.parent) { %> <br /> <b>Linked to:</b> <%- ann.parent.class %> <%- ann.parent.id %> <% } %> </span> </td> </tr> <% }) %> </script> <!-- Toolbar --> <script type="text/javascript" src="/static/3rdparty/jquery.chosen-1.8.7/chosen.jquery.js"></script> <!-- The following are required by the right-hand panel, E.g. annotations/metadata_general.html --> <script type="text/javascript" src="/static/3rdparty/jquery.quicksearch-1.0.js"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.editinplace-0.1.2.js"></script> <script type="text/javascript" src="/static/3rdparty/jquery.form-4.3.0.js"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.tagging_form.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.right_panel_tags_pane.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.right_panel_fileanns_pane.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.right_panel_comments_pane.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.right_panel_customanns_pane.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.right_panel_ratings_pane.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.right_panel_mapanns_pane.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.progress_overlay.js?_5.26.0"></script> <!-- preview viewer... --> <script type="text/javascript" src="/static/3rdparty/jquery.blockUI-2.66.0.js"></script> <script type="text/javascript" src="/static/webgateway/js/ome.viewport.js?_5.26.0"></script> <script type="text/javascript" src="/static/webgateway/js/ome.viewportImage.js?_5.26.0"></script> <script type="text/javascript" src="/static/webgateway/js/ome.gs_slider.js?_5.26.0"></script> <script type="text/javascript" src="/static/webgateway/js/ome.gs_utils.js?_5.26.0"></script> <script type="text/javascript" src="/static/webgateway/js/ome.scalebardisplay.js?_5.26.0"></script> <link rel="stylesheet" type="text/css" href="/static/3rdparty/JQuerySpinBtn-1.3a/JQuerySpinBtn.css" /> <link rel="stylesheet" type="text/css" href="/static/webgateway/css/ome.colorbtn.css?_5.26.0" /> <link rel="stylesheet" type="text/css" href="/static/3rdparty/farbtastic-1.2/farbtastic.css" media="all" /> <link rel="stylesheet" type="text/css" href="/static/webgateway/css/ome.postit.css?_5.26.0" /> <script type="text/javascript" src="/static/3rdparty/JQuerySpinBtn-1.3a/JQuerySpinBtn.js"></script> <script type="text/javascript" src="/static/webgateway/js/ome.colorbtn.js?_5.26.0"></script> <script type="text/javascript" src="/static/webgateway/js/omero_image.js?_5.26.0"></script> <script type="text/javascript" src="/static/webgateway/js/ome.smartdialog.js?_5.26.0"></script> <script type="text/javascript" src="/static/3rdparty/aop-1.3.js"></script> <script type="text/javascript" src="/static/webgateway/js/ome.postit.js?_5.26.0"></script> <script type="text/javascript" src="/static/3rdparty/farbtastic-1.2/farbtastic.js"></script> <script type="text/javascript" src="/static/3rdparty/d3-7.7.0/d3.min.js"></script> <script type="text/javascript" src="/static/webgateway/js/ome.histogram.js?_5.26.0"></script> <!-- ...including big images --> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/utils.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/PanoJS.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/controls.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/pyramid_Bisque.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/pyramid_imgcnv.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/pyramid_Zoomify.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/control_thumbnail.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/control_info.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/control_svg.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/control_roi.js"></script> <script type="text/javascript" src="/static/3rdparty/panojs-2.0.0/control_scalebar.js"></script> <script type="text/javascript" src="/static/3rdparty/jquery.mousewheel-3.1.13.js"></script> <!-- hammer.js for tablet gestures --> <script type="text/javascript" src="/static/3rdparty/hammer-2.0.8/hammer.min.js"></script> <link rel="stylesheet" type="text/css" href="/static/3rdparty/panojs-2.0.0/panojs.css" media="all" /> <link rel="stylesheet" href="/static/webgateway/css/ome.gs_slider.css?_5.26.0" type="text/css" media="screen"/> <link rel="stylesheet" href="/static/webgateway/css/ome.viewport.css?_5.26.0" type="text/css" media="screen"/> <!-- scripts for SPW plate.html --> <script type="text/javascript" src="/static/webgateway/js/ome.plateview.js?_5.26.0"></script> <script type="text/javascript" src="/static/webgateway/js/ome.spwgridview.js?_5.26.0"></script> <script> // OMERO constants if (OMERO === undefined) {var OMERO = {};} OMERO['constants'] = { 'namespaces': { 'NSCOMPANIONFILE': "openmicroscopy.org/omero/import/companionFile" }, 'annotation': { 'file': {'ORIGINALMETADATA': "original_metadata.txt"} }, 'metadata': { 'NSCLIENTMAPANNOTATION': "openmicroscopy.org/omero/client/mapAnnotation" } }; // Variables used by static files, particularly ome.tree.js for jstree setup // Also used by right panel annotation tabs var WEBCLIENT = {}; WEBCLIENT.active_group_id = 3; WEBCLIENT.USER = {'id': 52, 'fullName': "Public User"}; WEBCLIENT.active_user = {'id': 2, 'fullName': "Public data"}; WEBCLIENT.member_of_groups = [3, 1]; WEBCLIENT.isAdmin = false; WEBCLIENT.CAN_CREATE = false; WEBCLIENT.current_admin_privileges = []; WEBCLIENT.leader_of_groups = []; WEBCLIENT.URLS = {}; WEBCLIENT.URLS.webindex = "/webclient/"; WEBCLIENT.URLS.api_paths_to_object = "/webclient/api/paths_to_object/"; WEBCLIENT.URLS.api_containers = "/webclient/api/containers/"; WEBCLIENT.URLS.api_datasets = "/webclient/api/datasets/"; WEBCLIENT.URLS.api_images = "/webclient/api/images/"; WEBCLIENT.URLS.api_plates = "/webclient/api/plates/"; WEBCLIENT.URLS.api_plate_acquisitions = "/webclient/api/plate_acquisitions/"; WEBCLIENT.URLS.api_base = "/api/v0/"; WEBCLIENT.URLS.static_webclient = "/static/webclient/"; WEBCLIENT.URLS.static_webgateway = "/static/webgateway/"; WEBCLIENT.URLS.api_tags_and_tagged = "/webclient/api/tags/"; WEBCLIENT.URLS.fileset_check = "/webclient/fileset_check/delete/"; WEBCLIENT.URLS.api_parent_links = "/webclient/api/parent_links/"; WEBCLIENT.URLS.deletemany = "/webclient/action/deletemany/"; WEBCLIENT.URLS.copy_image_rdef_json = "/webclient/copyImgRDef/"; WEBCLIENT.URLS.reset_owners_rdef_json = "/webgateway/applyOwnersRDef/"; WEBCLIENT.URLS.reset_rdef_json = "/webgateway/resetRDef/"; WEBCLIENT.URLS.script_upload = "/webclient/script_upload/"; // jsTree code in ome.tree.js and center panel code in center_plugin.thumbs.js.html uses initially_select // instead of browser URL since URL may be /webclient/?path=plate.name-barcode|well.name-A1 WEBCLIENT.initially_select = ["image-13383922"]; WEBCLIENT.initially_open = ["project-501", "dataset-13901", "image-13383922"]; WEBCLIENT.URLS.tree_top_level = WEBCLIENT.URLS.api_containers; WEBCLIENT.URLS.api_experimenter = "/webclient/api/experimenters/2/"; WEBCLIENT.UI = {}; WEBCLIENT.UI.TREE = {}; WEBCLIENT.UI.TREE.pagination_nodes = ["experimenter", "screen", "plate", "acquisition", "project", "tagset", "image"]; WEBCLIENT.UI.TREE.type_order = "False".toLowerCase().split(",").filter(function(e){return e}); var PAGE_SIZE = 500; WEBCLIENT.HAS_RDEF = false; $.getJSON("/webgateway/getImgRDef/", function(data){ WEBCLIENT.HAS_RDEF = !!(data && data.rdef); }); // ** "Open With" config used by E.g. ome.tree.js ** // Loaded scripts can call OME.setOpenWithEnabledHandler and/or // OME.setOpenWithActionHandler to override default behaviour WEBCLIENT.OPEN_WITH = []; $.getJSON("/webgateway/open_with/", function(data){ if (data && data.open_with_options) { WEBCLIENT.OPEN_WITH = data.open_with_options; // Try to load scripts if specified: WEBCLIENT.OPEN_WITH.forEach(function(ow){ if (ow.script_url) { $.getScript(ow.script_url); } }) } }); $(document).ready(function(){ // initially hidden $("#user_dropdown ul").css('visibility', 'hidden'); // show on click $("#show_user_dropdown").on('click', function(e) { $("ul", $(this).parent()).css('visibility', 'visible'); e.preventDefault(); return false; }); // on hover-out, hide drop-down menus $("#user_dropdown ul").hover(function(){}, function(){ $(this).css('visibility', 'hidden'); }); $("#top_search_field label").inFieldLabels(); OME.setupAjaxError("/feedback/feedback/"); // AJAX 500 need feedback form url }); // The shown user's ID. This is located here because static javascript files // are not preprocessed by django function activeUserId() { return 2; }; // The currently logged in user ID function currentUserId() { return 52 }; </script> <!-- keep-alive ping so that OMERO session doesn't die --> <script> $(document).ready(function() { var ping_interval = 60000; var i = setInterval(function (){ $.get("/webclient/keepalive_ping/"); }, ping_interval); }); </script> <!-- toolbar helpers --> <script type="text/javascript"> if (typeof OME === "undefined") { OME={}; } OME.createShare = function() { $("#create_share_form").dialog("open"); return false; } $(document).ready(function(){ $("#create_share_form").dialog({ title: "Shares not supported", autoOpen: false, resizable: true, height: 250, width:450, modal: true, buttons: { "OK": function() { $( this ).dialog( "close" ); } } }); }); </script> <!-- hidden dialog --> <div id="create_share_form" style="display:none"> <p>Share functionality is no longer supported.</p> <p>Please see <a target="_blank" href="https://www.openmicroscopy.org/omero/features/share/">Sharing your data in OMERO</a> for alternative workflows. </p> </div> <!-- set-up right panel tabs --> <style type="text/css"> .keyValueTable tbody tr td span.favicon img { border: 1px solid hsl(210, 8%, 65%); box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); vertical-align: middle; width: 15px; height: 15px; } .keyValueTable tbody tr td span.blank img { width: 17px; height: 15px; } .keyValueTable thead tr h1 { padding: 2px 0px; } </style> <script type="text/javascript"> /** * This script is included in the main containers.html page as well as the container_tags.html and public.html pages, * adding itself as a selection listener to the jsTree in each case. * It loads appropriate data into the right-hand 'general' tab on selection changes in the jsTree. **/ var urlRegex = new RegExp("(https?|ftp|file):\/\/[!-~]*", "igm"); var iconify = function(input, imgsrc) { if (typeof imgsrc === 'undefined') { // e.g. https://www.google.com/s2/favicons?domain=https://www.ensembl.org/index.html // mapr can store icon in redis and serve at // e.g https://idr.openmicroscopy.org/mapr/favicon/?u=https://www.ensembl.org/index.html imgsrc = "/mapr/favicon/?u="; } function replacer(match){ return ' <span class="favicon"><a href="' + match + '" target="_blank" ><img src="' + imgsrc + encodeURIComponent(decodeURIComponent(match)) + '" /></a></span>'; }; return input.replace(urlRegex, replacer); }; var isURL = function(input) { return urlRegex.test(input); }; var old_linkify_element = OME.linkify_element; OME.linkify_element = function(elements) { if ($("table.keyValueTable").is(elements) ) { var mapr_menu = {"gene": {"default": ["Gene Symbol"], "all": ["Gene Symbol", "Gene Identifier"], "ns": ["openmicroscopy.org/mapr/gene"], "label": "Gene", "case_sensitive": true}, "genesupplementary": {"default": [], "all": [], "ns": ["openmicroscopy.org/mapr/gene/supplementary"], "label": "Gene supplementary"}, "sirna": {"default": ["siRNA Identifier"], "all": ["siRNA Identifier", "siRNA Pool Identifier"], "ns": ["openmicroscopy.org/mapr/sirna"], "label": "siRNA"}, "omap": {"default": ["OMAP Number"], "all": ["OMAP Number"], "ns": ["openmicroscopy.org/mapr/OMAP"], "label": "OMAP"}, "sirnasupplementary": {"label": "siRNA supplementary", "default": [], "all": [], "ns": ["openmicroscopy.org/mapr/sirna/supplementary"]}, "phenotype": {"default": ["Phenotype"], "all": ["Phenotype", "Phenotype Term Accession"], "ns": ["openmicroscopy.org/mapr/phenotype"], "label": "Phenotype", "case_sensitive": true, "wildcard": {"enabled": true}}, "compound": {"default": ["Compound Name"], "all": ["Compound Name"], "ns": ["openmicroscopy.org/mapr/compound"], "label": "Compound", "case_sensitive": true}, "compoundsupplementary": {"default": [], "all": [], "ns": ["openmicroscopy.org/mapr/compound/supplementary"], "label": "Compound supplementary"}, "organism": {"default": ["Organism"], "all": ["Organism"], "ns": ["openmicroscopy.org/mapr/organism"], "label": "Organism", "wildcard": {"enabled": true}}, "antibody": {"default": ["Antibody Identifier"], "all": ["Antibody Name", "Antibody Identifier"], "ns": ["openmicroscopy.org/mapr/antibody"], "label": "Antibody", "case_sensitive": true}, "antibodysupplementary": {"default": [], "all": [], "ns": ["openmicroscopy.org/mapr/antibody/supplementary"], "label": "Antibody supplementary"}, "orf": {"default": ["ORF Identifier"], "all": ["ORF Identifier"], "ns": ["openmicroscopy.org/mapr/orf"], "label": "ORF"}, "orfsupplementary": {"default": [], "all": [], "ns": ["openmicroscopy.org/mapr/orf/supplementary"], "label": "ORF supplementary"}, "cellline": {"default": ["Cell Line"], "all": ["Cell Line"], "ns": ["openmicroscopy.org/mapr/cell_line"], "label": "Cell Lines", "wildcard": {"enabled": true}}, "celllinesupplementary": {"default": [], "all": [], "ns": ["openmicroscopy.org/mapr/cell_line/supplementary"], "label": "Cell Lines supplementary"}, "protein": {"default": ["Protein"], "all": ["Protein"], "ns": ["openmicroscopy.org/mapr/protein"], "label": "Protein"}, "proteinsupplementary": {"default": [], "all": [], "ns": ["openmicroscopy.org/mapr/protein/supplementary"], "label": "Protein supplementary"}, "others": {"default": ["Others"], "all": ["Others"], "ns": ["openmicroscopy.org/omero/bulk_annotations"], "label": "Others"}, "notebook": {"default": ["Study Notebook"], "all": ["Study Notebook"], "ns": ["openmicroscopy.org/idr/analysis/notebook"], "label": "Analysis Notebook"}}; // Replace namespace with label var ns2menu = {}; $.each( mapr_menu, function( i, obj ) { $.each( obj['ns'], function( j, ns ) { ns2menu[ns] = obj['label']; }); }); var t = elements.find("thead").find("tr"); var $targets = t.filter(function() { if ($(this).attr('title') in ns2menu) { var old = $(this).children("th").text(); $(this).children("th").html("<h1>" + ns2menu[$(this).attr('title')] + "</h1>"); $(this).closest('tr').nextAll("tr.tooltip").children('th').children('span') .append('<br/><b>NS:</b> ' + old + ''); } }); // linkify also mapr keys and convert URL to icons // Dict of {'Key Name': 'maprConfigId'} var keys2menu = {}; $.each( mapr_menu, function( configId, config ) { $.each( config['all'], function( j, maprKey ) { // some configs support multiple Keys in 'all' keys2menu[maprKey] = configId }); }); let menu2keys = Object.keys(keys2menu); // Look for rows that... elements.find("tbody").find("tr").each(function() { var $row = $(this); if (!$row.parent().parent().hasClass("editableKeyValueTable")) { var $chlabel = $row.children("td:nth-child(1)"); var $chvalue = $row.children("td:nth-child(2)"); var _key = $chlabel.text().trim(); var _val = $chvalue.text().trim(); var _tkey = _key.replace(/url/i, "").trim(); // Look for K-V pairs where the label is // '<mapr-PK>' or '<mapr-PK> URL' // Will be true for 'Gene Identifier' and 'Gene Identifier URL' if ( $.inArray(_tkey, menu2keys) > -1 ) { // IF the value is a URL, create a favicon $chvalue.html(iconify($chvalue.text())); // If the key is named in any mapr config, // E.g. _key is "Gene Symbol" or "Gene Identifier" for "gene" mapr config if ( $.inArray(_key, menu2keys) > -1 ) { if ( _key in keys2menu && keys2menu[_key] !== undefined ) { // Make Value into link to Internal mapr URL for the value, e.g. .../mapr/gene/?value=CDK5RAP2 var _url = location.protocol + "//" + location.host + "/mapr/" + keys2menu[_key] +"/?value=" + encodeURIComponent(_val); $chvalue.html($chvalue.html().replace(_val, '<a href="' + _url + '">' + _val + '</a>')); } } // If we're on e.g. "Gene Identifier URL" row... if ( _key.match(new RegExp("url", "i")) ) { // Find the next previous row where // row[N-1].key == row[N].key + ' URL' var $target = $row.prev('tr'); while ($target.length > 0 && $target.children("td:nth-child(1)").text() !== _tkey) { $target = $target.prev('tr'); } if ($target.children("td:nth-child(1)").text() === _tkey) { var $targetchvalue = $target.children("td:nth-child(2)"); // Take the Favicon html we created with iconify() above // and add it to the previous Value. Then hide this row. $targetchvalue.append($chvalue.html()); $row.hide(); } } } else { old_linkify_element($row); } } }); } else { old_linkify_element(elements); } }; $(function () { $("#annotation_tabs").tabs({cache: true, disabled: true}); var $metadata_general = $("#metadata_general"); var general_tab_index = 0; // make sure details are ALWAYS opened OME.setPaneExpanded('details', true); // make sure maps are ALWAYS opened OME.setPaneExpanded('maps', true); // this is called on change in jstree selection, or switching tabs var update_metadata_general_tab = function() { var selected = $("body").data("selected_objects.ome"); var prefix = '/webclient/'; if(selected.length == 0) { return; } if (selected.length > 1) { // handle batch annotation... var productListQuery = new Array(); var well_index; for (var i=0; i<selected.length; i++) { productListQuery[i] = selected[i]["id"].replace("-","="); well_index = well_index || selected[i]["index"]; } var query = '/webclient/batch_annotate/'+"?"+productListQuery.join("&"); if (well_index) { query += "&index=" + well_index; } // Load right hand panel... $.ajax({ url: query, dataType: "html", // Need to check that the selected objects haven't changed during AJAX call... success: function(data) { var selected_ids = [], i, selected_data = $("body").data("selected_objects.ome"); for(i=0; i<selected_data.length; i++) { selected_ids.push(selected_data[i].id); } var oids = $(data).filter("#object-ids").text().split("|"); // need to compare two arrays of strings... selected_ids.sort(); oids.sort(); for(i=0;i<oids.length; i++) { if (oids[i] !== selected_ids[i]) { return; // any differences, don't load panel. } } $metadata_general.html(data); } }); } else { $("#annotation_tabs").tabs("enable", general_tab_index); // always want metadata_general enabled var url = null; var data = {}; //var oid = selected.attr('id'); //var orel = selected[0].attr('rel'); var oid = selected[0]["id"]; var orel = oid.split("-")[0]; if (typeof oid =="undefined" || oid==false) return; // handle loading of GENERAL tab if ($metadata_general.is(":visible") && $metadata_general.is(":empty")) { // orphaned if (oid.indexOf("orphaned")>=0) { $metadata_general.html('<div class="right_tab_inner"><p class="description">This is a virtual container with orphaned images. These images are not linked anywhere. Just drag them to the selected container.</p></div>'); //return; // experimenter } else if (oid.indexOf("experimenter")>=0) { //$metadata_general.html('<p>'+selected.children().eq(1).text()+'</p>'); } else if (orel.indexOf("map")>=0) { var map_prefix = '/mapr/'; url = map_prefix+'metadata_details/userdata/'; data = {'value': oid.split(/-(.+)/)[1]}; // everything else } else { if(orel=="image") { if (selected[0]["shareId"]) { url = prefix+'metadata_details/'+orel+'/'+oid.split("-")[1]+'/'+selected[0]["shareId"]+'/'; } else { url = prefix+'metadata_details/'+orel+'/'+oid.split("-")[1]+'/'; } } else if(orel=="well"){ var well_index = selected[0]["index"] || 0; url = '/webclient/metadata_details/well/'+oid.split('-')[1]+'/'; data = {'index': well_index}; } else { url = prefix+'metadata_details/'+orel+'/'+oid.split("-")[1]+'/'; } } if (url !== null) { // We are effectively doing $metadata_general.load(url) BUT we need to check that selection // is still correct (hasn't changed during the AJAX call); $.ajax({ url: url, data: $.param(data), dataType: "html", success: function(data) { var selected_id = $("body").data("selected_objects.ome")[0].id; var oid = $(data).filter("#object-id").text(); if (oid === selected_id) { $metadata_general.html(data); } } }); } } } } // update tabs when tabs switch $("#annotation_tabs").on( "tabsactivate", function(event, ui){ // we get a "tabsactivate" event, but need to check if 'this' tab was chosen... if (ui.newTab.index() == general_tab_index) { // sometimes this doesn't get shown until too late, so the :visible check above failed $metadata_general.show(); update_metadata_general_tab(); } }); // on selection change, clear tab contents then update $("body").on("selection_change.ome", function(event) { // clear contents of panel $("#metadata_general").empty(); update_metadata_general_tab(); // update }); }); </script> <!-- include scripts for loading data into right-hand tabs --> <!-- these are configured in settings.py under "omero.web.ui.right_tabs" --> <script> $(document).ready(function() { $("#metadata_tab").omeroweb_right_plugin({ plugin_index:1, load_plugin_content: function(selected, obj_dtype, obj_id) { // Url based on selected object(s) var well_index = selected[0]["index"] || 0; var url; if (selected[0]["shareId"]) { url = '/webclient/metadata_acquisition/'+obj_dtype+'/'+obj_id+'/'+selected[0]["shareId"]+'/'; } else { url = '/webclient/metadata_acquisition/'+obj_dtype+'/'+obj_id+'/'; } if (well_index > 0) { url = url + '?index='+well_index; } OME.fadeInSpinner($(this)); $(this).load(url); }, supported_obj_types: ['image'] }); }); </script> <script> $(document).ready(function() { $("#right_panel").on('resize', function(event) { // This behaviour is also in the metatdata_preview.html itself var vpWidth = $(this).width() - 50; // Use a 1:1 aspect ratio with the width. Keep the viewport from // taking up more than 75% of the available height. var vpHeight = Math.max( 300, Math.min(vpWidth, ($("#preview_tab").height() - 100) * 0.75) ); if (OME.preview_viewport) { // Need to set a few sizes, then call viewport.refresh() $("#viewport").css({'width': vpWidth + 'px', 'height': vpHeight + 'px'}); $("#viewport-vp").css({'width': vpWidth + 'px'}); $("#viewport-top").css({'height': vpHeight + 'px'}); OME.preview_viewport.refresh(true); } }); $("#preview_tab").omeroweb_right_plugin({ plugin_index:2, load_plugin_content: function(selected, obj_dtype, obj_id) { // Url based on selected object(s) var url; var well_index = selected[0]["index"] || 0; if (selected[0]["shareId"]) { url = '/webclient/metadata_preview/'+obj_dtype+'/'+obj_id+'/'+selected[0]["shareId"] + '/'; } else { url = '/webclient/metadata_preview/'+obj_dtype+'/'+obj_id + '/'; } if (well_index > 0) { url = url + '?index='+well_index; } OME.fadeInSpinner($(this)); $(this).load(url); }, supported_obj_types: ['image', 'well'] }); }); </script> <script type="text/javascript" src="/static/3rdparty/jquery.jstree-3.3.12/jstree.js"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.jstree.locate_plugin.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.jstree.conditionalselect_plugin.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.jstree.pagination_plugin.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.jstree.fields_plugin.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.jstree.omecut_plugin.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.jstree.ometools_plugin.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.jstree.childcount_plugin.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/jquery.jstree.truncatetext_plugin.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.chgrp.js?_5.26.0"></script> <script type="text/javascript" src="/static/webclient/javascript/ome.chown.js?_5.26.0"></script> <!-- Main jsTree code is here --> <script src="/static/webclient/javascript/ome.tree.js?_5.26.0"></script> <script type="text/javascript"> // Variable to store selection data when using jstree refresh var refreshPathsReverse = []; // Called from ome.tree.js var updateParentRemoveNode = function(inst, node, parent) { /* Update any other instances of the parent of this node to remove it * Also Based on if the parent of this node has any children * remaining, update the parental status of matching parents */ // Get any other instances of the parent var parentKey = inst.locate_key(parent); var parentNodes = inst.locate_node(parentKey); // Get the parentNodeIds to easily check set membership var parentNodeIds = []; $.each(parentNodes, function(index, parentNode) { parentNodeIds.push(parentNode.id); }); // Determine if this parent now has no children var parentChildless = !inst.is_parent(parent); // For performance reasons it is quicker to look for the nodes and check // their parents than to look for the parents and scan through all // their children var nodeKey = inst.locate_key(node); var nodeNodes = inst.locate_node(nodeKey); var updateParentRemoveNodes = []; $.each(nodeNodes, function(index, nodeNode) { // Discount the original node as jstree is removing that if (nodeNode.id === node.id) { return true; } // Discount any nodes that do not have one of the predetermined parents if ($.inArray(inst.get_parent(nodeNode), parentNodeIds) === -1) { return true; } // The remaining are children of identical parents and should // be removed updateParentRemoveNodes.push(nodeNode); }); // Actually do the remove inst.delete_node(updateParentRemoveNodes); // If the parent is now childless it is also necessary to check // for any identical parent containers that are marked as expandable // which has ceased to be the case $.each(parentNodes, function(index, parentNode) { // Discount the original parent as jstree is handling that if (parentNode.id === parent.id) { return true; } if (parentChildless) { if (!inst.is_loaded(parentNode)) { // Remove the offer of expansion, this is denoted by a node which is // loaded, but has zero children parentNode.state.loaded = true; inst.redraw_node(parentNode); } } // Update the child count (override the childcount because it may not be // loaded and will thus always have no count) OME.updateNodeChildCount(inst, parentNode, parent.children.length); }); }; var updateParentInsertNode = function(inst, node, parent, position) { /* Update any other instances of the parent with the new node * */ // Get any other instances of the parent var parentKey = inst.locate_key(parent); var parentNodes = inst.locate_node(parentKey); var oldParent = inst.get_node(inst.get_parent(node)); $.each(parentNodes, function(index, parentNode) { // Discount the original parent as jstree is inserting that if (parentNode.id === parent.id) { // Continue return true; } if ((inst.is_loaded(parentNode) && inst.is_parent(parentNode)) || (inst.is_loaded(parentNode) && inst.is_open(parentNode))) { // Create a new node to match the other examples with // data from the old var new_node_data = { 'data': {'id': node.data.obj.id, 'obj': node.data.obj}, 'text': node.text, 'type': node.type, // If it has children, we wish it to be loadable, but not loaded // so just give it boolean instead of actual nodes 'children': inst.is_parent(node), 'li_attr': { 'class': node.type, 'data-id': node.data.obj.id } }; inst.create_node(parentNode, new_node_data, position); // An empty container } else if (!inst.is_parent(parentNode) && inst.is_loaded(parentNode)) { // Change the node to offer expansion parentNode.state.loaded = false; // Close the node inst.redraw_node(parentNode); } // Update the child count (override the childcount because it may not be // loaded and will thus always have no count) OME.updateNodeChildCount(inst, parentNode, oldParent.children.length); }); }; var removeDuplicateNodes = function(inst, node) { /** * Removes all duplicates of a node throughout the tree. * For use mainly when something is truly deleted as opposed to * unlinked */ var nodeKey = inst.locate_key(node); var nodeNodes = inst.locate_node(nodeKey); $.each(nodeNodes, function(index, nodeNode) { // Update the parent count var parent = inst.get_node(inst.get_parent(nodeNode)); var count = 0; if (parent.children) { count = parent.children.length; } OME.updateNodeChildCount(inst, parent, count); // Actually delete the node inst.delete_node(nodeNode); }); }; if (typeof OME === "undefined") { OME={}; } OME.handleNewContainer = function(container_type) { // If we are filtering to show another user's data, // we 'should' have writeOwned privilege var writeOwned = WEBCLIENT.current_admin_privileges.indexOf("WriteOwned") > -1; var $f = $("#new-container-form"); var memberOfGroup = WEBCLIENT.member_of_groups.indexOf(WEBCLIENT.active_group_id) > -1; // clear fields $("input[name='owner']", $f).val(""); var new_container_name = $("input[name='name']", $f).val(""); var new_container_desc = $("textarea[name='description']", $f).val(""); $("#new_pds_owner_controls").hide(); // If we're not filtering by MY data, could be 'All Members' or another User... // Data will be and we need to show who owner will be if (WEBCLIENT.active_user.id !== WEBCLIENT.USER.id) { // We 'should' have right permissions but just in case... if (!memberOfGroup && !writeOwned) { alert("You don't have permission to create data for other users"); return; } // If filtering by User, data will belong to them if (WEBCLIENT.active_user.id !== -1) { $("#new_pds_owner").text(WEBCLIENT.active_user.fullName); $("input[name='owner']", $f).val(WEBCLIENT.active_user.id); $("#new_pds_owner_controls").show(); } // Otherwise, if we can writeOwned, pick Owner from members of this group else if (writeOwned) { // Not yet supported (Create options should be disabled if All Members) // To support this, will need to show <select> to choose owner. } } $("#new_container_type").text(container_type.capitalize()); $("#new-container-form").dialog('open'); } // Call this when a parent child count may have changed OME.updateNodeChildCount = function(tree, parent, override_count) { var node = tree.get_node(parent); var count; if (override_count) { count = override_count; } else { count = node.children.length; } // Restrict this to projects, datasets, screens and plates if (node.type === 'project' || node.type === 'dataset' || node.type === 'screen' || node.type === 'plate' || node.type === 'orphaned') { node.data.obj.childCount = count; tree.redraw_node(node, false, false); } }; function enableToolbarButton(name, enabled) { if (enabled) { $('input#'+name+'Button').removeClass('button-disabled').prop('disabled', false); } else { $('input#'+name+'Button').addClass('button-disabled').prop('disabled', true); } }; function buttonsShowHide(selected, inst) { // Disable all unless explicitly enabled below var toolbar_config = { "addproject":false, 'adddataset':false, 'addscreen':false, 'addtag': false, 'addtagset': false, 'copy':false, 'cut':false, 'paste': false, 'delete':false, 'createshare':false }; // We 'canCreate' top level items, E.g. Project, Dataset, Screen, if the current userId is self or 'All Members' var userId = 2, memberOfGroup = WEBCLIENT.member_of_groups.indexOf(WEBCLIENT.active_group_id) > -1, writeOwned = WEBCLIENT.current_admin_privileges.indexOf("WriteOwned") > -1, allMembers = userId === -1, // canCreate if looking at your own data or 'All Members' OR User's data with writeOwned canCreate = (userId === WEBCLIENT.USER.id || (allMembers && memberOfGroup) || (!allMembers && writeOwned)); canCreate = canCreate && WEBCLIENT.CAN_CREATE; // global state for read-only server // These nodes can be Orphans, so creation is not selection-specific if (canCreate) { toolbar_config["addproject"] = true; toolbar_config["adddataset"] = true; toolbar_config['addscreen'] = true; toolbar_config["addtag"] = true; toolbar_config["addtagset"] = true; } if(selected.length > 0) { // If the current selection(s) can be deleted // TODO Admin will have delete permissions on user // probably they should not be able to delete the // user from there if(OME.nodeHasPermission(selected, 'canDelete')) { toolbar_config['delete'] = true; } // Only allow paste if there is a single selection and there is // something to paste if(selected.length == 1 && inst.can_paste()) { // Check if the target is a suitable container for pasteing it var buffer = inst.get_buffer(); $.each(buffer.node, function(index, node) { // Never allow pasteing into orphaned or experimenter // TODO What about pasteing a project from one experimenter to another? // Run the standard jstree check to determine if paste is allowed as if using // drag'n'drop. if (inst.get_node(selected[0]).type !== 'orphaned' && inst.get_node(selected[0]).type !== 'experimenter' && inst.check(buffer.mode, node, selected[0], 0)) { toolbar_config['paste'] = true; } else { // Break out of $.each as if one item can't paste, we can't paste return false; } }); } // Only allow cut if the selected item(s) are elligible. This uses the slightly // confusingly named 'is_draggable' which is part of the drag'n'drop plugin // which in turn uses a jstree node type property 'draggable' // It also checks it the selected nodes can be linked if(inst.settings.dnd.is_draggable(selected)) { toolbar_config['cut'] = true; } // Allow Copy of Dataset/Image/Plate if you 'canLink' all selected nodes var canCopy = selected.reduce(function(prev, n){ var node_type = n.type, parent_type = inst.get_node(n.parent).type; // In tag tree, can't copy anything except a tag var invalidType = (WEBCLIENT.TAG_TREE && node_type !== "tag"); // Can Copy objects under their true parent types (NOT orphaned tag, dataset etc) var plink = ((node_type === "dataset" && parent_type === "project") || (node_type === "image" && parent_type === "dataset") || (node_type === "plate" && parent_type === "screen") || (node_type === "tag" && parent_type === "tagset")); return (!invalidType) && plink && OME.nodeHasPermission(n, 'canLink') && prev; }, true); toolbar_config['copy'] = canCopy; // Only images can be added to a basket and only if they all are toolbar_config['createshare'] = true; $.each(selected, function(index, node) { if (node.type != 'image' || !OME.nodeHasPermission(node, 'canLink')) { toolbar_config['createshare'] = false; // Break out of $.each return false; } }); } for (var btnName in toolbar_config) { enableToolbarButton(btnName, toolbar_config[btnName]); } }; // Helper method used by linkNode and unlinkNode below. // Simply adds parent_type, parent_id, child_type & child_id to payload object // e.g. {"dataset":{"10":{"image":[1,2,3]}}}: function addDataToPayload(payload, node, parent) { var parent_id = parent.data.obj.id, parent_type = parent.type, child_id = node.data.obj.id, child_type = node.type; // payload is payload.parent_type.parent_id.child_type: [child_ids] if (!(parent_type in payload)) payload[parent_type] = {}; if (!(parent_id in payload[parent_type])) { payload[parent_type][parent_id] = {}; } if (!(child_type in payload[parent_type][parent_id])) { payload[parent_type][parent_id][child_type] = []; } payload[parent_type][parent_id][child_type].push(child_id); } // linkNode and unlinkNode (below) use a 'debounce' timeout to collect // many link or unlink calls into a single AJAX call. // On each call to linkNode or unlinkNode, we add the data from node & parent // to the payload that we submit. This is sent once the timeout expires. // linkNode and unlinkNode both return a deferred promise that will be // resolved when the AJAX call returns. var linkNodeTimeout, linkPayload = {}, deferredLink = jQuery.Deferred(); function linkNode(inst, node, parent) { // doLink is called on timeout to submit AJAX call var doLink = function() { // we send a reference to the deferred... var dd = deferredLink; // ...and create a new deferred to handle subsequent calls to linkNode deferredLink = jQuery.Deferred(); // Do the call, and resolve the deferred when done $.ajax({ url: "/webclient/api/links/", type: "POST", data: JSON.stringify(linkPayload), dataType: 'json' }) .done(function(data){ dd.resolve(data); }); // empty the payload, ready for sebsequent calls linkPayload = {}; }; // build up an object with all the links we want to create addDataToPayload(linkPayload, node, parent) // if we're waiting on timeout, clear this... if (linkNodeTimeout) { clearTimeout(linkNodeTimeout); } // ...start new timeout linkNodeTimeout = setTimeout(doLink, 10); // return a promise (cannot call resolve() on it elsewhere) return deferredLink.promise(); } // See docs above for linkNode (works the same as unlinkNode) var unlinkNodeTimeout, unlinkPayload = {}, deferredUnlink = jQuery.Deferred(); function unlinkNode(inst, node, parent) { var doUnlink = function() { var dd = deferredUnlink; deferredUnlink = jQuery.Deferred(); $.ajax({ url: "/webclient/api/links/", type: "DELETE", data: JSON.stringify(unlinkPayload), dataType: 'json' }) .done(function(data){ dd.resolve(data); }); unlinkPayload = {}; }; addDataToPayload(unlinkPayload, node, parent) if (unlinkNodeTimeout) { clearTimeout(unlinkNodeTimeout); } unlinkNodeTimeout = setTimeout(doUnlink, 10); return deferredUnlink.promise(); }; // Remove duplicate nodes, normally as a result of copy_node // or move_node // Global function, called from omecut_plugin function removeDuplicate(inst, node, parentId) { var parent = inst.get_node(parentId), found = false; $.each(parent.children, function(index, childId) { var child = inst.get_node(childId); if (child.type === node.type && child.data.obj.id === node.data.obj.id && child.id != node.id) { inst.delete_node(child); found = true; // Break out of $.each return false; } }); return found; }; // Stuff to do on load... $(function() { // We (un)truncate images when the left panel resizes... $("#left_panel").on('resize', function(event) { var inst = $.jstree.reference('#dataTree'); inst.redraw(true); }); // Handle creation of new Project, Dataset or Screen... $("#new-container-form").dialog({ autoOpen: false, resizable: true, height: 280, width:420, modal: true, buttons: { "OK": function() { createNewContainer(); $( this ).dialog( "close" ); }, "Cancel": function() { $( this ).dialog( "close" ); } } }); // same code is called from closing dialog or 'submit' of form $("#new-container-form").on('submit', function() { $("#new-container-form").dialog( "close" ); createNewContainer(); return false; }); var createNewContainer = function() { var cont_type = $("#new_container_type").text().toLowerCase(); // E.g. 'project' var $f = $("#new-container-form"); var new_container_name = $("input[name='name']", $f).val(); var new_container_desc = $("textarea[name='description']", $f).val(); var new_container_owner = $("input[name='owner']", $f).val(); if (new_container_name.trim().length == 0) { alert("Please enter a Name"); return; } // If images under orphaned are selected, note IDs (for adding to new dataset) var inst = $.jstree.reference('#dataTree'); var selected = inst.get_selected(true); // TODO Only keeping img_ids because it is simpler to POST the data using that // Can be removed when updating the ajax call var img_ids = []; var orphaned_image_nodes = []; $.each(selected, function(index, node) { if (node.type === 'image' && inst.get_node(inst.get_parent(node)).type === 'orphaned' && OME.nodeHasPermission(node, 'canLink')) { img_ids.push(node.data.obj.id); orphaned_image_nodes.push(node); } }); // Default: Create an orphan of "folder_type" ('project', 'dataset', 'screen', 'tag', 'tagset' etc. ) url = '/webclient/action/addnewcontainer/'; // Find the 'experimenter' node as parent var root = inst.get_node('#'); $.each(root.children, function(index, id) { var node = inst.get_node(id); if (node.type === 'experimenter' && node.data.obj.id === 2) { parent = node; // Break out of each return false; } }); // If a project is selected (or selected is a child of project) create dataset under it var url, position = 0; var parent = false; if (selected.length > 0 && cont_type == 'dataset') { if (selected[0].type === 'project') { parent = selected[0]; } else if (inst.get_node(selected[0].parent).type === 'project') { parent = inst.get_node(selected[0].parent); } // If a tagset is selected (or selected is a child of tagset), create tag under it } else if(selected.length > 0 && cont_type == 'tag') { if (selected[0].type === 'tagset') { parent = selected[0]; } else if (inst.get_node(selected[0].parent).type === 'tagset') { parent = inst.get_node(selected[0].parent); } } if (parent) { url = '/webclient/action/addnewcontainer/' + parent.type + '/' + parent.data.obj.id + '/'; } else { // otherwise create an orphan of "folder_type" ('project', 'dataset', 'screen', 'tag', 'tagset' etc. ) url = '/webclient/action/addnewcontainer/'; // Find 'experimenter' to be parent var root = inst.get_node('#'); $.each(root.children, function(index, id) { var node = inst.get_node(id); if (node.type === 'experimenter' && node.data.obj.id === 2) { parent = node; // Break out of each return false; } }); } var ajax_data = { "name" : new_container_name, "folder_type" : cont_type, "description" : new_container_desc, "owner": new_container_owner } if (img_ids.length > 0){ ajax_data['image'] = img_ids; } $.ajax({ url: url, data: ajax_data, dataType: "json", type: "POST", traditional: true, success: function(r){ var data = { 'id': r['id'], 'isOwner': true, 'ownerId': 52, 'name': new_container_name, 'permsCss': 'canEdit canAnnotate canLink canDelete canChgrp' }; var node = { 'data': {'id': r['id'], 'obj': data}, 'text': new_container_name, 'children': false, 'type': cont_type, 'li_attr': { 'class': cont_type, 'data-id': r['id'] } }; // Create the node, move any orphans into it and select only it node = JSON.parse(JSON.stringify(node)); inst.create_node(parent, node, 'last', function(node) { if (orphaned_image_nodes.length > 0) { inst.move_node(orphaned_image_nodes, node); } // There is no need to update duplicates at the moment as nothing that // can be created could have a duplicate to need updating inst.deselect_all(); inst.select_node(node); //TODO Scroll to new if off screen? https://github.com/vakata/jstree/issues/519 }); } }); }; $("#delete-dialog-form").dialog({ dialogClass: 'delete_confirm_dialog', autoOpen: false, resizable: true, height: 210, width:420, modal: true, buttons: { "Yes": function() { $("#delete-dialog-form").data("clicked_button", "Yes"); $( this ).dialog( "close" ); }, "No": function() { $("#delete-dialog-form").data("clicked_button", "No"); $( this ).dialog( "close" ); } } }); }); </script> <!-- configure toolbar buttons --> <script type="text/javascript"> $(function () { var inst = $.jstree.reference('#dataTree'); // Attach click handlers to the individual buttons $('#addprojectButton').on('click', function() { OME.handleNewContainer("project"); }); $('#adddatasetButton').on('click', function() { OME.handleNewContainer("dataset"); }); $('#addscreenButton').on('click', function() { OME.handleNewContainer("screen"); }); $('#addtagButton').on('click', function() { OME.handleNewContainer("tag"); }); $('#addtagsetButton').on('click', function() { OME.handleNewContainer("tagset"); }); $('#copyButton').on('click', function() { var objs = inst.get_selected(true) inst.copy(objs); }); $('#cutButton').on('click', function() { var objs = inst.get_selected(true) inst.cut(objs); }); $('#createshareButton').on('click', function() { OME.createShare(inst.get_selected()); }); $('#pasteButton').on('click', function() { var objs = inst.get_selected(true); if (objs.length == 1) { inst.paste(objs[0]); // Always disable paste button immediatly after using it enableToolbarButton('paste', false); } }); $('#deleteButton').on('click', function() { var deleteUrl = "/webclient/action/deletemany/", filesetCheckUrl = "/webclient/fileset_check/delete/"; OME.handleDelete(deleteUrl, filesetCheckUrl, 52); }); $('#refreshButton').on('click', function() { // Ensure the button cannot be clicked again while we are // performing a refresh. The "refresh.jstree" event handler // is in ome.tree.js and will be responsible for enabling the // button again. event.target.disabled = true; // Grab the paths to the items that are currently selected, for restoration later var selections = inst.get_selected(); $.each(selections, function(index, selection) { var path = inst.get_path(selection, false, true).reverse(); var refreshPathReverse = []; $.each(path, function(index, pathComponent) { var node = inst.get_node(pathComponent); var tuple = [node.type, node.data.obj.id]; refreshPathReverse.push(tuple); }); refreshPathsReverse.push(refreshPathReverse); }); inst.deselect_all(); // NB: the global variable refreshPathsReverse is used in ome.tree.js // after refresh, then set to empty list. inst.refresh(); }); }); </script> <!-- set up the middle panel to only show the div chosen by <select> --> <script type="text/javascript"> if (typeof OME === "undefined") {OME = {}} // set the enabled status of the option to select a plugin. Set by index (0-based) OME.set_center_plugin_enabled = function (index, enabled) { // the first 'plugin' is actually the second option (first is thumbnails) var $plugin_option = $('#center_panel_chooser select option:nth-child('+ (index+1) +')'); if (enabled) { $plugin_option.prop('disabled', false); } else { $plugin_option.prop('disabled', true); } } $(document).ready(function() { $(".center_panel_content").hide(); $("#content_details").show(); $('#center_panel_chooser select').on('change', function() { var panel_id = this.options[this.selectedIndex].value; $(".center_panel_content").hide(); $(panel_id).show(); // other listeners are bound to the parent element (independent of chooser being <select>) $('#center_panel_chooser').trigger("center_plugin_changed.ome"); }); }); </script> <!-- include code to handle primary 'thumbs' middle plugin --> <!-- Include script for filtering center panel --> <script src="/static/webclient/javascript/ome.center_plugin_filter.js?_5.26.0" type="text/javascript"></script> <script> /** * This script is included in the main containers.html page as well as public.html, * adding itself as a selection listener to the jsTree in each case. * It loads appropriate data into the middle panel on selection changes in the jsTree. * For the main containers.html page, it also responds to switching between 'plugins' **/ $(document).ready(function() { // Revamp of thumbnail update /* When should the panel update 1) The image selection has changed (update) 2) The container selection has changed (refresh) 3) Something has been deleted or moved out (remove) 4) Something has been added (refresh) Problems An image is moved to another dataset while selected. This causes the view to shift to the new view There will also be a remove attempt (this must do nothing) */ var inst = $.jstree.reference('#dataTree'); var parentNode; // the currently selected node // We compile the underscore templates, ready for rendering var tmplText = $('#icon_thumbnails_template').html(); var iconTmpl = _.template(tmplText); var headerText = $('#icon_header_template').html(); var headerTmpl = _.template(headerText); // Variables for layout and thumbnail zooming var layout = "icon"; var icon_styles = []; var li_styles = []; var aspect_ratios = []; var iconSize = 65; var parentId; // E.g. dataset-1 var dateFormatOptions = { weekday: "short", year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" }; var setIconSize = function() { $("#dataIcons").get(0).style.setProperty("--iconSize", iconSize + "px"); } // Start listening for Node Loading events on the tree... // If a node loads and it's selected, update_thumbs... $("#dataTree").on('load_node.jstree', function(event, data){ if (data.node.state.selected) { update_thumbnails_panel(event, data); } }); $("#content_details").on("click", "input.button_pagination", function(){ var page = $(this).attr('data-page'); page = parseInt(page, 10); inst.deselect_all(true); inst.change_page(parentNode, page); // and then reselect the same node again to trigger update inst.select_node(parentNode); }); // double-click handler on image - launches image viewer $("#content_details").on("dblclick", "li.row", function(event) { var $this = $(this), iid = $this.attr('id').split("-")[1], url; // Image only has share ID if user doesn't own the share. if ($this.attr('data-share')) { url = "/webclient/99/img_detail/0/".replace('/99/', "/" + $this.attr('data-share') + "/" ); } else { url = "/webclient/img_detail/0/"; } url = url.replace('/0/', "/" + iid + "/" ); // We try to traverse the jstree, to find parent of selected image var parent = OME.getTreeImageContainerBestGuess(iid); if (parent && parent.data) { if (parent.type === 'dataset') { url += '?' + parent.type + '=' + parent.data.id; } } window.open(url, '_blank'); }); // Set up the centre panel header. var setupDatasetHeader = function() { var html = headerTmpl({'layout': layout, 'staticUrl': '/static/webclient/'}); $("#content_details").html(html); // single click handler on image (container). Selection then update toolbar & metadata pane $( "#icon_table" ).on( "click", "li.row", function(event) { event.preventDefault(); handleClickSelection(event); }); $("#filtersearch label").inFieldLabels(); $("#thumb_size_slider").slider({ max: 200, min: 30, value: iconSize, slide: function(event, ui) { iconSize = ui.value; setIconSize(); } }); // handle Radio buttons $("#layout_chooser button").on('click', function(event) { if ($(event.target).attr('id') == 'table_layout') { layout = 'table'; } else { layout = 'icon'; } setLayout(); $("#layout_chooser button").removeClass("checked"); $(event.target).addClass("checked"); }); } // This is called directly by various jstree plugins // E.g. omecut_plugin.js as well as the jstree in containers.html window.update_thumbnails_panel = function(event, data) { // Get the current selection var selected = inst.get_selected(true); if (!$("#content_details").is(":visible") || selected.length === 0) { $("#content_details").html(""); parentId = undefined; clearThumbnailsPanel(); return Promise.resolve(); } var dtype = selected[0].type; if (selected.length > 1 && dtype !== "image") { $("#content_details").html(""); parentId = undefined; clearThumbnailsPanel(); return Promise.resolve(); } // parent node could be dataset, orphaned, share or tag // see WEBCLIENT.UI.TREE.pagination_nodes and ome.tree.js var parentTypes = WEBCLIENT.UI.TREE.pagination_nodes, imgNodes = []; if (parentTypes.indexOf(dtype) < 0) { parentNode = selected[0]; } else if (dtype === "image") { parentNode = inst.get_node(inst.get_parent(selected[0])); } else if (dtype === "plate" || dtype === "acquisition") { parentId = undefined; load_spw(event, data); return Promise.resolve(); // All other types have blank centre panel } else { parentId = undefined; clearThumbnailsPanel(); return Promise.resolve(); } if (!parentNode) { // need this for pagination etc parentNode = inst.get_node(data.node); } var newParentId = parentNode.type + "-" + parentNode.data.obj.id; if (parentId === newParentId && event.type !== "copy_node" && event.type !== "create_node" && event.type !== "load_node" && event.type !== "delete_node" && event.type !== "refreshThumb" && event.type !== "refreshThumbnails") { highlightSelectedThumbs(selected); return Promise.resolve(); } // update single thumbnail, see OME.refreshThumbnails if (event.type === "refreshThumb") { OME.load_thumbnail( data.imageId, "/webclient/get_thumbnail/"+data.imageId+"/", function(thumb) { $("li#image_icon-"+data.imageId+ " img").attr("src", thumb); } ); return Promise.resolve(); } parentId = newParentId; // clear html and stored data before adding back html clearThumbnailsPanel(); imgNodes = []; parentNode.children.forEach(function(ch){ var childNode = inst.get_node(ch); // Ignore non-images under tags or 'deleted' under shares if (childNode.type == "image") { imgNodes.push(childNode); } }); var imgJson = [], selFileSets = [], shareId = null; // Convert jsTree nodes into json for template imgNodes.forEach(function(node){ var d = node.data.obj.date || node.data.obj.acqDate; var date = new Date(d); date = date.toLocaleTimeString(undefined, dateFormatOptions); var iData = {'id': node.data.obj.id, 'name': node.text, 'data': JSON.parse(JSON.stringify(node.data)), 'selected': node.state.selected, 'date': date, }; // Note fileset IDs for selected images if (iData.selected) { var fsId = node.data.obj.filesetId; if (fsId) { selFileSets.push(fsId); } } // If image is in share and share is not owned by user... if (node.data.obj.shareId && !parentNode.data.obj.isOwned) { // share ID will be needed to open image viewer iData.shareId = node.data.obj.shareId; shareId = node.data.obj.shareId; } imgJson.push(iData); }); // Now we know which filesets are selected, we can // go through all images, adding fs-selection flag if in if (selFileSets.length > 0) { imgJson.forEach(function(img){ if (selFileSets.indexOf(img.data.obj.filesetId) > -1) { img.fsSelected = true; } }); } if ($("#icon_table").length == 0) { setupDatasetHeader(); } var json = {'images': imgJson, 'webindex': '/webclient/', 'layout': layout, 'paging': false} // Add 'dataset':id if we have it json['dataset'] = parentNode.type == 'dataset' ? parentNode.data.obj.id : false; var childCount = parentNode.data.obj.childCount; if (PAGE_SIZE < childCount) { json.pageCount = Math.ceil(childCount / PAGE_SIZE); json.paging = true; json.page = inst.get_page(parentNode); } var html = iconTmpl(json); $("#icon_table").html(html); // intial size setIconSize(); // load thumbnails in a batches thumbnailsBatch = 50; iids = $.map(imgJson, function(img){ return img.id; }); thumbUrl = "/webclient/get_thumbnails/"; if (shareId !== null) { thumbUrl = "/webclient/get_thumbnails/" + shareId + "/" } // getThumbnailSet() doesn't work with share permissions, so we get // single thumbnails at a time if (parentNode.type === "share") { thumbnailsBatch = 1; } var promise = OME.load_thumbnails( thumbUrl, iids, thumbnailsBatch, "/static/webgateway/img/image128.png" ); // setup quicksearch filtering of images setupFiltering(); // init the elementsorter plugin setupSorting(); // plugin to handle drag-select of images (share is single-select only) if (parentNode.type !== "share") { setupSelectable(); } // scroll to selected thumbnail (if any) focusThumbnail(); return promise; } // Update thumbnails when we switch between plugins $('#center_panel_chooser').on('center_plugin_changed.ome', update_thumbnails_panel); // Use selected nodes from tree to indicate thumbnails var highlightSelectedThumbs = function(selected) { $("#dataIcons li.row").removeClass("ui-selected").removeClass("fs-selected"); var selFileSets = []; selected.forEach(function(node){ if (node.type == "image") { $("#image_icon-" + node.data.obj.id).addClass("ui-selected"); } var fsId = node.data.obj.filesetId; if (fsId) { selFileSets.push(fsId); } }); selFileSets.forEach(function(fsId){ $("#dataIcons li[data-fileset='" + fsId + "']").addClass("fs-selected"); }); focusThumbnail(); } var focusThumbnail = function() { // We focus the thumbnail to make sure it's scrolled into view var $focused = $(':focus'); $("#dataIcons li.ui-selected").first().trigger('focus'); // Then re-focus the jstree node, so that hot-keys work etc $focused.trigger('focus'); } var getRandom = function() { return (Math.random() + "").slice(2); } var setupSelectable = function() { $("#dataIcons").selectable({ filter: 'li.row', distance: 2, stop: function() { // Make the same selection in the jstree etc syncTreeSelection(); }, start: function() { // Remove any fileset selection markings $("#dataIcons li.row:visible") .removeClass("fs-selected") .removeClass("lastSelected"); } }); } var setupSorting = function() { // simple emulation of table-sorter for other elements... // performs sort when intialised on 'sort-init' column $(".element_sorter").elementsorter({ head: '.thead div', // Selector for the equivalent of 'table head' body: 'li.row', // Selector for the equivalent of 'table rows' sort_key: '.hidden_sort_text' // optional - how to find the text within each child of a 'row'. }); } var setupFiltering = function() { // All the image filtering functionality setup here... // {'id': {'text': 'name', 'images': [1,2,3]} } for all tags used for filtering var usedTags = {}; // {key: {'values':{'imageId': 'val1, val2'}, 'type': 'number'} var usedKeyValues = {}; var currentFilterKey; var filter_tag_ids = []; var filterObjects = []; function doFiltering() { // get image IDs, find which are filtered and update images $("#dataIcons li.row").each(function(){ var $this = $(this); var iid = $this.attr("data-id"); // check if visible in all filters var visible = true; filterObjects.forEach(function(f){ if (!f.isImageVisible(iid)) { visible = false; } }); if (visible) { $this.removeClass('mapFilter_hidden'); } else { $this.addClass('mapFilter_hidden'); } }); // finally, we de-select hidden deselectFiltered(); } // Chooser for revealing various filter components $("#choosefilter").on('change', function() { var $this = $(this), filterby = $this.val(); // reset chooser $this.val("addfilter"); // if we're already showing this filter, ignore if ($("#filter" + filterby).is( ":visible" )) { return; } // Handle 'Remove all filters' if (filterby == "removeAll") { $(".imagefilter").hide(); // clear text filter (will also undo fitering) $('#id_search').val('').trigger('keyup'); // reset rating to 0 $("#filterrating img").attr('src', "/static/webclient/image/rating0.png"); // clear any filtering by rating/unrated $("#dataIcons li.row").removeClass('ratingFilter_hidden'); // reset Tag filtering filter_tag_ids = []; // clear filter objects if any exist if (filterObjects.length) { filterObjects = []; doFiltering(); } renderFilterTags(); } // Unrated and By rating are mutually exclusive if (filterby === "unrated") { // reset 'by rating' filter $("#filterrating").hide().find("img").attr('src', "/static/webclient/image/rating0.png"); // do filtering filterUnrated(); } else if (filterby === "rating") { $("#filterunrated").hide(); $("#dataIcons li.row").removeClass('ratingFilter_hidden'); } if (filterby === "tag") { // Load tags for images var iids = []; $("#dataIcons li.row").each(function(){ iids.push($(this).attr("data-id")); }); // Check to ensure images are available for filtering // if (iids.length === 0) { // // No images to filter // return; // } var query = "image=" + iids.join("&image="); $.getJSON("/webclient/api/annotations/?type=tag&" + query, function(data){ // map imageId to rating... rdata = {'iid': hide?} usedTags = data.annotations.reduce(function(prev, t){ var imgId = t.link.parent.id; if (!prev[t.id]) { prev[t.id] = {'textValue': t.textValue, 'images': []}; } prev[t.id].images.push(imgId); return prev; }, {}); renderFilterTags(); }); } if (filterby === "map") { // Load map annotations for images var iids = []; $("#dataIcons li.row").each(function(){ iids.push($(this).attr("data-id")); }); var f = new MapAnnFilter(iids, $('#center_toolbar'), doFiltering, filterObjects); filterObjects.push(f); return; } // Show filter if (filterby != "addfilter") { $("#filter" + filterby).show(); } }); // Filters have a 'X' for clearing filter $(".filtersearch").on('click', '.removefilter', function(){ var $fltr = $(this).parent(); var fid = $fltr.attr('id'); if (fid === 'filterrating') { $("#dataIcons li.row").removeClass('ratingFilter_hidden'); $("#filterrating img").attr('src', "/static/webclient/image/rating0.png"); } else if (fid === 'filterunrated') { $("#dataIcons li.row").removeClass('ratingFilter_hidden'); $fltr.hide(); } else if ($fltr.attr('data-tagId')) { // Remove a single Tag var tagId = $fltr.attr('data-tagId'); filter_tag_ids = filter_tag_ids.filter(function(i){return i !== tagId}); renderFilterTags(); } else if (fid == 'filtertag') { // Remove ALL tags and hide tag filter $fltr.hide(); filter_tag_ids = []; renderFilterTags(); } }); // Handle filtering by text var $filter_input = $('#id_search'); var filter_txt = $filter_input.val(); $filter_input.quicksearch('#dataIcons li.row', { 'delay': 300, 'bind': 'keyup', 'loader': 'span.loading', 'selector': '.hidden_sort_text', // override to replace & from item.innerHTML with '&' testQuery: function (query, txt, _row) { txt = txt.replace(/&/g, '&') // query is a list of strings for (var i = 0; i < query.length; i += 1) { if (txt.indexOf(query[i]) === -1) { return false; } } return true; }, onAfter: function(){ // onAfter can get triggered without text change, E.g. by tree selection! var new_txt = $filter_input.val(); if (filter_txt != new_txt){ // test text has changed filter_txt = new_txt; deselectFiltered(); } } }); // filter by unrated var filterUnrated = function(){ var iids = []; $("#dataIcons li.row").each(function(){ iids.push($(this).attr("data-id")); }); // Check to ensure images are available for filtering if (iids.length === 0) { // No images to filter return; } var query = "image=" + iids.join("&image="); $.getJSON("/webclient/api/annotations/?type=rating&" + query, function(data){ // map imageId to rating... rdata = {'iid': hide?} var rdata = data.annotations.reduce(function(prev, r){ var iid = "" + r.link.parent.id; prev[iid] = true; return prev; }, {}); $("#dataIcons li.row").each(function(){ var $this = $(this), iid = $this.attr("data-id"); if (rdata[iid]) { // hide $this.addClass('ratingFilter_hidden'); } else { // show $this.removeClass('ratingFilter_hidden'); } }); // finally, we de-select hidden deselectFiltered(); }); }; // filter by rating $("#filterrating img").on('click', function(event) { var iids = [], $rating = $(this), clickX = event.pageX - $rating.offset().left; var rating = (clickX/ $rating.width()) * 5; rating = parseInt(Math.ceil(rating), 10); var src="/static/webclient/image/rating" + rating + ".png"; $rating.attr('src', src); $("#dataIcons li.row").each(function(){ iids.push($(this).attr("data-id")); }); // Check to ensure images are available for filtering if (iids.length === 0) { // No images to filter return; } var query = "image=" + iids.join("&image="); // get ratings... $.getJSON("/webclient/api/annotations/?type=rating&" + query, function(data){ // map imageId to rating... rdata = {'iid': show?} var rdata = data.annotations.reduce(function(prev, r){ // we don't care who owns the rating/link var iid = "" + r.link.parent.id, longValue = r.longValue; if (r.longValue === rating) { prev[iid] = true; } return prev; }, {}); $("#dataIcons li.row").each(function() { var $this = $(this), iid = $this.attr("data-id"); if (rating === 0 || rdata[iid]) { $this.removeClass('ratingFilter_hidden'); } else { $this.addClass('ratingFilter_hidden'); } }); // finally, we de-select hidden deselectFiltered(); }); }); // Filtering by Tag function renderFilterTags() { // Render Tag filter chooser, without current filter tags var tagList = []; for (t in usedTags) { if (usedTags.hasOwnProperty(t)) { tagList.push({id: t, text: usedTags[t].textValue}); } } // sort list of tags tagList.sort(function(a, b){ return a.text < b.text ? -1 : 1; }); var html = tagList.map(function(t){ // don't show tags we're filtering by if (filter_tag_ids.indexOf(t.id) > -1) return ""; return "<option value='" + t.id + "'>" + t.text.escapeHTML() + "</option>"; }).join(""); html = "<option value='0'>Choose Tag</option>" + html; $("#filter_by_tag").html(html); // hide filtered images $("#dataIcons li.row").each(function() { var $this = $(this), iid = parseInt($this.attr("data-id"), 10); // Visible if image has ALL filter tags var visible = filter_tag_ids.reduce(function(prev, tid){ return prev && (usedTags[tid].images.indexOf(iid) > -1); }, true); if (visible) { $this.removeClass('tagFilter_hidden'); } else { $this.addClass('tagFilter_hidden'); } }); // show tags var html = filter_tag_ids.map(function(tagId){ var tagText = usedTags[tagId].textValue.escapeHTML(); return "<div class='tag' data-tagId='" + tagId + "'><a class='tag_inner' href='#'>" + tagText + "</a><span class='removeTag removefilter' title='Remove Tag'>-</span></div>"; }).join(""); $("#currentFilterTags").html(html); }; $("#filter_by_tag").on('change', function(event){ var $this = $(event.target); var tagId = $this.val(); if (filter_tag_ids.indexOf(tagId) === -1) { filter_tag_ids.push(tagId); } renderFilterTags(); // reset choose $this.val('0'); // finally, we de-select hidden deselectFiltered(); }); } // When we filter, unselect the hidden icons and update // selections in the jstree var deselectFiltered = function(e, a){ // check if any selection change is needed var $filtered = $("#dataIcons li.ui-selected:hidden"); if ($filtered.length == 0) return; // If we've filtered any selected images, de-select icon $filtered.removeClass("ui-selected"); var imageId = $filtered.first().data('id'); syncTreeSelection(imageId); }; // handles selection for 'clicks' (not drags) var handleClickSelection = function(event) { // It is possible to select the image itself or its individual container // Handle that here var $targetIcon = $(event.target).closest(".row"); var imageId = $targetIcon.attr('data-id'); // Add ui-selected class to range of icons etc highlightClickedIcons(event, $targetIcon); syncTreeSelection(); } var highlightClickedIcons = function(event, $targetIcon) { /*** * Based on the icon that was selected and any modifier keys * update the thumbnail selections */ // Get the visible icons that might need selecting var $visibleIcons = $("#dataIcons li.row:visible"); // Get the start point for a range select if there is one // and it is visible var $lastSelected = $('#dataIcons .lastSelected:visible'); if ($lastSelected.length === 0) { // possible after drag select - just pick selected $lastSelected = $('#dataIcons .ui-selected:visible'); } // Calculate the index of the last selected item if there was one var lastSelectedIndex = -1; $lastSelected.each(function(index, el) { lastSelectedIndex = $visibleIcons.index(el); }); // Get the index of the target item var targetIndex = $visibleIcons.index($targetIcon); // Remove the starting point as a new one is about to be applied $lastSelected.removeClass('lastSelected'); // Get the platform specific multi-select key var multiSelectKey = OME.multi_key() + "Key"; var selectedIcons = []; var unselectedIcons = []; // If this is a range select if (event.shiftKey && lastSelectedIndex != -1) { // Find the start and end of the range var start = Math.min(lastSelectedIndex, targetIndex); var end = Math.max(lastSelectedIndex, targetIndex); // Mark the icons for selection $visibleIcons.slice(start, end+1).each(function(index, el) { $(el).addClass('ui-selected'); }); // If this is a multi-select } else if (event[multiSelectKey] && lastSelectedIndex != -1) { if ($targetIcon.hasClass('ui-selected')) { $targetIcon.removeClass('ui-selected'); } else { $targetIcon.addClass('ui-selected'); } // This is a single selection or there was no other selection for the range/multi select } else { // Remove all selections $visibleIcons.removeClass("ui-selected fs-selected"); // Add selection for this one item $targetIcon.addClass("ui-selected lastSelected"); } // Mark the most recent selection $targetIcon.addClass('lastSelected'); } // use IDs of the selected visible thumbnails to sync tree selection var syncTreeSelection = function() { var $selectedIcons = $('#dataIcons li.ui-selected'); if (parentNode.type == 'share') { // We don't support multi-selection of images in share $selectedIcons.removeClass('ui-selected'); $selectedIcons = $selectedIcons.first().addClass('ui-selected'); } var imageId = $selectedIcons.first().data('id'); // Get the node that seems to be the current container // Images only, all that is required for now as center panel only shows images var containerNode; if (imageId) { // don't lookup container if no images selected containerNode = OME.getTreeImageContainerBestGuess(imageId); } // Deselect all to begin (supress jstree event) inst.deselect_all(true); var fsIds = {}; if (containerNode) { // Select the selected icons in the tree (supress jstree event) $selectedIcons.each(function(index, el) { var $el = $(el), fsId = $el.data('fileset'); if (fsId != undefined && fsId != '') { fsIds[fsId] = true; } var selectedNode = inst.locate_node($el.data('type') + '-' + $el.data('id'), containerNode)[0]; inst.select_node(selectedNode, true); // we also focus the node, so that hotkey events come from the node if (selectedNode) { $("#" + selectedNode.id).children('.jstree-anchor').trigger('focus'); } }); } else { console.log('jstree may need to be refreshed'); } // Update thumbnail highlighting of filesets highlightFilesets(fsIds); // Since we've suppressed jstree events, we need to manually handle selection change... OME.writeSelectedObjs(undefined, $selectedIcons); // Update the buttons in the jstree if (buttonsShowHide) { buttonsShowHide(inst.get_selected(true), inst); } // Tell jsTree how many elements are selected // This event also bubbles up to $("body") $("#dataTree").trigger('selection_change.ome', inst.get_selected(true).length); return false; } // When an image(s) are selected, also indicate others with the same fileset(s). var highlightFilesets = function(fsIds) { var $selectee = $("#dataIcons").find("li.row"); $selectee.removeClass('fs-selected'); for (var fsid in fsIds) { $selectee.filter("[data-fileset='"+ fsid + "']").addClass('fs-selected'); } } // switch between 'icon' or 'table' layout by switching CSS var setLayout = function() { if (layout == "icon"){ $("#dataIcons").removeClass("tableLayout"); $("#dataIcons").addClass("iconLayout"); } else { $("#dataIcons").removeClass("iconLayout"); $("#dataIcons").addClass("tableLayout"); } // on larger pages, may need to scroll to show selected thumbnail again focusThumbnail(); } // This is called for plates or acquisitions (runs) only. // Was previously used for loading ALL data types but now // images are retrieved from jsTree directly so we only need to // load plates. // TODO: More cleanup needed to remove all non-SPW code var load_spw = function(event, data) { // Get the container that is currently rendered // If any of these are not defined then nothing was selected var $contentDetails = $("div#content_details"); // Get details about the currently displayed container var currentType = $contentDetails.data('type'); var currentId = $contentDetails.data('id'); var currentPath = $contentDetails.data('path') var currentNode = false; var currentPage = $("div#page").data('page'); var currentField = $contentDetails.data('field'); if (currentPage === undefined) { currentPage = 1; } if (currentField === undefined) { currentField = 0; } // Retrieve current container node if (currentType != undefined && currentId != undefined && currentPath != undefined) { currentPath = JSON.parse(currentPath); currentNode = inst.find_omepath(currentPath); } // Get the current selection var selected = inst.get_selected(true); // If there are selections then determine if the currently // rendered container is one of them. var containerNode = false; if (currentNode) { $.each(selected, function(index, node) { if (node === currentNode) { containerNode = node; // Break out of each return false; } }); } // If the containerNode is the current container then this is // a selection change within the container or a pagination/field // change if (containerNode) { // This is a page change if (getPageOr1(inst, containerNode) !== currentPage) { loadThumbnailsPanel(containerNode, getPageOr1(inst, containerNode), undefined); // Fields are only applicable for acquisitions } else if ((containerNode.type === 'acquisition' || containerNode.type === 'plate') && getfieldOr0(inst, containerNode) !== currentField) { loadThumbnailsPanel(containerNode, undefined, getfieldOr0(inst, containerNode)); } // Return as this was either a page/field change or image // selection only return; } // There was no current node or it was not selected // Load the first of the selected nodes containerNode = selected[0]; // While the new selection may not be the same path as the old one // if it is an equivalent node, we can save a reload and simply update // the recorded path, but only if it is the same page/field if (currentNode && inst.omecompare(containerNode, currentNode) && (currentPage === inst.get_page(containerNode || (containerNode.type === 'acquisition' && currentField === inst.get_field(containerNode))))) { $contentDetails.data('path', JSON.stringify(inst.get_omepath(containerNode))); return; } // Load the thumbnails for this container with the appropriate page/field // E.g. public.html tree doesn't support pages/fields var nodePage = inst.get_page ? inst.get_page(containerNode) : 1; var nodeField = inst.get_field ? inst.get_field(containerNode) : 0; loadThumbnailsPanel(containerNode, nodePage, nodeField); return; }; // Load the thumbnails (Will wait until there are no new requests // for 200ms) var loadThumbnailsPanelTimeout = false; var loadThumbnailsPanel = function(node, newPage, newField) { // Reset timeout if within 200ms of the last request if (loadThumbnailsPanelTimeout) { window.clearTimeout(loadThumbnailsPanelTimeout); } loadThumbnailsPanelTimeout = window.setTimeout(function() { // Clear the timeout loadThumbnailsPanelTimeout = false; // Update the central panel var inst = $.jstree.reference('#dataTree'); var url = "/webclient/"; node = inst.get_node(node); var nodeType = node.type; var nodeId = node.data.obj.id; var show = WEBCLIENT.initially_select.join("|"); // clear WEBCLIENT.initially_select so we don't select well next time user loads plate WEBCLIENT.initially_select = []; switch(nodeType) { case 'plate': url += 'load_plate/' + nodeType + '/' + nodeId + '/?index=' + newField; if (show) { url += "&show=" + show; } break; case 'acquisition': url += 'load_plate/' + nodeType + '/' + nodeId + '/?index=' + newField; if (show) { url += "&show=" + show; } break; default: url = undefined; } if (url != undefined) { // Loading screen $("div#content_details").html('<p class="loading_center">Loading... please wait. <img src ="/static/webgateway/img/spinner_big.gif"/></p>'); // Load html from url $("div#content_details").load(url); setThumbnailsPanel(nodeType, nodeId, inst.get_omepath(node), newPage, newField); // Or if it's not a container that can be loaded, sometimes display a message } else { // If there are no children of a project node, display a message if (nodeType === 'project' && !inst.is_parent(node)) { var msg = "<p class='center_message' title='Create Datasets using toolbar or right-click menu'>No Datasets in Project</a>"; $("div#content_details").html(msg); // If there are no children of a screen node, display a message } else if (nodeType === 'screen' && !inst.is_parent(node)) { msg = "<p class='center_message' title='Import Plates using OMERO.insight'>No Plates in Screen</a>"; $("div#content_details").html(msg); } // And ensure the content is empty clearThumbnailsPanel(); } }, 200); }; var clearThumbnailsPanel = function() { var $contentDetails = $("div#content_details"); $contentDetails.empty(); $contentDetails.removeData('type'); $contentDetails.removeData('id'); $contentDetails.removeData('path'); $("div#page").removeData('page'); $contentDetails.removeData('field'); // empty SPW 'spatial' birds eye view below tree if (OME.emptyWellBirdsEye) { OME.emptyWellBirdsEye(); } }; // Allows other web apps to access this method OME.clearThumbnailsPanel = clearThumbnailsPanel; var setThumbnailsPanel = function(type, id, path, page, field) { var $contentDetails = $("div#content_details"); $contentDetails.data('type', type); $contentDetails.data('id', id); $contentDetails.data('path', JSON.stringify(path)); $("div#page").data('page', page); $contentDetails.data('field', field); }; var getPageOr1 = function(inst, node) { if ('get_page' in inst) { return inst.get_page(node); } return 1; }; var getfieldOr0 = function(inst, node) { if ('get_field' in inst) { return inst.get_field(node); } return 0; }; }); </script> <!-- block for extra head elements (not css or javascript) E.g. icon, meta tags --> <link rel="icon" href="/favicon.ico" type="image/x-icon" /> </head> <body> <div id="middle_header"> <div id="middle_header_logo"> <a id="logo" href="https://idr.openmicroscopy.org/" > <img src="/about/img/logos/logo-idr-webclient.png" /> </a> </div> <div id="middle_header_left"> <ul> <!-- Handle links from settings --> <li class="menu_link"> <a href="/webclient/?experimenter=-1" title="Image Data Repository" >Studies</a> </li> <li class="menu_link"> <a href="/mapr/gene/?experimenter=-1" title="Genes browser" >Genes</a> </li> <li class="menu_link"> <a href="/mapr/phenotype/?experimenter=-1" title="Phenotypes browser" >Phenotypes</a> </li> <li class="menu_link"> <a href="/mapr/cellline/?experimenter=-1" title="Cell Lines browser" >Cell Lines</a> </li> <li class="menu_link"> <a href="/mapr/sirna/?experimenter=-1" title="siRNAs browser" >siRNAs</a> </li> <li class="menu_link"> <a href="/mapr/antibody/?experimenter=-1" title="Antibodies browser" >Antibodies</a> </li> <li class="menu_link"> <a href="/mapr/compound/?experimenter=-1" title="Compounds browser" >Compounds</a> </li> <li class="menu_link"> <a href="/mapr/organism/?experimenter=-1" title="Organisms browser" >Organisms</a> </li> <li class="menu_link"> <a href="https://idr.openmicroscopy.org/about/" title="About the Image Data Resource" >About</a> </li> </ul> </div> <div id="middle_header_right"> <!-- Global Tools --> <ul class="header_toolbar" id="script_notifications"> <li> <!-- Script launcher --> <span id="scriptButton" class="scriptButton toolbar_button" href="/webclient/list_scripts/" title="Run Script"></span> <div id="scriptList" class="sub_menu info_panel" style="min-width: 200px !important"> <img id="scriptSpinner" style="display:none" src="/static/webgateway/img/spinner.gif" /> <!-- script list-items loaded here by AJAX --> </div> </li> <script> if (typeof OME === "undefined"){ OME = {}} OME.showActivities = function() { $("#activities_panel").show(); OME.displayStatus(-1); // reset new_results OME.refreshActivities(); } // this is called by the setInterval loop below OME.activitiesUpdate = function() { var i = OME.activitiesInterval; $.get("/webclient/activities/", function(data) { $('#activities_spinner').hide(); var inprogress = $("#inprogress", data).text(); var new_results = $("#new_results", data).text(); var failure = $("#failure", data).text(); // if we've got no jobs still running, stop checking if ((typeof inprogress == 'undefined') || (inprogress.length == 0)) { if (i) clearInterval(i); OME.activitiesInterval = undefined; showJobCount(0); //return; } inprogress = parseInt( inprogress ); if (inprogress==0) { if (i != undefined) { clearInterval(i); OME.activitiesInterval = undefined; } } // display what we recieved, bind events etc $("#activities_content").empty(); $("#activities_content").append( $("#jobsTable", $(data)) ); $("#jobsTable").alternateRowColors(); // bind events $("#activities_content a").on('click', function() { var href = $(this).attr('href'); if (href) { window.location.href = href; } }); // show chgrp errors in new window $(".chgrp_error a").on('click', function(event) { var error_msg = $(this).next().html(); var newWindow=window.open('','','height=500,width=500,scrollbars=yes, top=50, left=100'); newWindow.document.write(error_msg); newWindow.document.close(); return false; }); // If we've got new errors - show Activities if ($("#new_errors", data).length > 0) { $("#activities_panel").show(); new_results = -1; // hide notification number } OME.displayStatus(new_results, inprogress); }).fail(function() { // this requires jQuery 1.5 or later clearInterval(i); }); } // Add new results to the count, unless we have the Activities panel open OME.displayStatus = function(newresults, inprogress) { // update inprogress 'spinner' if (typeof inprogress != 'undefined') { if ((inprogress == '0') || (inprogress == 0)) { $("#runningStatus").hide(); } else { $("#runningStatus").show(); } } // update result count var results_count = parseInt($('#jobstatus').text()) || 0; if (typeof newresults != 'undefined') { var new_results = parseInt(newresults); if (new_results < 0) results_count = 0 // clear else results_count += new_results } // only display if we're not showing activities if ((results_count > 0) && !$("#activities_panel").is(":visible")) { $('#jobstatus').show(); } else { results_count = 0; $('#jobstatus').hide() } $('#jobstatus').text(results_count + ''); // if neither flag is showing, fade out the main icon if ($('#jobstatus').is(":visible") || $('#runningStatus').is(":visible")) { // turning off, see #9154 $("#launch_activities").css('opacity', 1.0); } else { // turning off, see #9154 $("#launch_activities").css('opacity', 0.5); } } OME.refreshActivities = function() { if (OME.activitiesInterval) { return; } OME.activitiesInterval = setInterval(function (){ OME.activitiesUpdate(); }, 5000); OME.activitiesUpdate(); } $(document).ready(function() { OME.displayStatus(-1); // reset new_results OME.refreshActivities(); $("#activities_panel").hide(); // add click handler to entire body, to close the activities panel $('body').on('click', function(event) { var id = event.target.id; //$(".info_panel").hide(); if ((id == 'launch_activities') || (id == 'jobstatus') || (id == 'runningStatus')) { OME.showActivities(); } else { $("#activities_panel .new_result").removeClass('new_result'); $("#activities_panel").hide(); } }); // BUT also stop any events bubbling up from panel itself $("#activities_panel").on('click', function(event) { return false; }); $("#clear_activities").on('click', function() { $.post("/webclient/activities_update/clean/"); $("#jobsTable>tbody>tr").filter(":not(.in_progress)").remove(); }); // close panels on 'ESCAPE' $("body").on('keyup', function(event){ if (event.keyCode === $.ui.keyCode.ESCAPE) { $(".info_panel").hide(); } }); // If a chgrp fails due to filesets, we provide a 'move all' button: $("#activities_content").on("click", ".chgrp_move_all", function(event){ var $this = $(this); if ($this.prop('disabled')){ return false; } $this.prop('disabled', true); $.get($this.attr('url'), function(){ OME.refreshActivities(); }); event.preventDefault(); return false; }); }); </script> <li id="queue"> <span class="toolbar_button" id="launch_activities" title="Activities"> <span id="jobstatus" class="notifier">0</span> <span id="runningStatus" style="display:none;"></span> </span> <div id="activities_panel" class="info_panel"> <div class="info_panel_title"> <h3> Activities <img id='activities_spinner' src="/static/webgateway/img/spinner.gif"/> </h3> <button type="button" id="clear_activities">Clear List</button> </div> <div id="activities_content"> </div> </div> </li> </ul> <!-- Global Search --> <form id="search" action="/webclient/search/" title="Search OMERO"> <div id="top_search_field"> <label class="inline_label" for="id_search_query">Search:</label> <input type="text" name="search_query" size="25" required id="id_search_query"> <input type="submit" value="Go" /> </div> </form> <!-- User Dropdown --> </div> <div class="clear"></div> </div> <div id="content"> <div id="group_user_selection"> <script> $(document).ready(function(){ var $groupList, $listViewPort = $("#listViewPort").css("visibility", "visible").hide(), $usersList, DROPDOWN_HEIGHT = $(document).height()-110; // Button toggles visibility of group chooser dropdown $("#groupsUsersButton").on('click', function(event){ event.preventDefault(); if ($listViewPort.is(":visible")) { $listViewPort.hide(); } else { $listViewPort.show(); // First time we show menu, load if empty, then setup events... if ($listViewPort.is(':empty')) { $listViewPort.append("<h1 style='margin: 10px'>Loading Groups...</h1>"); $listViewPort.load("/webclient/group_user_content/?url=/webclient/userdata/", function() { // prepare lists for show/hide $groupList = $("#groupList"); $usersList = $(".usersList"); $groupList.css('max-height', DROPDOWN_HEIGHT + 'px'); $usersList.css('max-height', DROPDOWN_HEIGHT + 'px'); $groupList.css('visibility', 'visible'); $usersList.css('visibility', 'visible').hide(); // When hover or click on a Group, show Users... $( "#groupList" ).on( "mouseenter click", "li a", function(event) { event.preventDefault(); var gid = $(this).attr("data-gid"); $usersList.hide(); $("#groupMembers-" + gid).show(); return false; }); }); } } return false; }); // Any clicks that bubble up to body hide list... $('body').on('click', function(event) { $listViewPort.hide(); }); }); </script> <ul id="group_user_chooser"> <li class="dropdown_menu"> <!-- Group / User drop-down --> <div id="groupsUsersButton" title="Switch Group/User"> <img src="/static/webclient/image/group_red16.png" /> <span>Public</span><span>Public data</span> </div> <div id="listViewPort" class="dropdown"></div> </li> </ul> </div> <div id="left_panel"> <div class="left_panel_content"> <div id="left_panel_tabs" class="left_panel_tabs_container ui-tabs"> <ul id="left_panel_tab_list" class="ui-tabs-nav"> <!-- Remember to update this in public/public.html as well. We should change this, but for the meantime, you need to manually update the menu there too --> <li id="explore_tab" class="ui-state-default ui-tabs-active"> <a href="/webclient/userdata/" class="ui-tabs-anchor" title="Explore">Explore</a> </li> <li id="tags_tab" class="ui-state-default"> <a href="/webclient/usertags/" class="ui-tabs-anchor">Tags</a> </li> <li id="public_tab" class="ui-state-default"> <a href="/webclient/public/" class="ui-tabs-anchor">Shares</a> </li> </ul> <!-- toolbar above tree --> <div class="left_panel_toolbar" > <!-- Show Projects toolbar for main page... --> <ul class="toolbar"> <li><input id="addprojectButton" class="button button-disabled" type="image" src="/static/webclient/image/folder16.png" alt="Create new project" title="Create new Project"/></li> <li><input id="adddatasetButton" class="button button-disabled" type="image" src="/static/webclient/image/folder_image16.png" alt="Create new dataset" title="Create new Dataset"/></li> <li><input id="addscreenButton" class="button button-disabled" type="image" src="/static/webclient/image/folder_screen16.png" alt="Create new screen" title="Create new Screen"/></li> <li class="seperator"></li> <li><input id="cutButton" class="button button-disabled" type="image" src="/static/webclient/image/icon_toolbar_cut.png" alt="Cut Link" title="Cut Link"/> </li> <li><input id="copyButton" class="button button-disabled" type="image" src="/static/webclient/image/icon_toolbar_copy.png" alt="Copy Link" title="Copy a link to the selected object"/></li> <li><input id="pasteButton" class="button button-disabled" type="image" src="/static/webclient/image/icon_toolbar_paste.png" alt="Paste Link" title="Paste the copied link"/></li> <li class="seperator"> <li><input id="deleteButton" class="button button-disabled" type="image" src="/static/webclient/image/icon_toolbar_delete.png" alt="Delete" title="Delete"/> </li> <li class="seperator"> <li><input id="createshareButton" class="button button-disabled" type="image" src="/static/webclient/image/icon_toolbar_share2.png" alt="Create Share" title="Create Share"></li> <li class="seperator"></li> <li><input id="refreshButton" class="button" type="image" src="/static/webclient/image/icon_toolbar_refresh.png" alt="Refresh" title="Refresh"> </li> </ul> </div> </div> <div class="left_panel_tree_container"> <div id="tree_details" class="left_panel_tree"> <div class="dataTree" id="dataTree"></div> </div> <!-- Panel hidden unless needed for showing spatial birds eye view of Wells - see ome.spwgridview.js --> <div id="well_details" class="left_panel_preview"> <div class="left_panel_preview__header"> <span class="left_panel_preview__title--centered left_panel_preview__title--medium"> Field positions in well </span> <a id="hide_well_birds_eye" class="left_panel_preview__close action" href="#">X</a> </div> <div id="well_birds_eye_container" class="left_paneL_preview__content"> <div id="well_birds_eye" class="left_paneL_preview__well"></div> </div> </div> </div> </div> <!-- hidden form for delete dialogs --> <div id="delete-dialog-form" title="Delete" style="display:none" data-url="/webclient/action/deletemany/" data-fileset-check-url="/webclient/fileset_check/delete/"> <p id="deleteOthersWarning" class='error' style="font-size: 120%; font-weight: bold"> Warning: Some objects you selected are owned by other users. </p> <p id="deleteCopyWarning" class='error' style="font-size: 120%; font-weight: bold"> Warning: One or more <span class="delete_type">Images</span> are linked to multiple <span class="delete_parent_type">Dataset</span>s. This will DELETE them from ALL of those <span class="delete_parent_type">Dataset</span>s. If you only wish to remove <span class="delete_type">Images</span> from one <span class="delete_parent_type">Dataset</span>, use the "Cut Link" action. </p> <p>Are you sure you want to delete the selected <span class="delete_type">Images</span>?</p> <p>If yes:</p> <form> <fieldset style="border: 0px solid white"> <input type="checkbox" name="delete_anns" id="delete_anns" /> Also delete any Annotations that become 'orphans'?<br/> </fieldset> </form> </div> <!-- hidden dialog for new Container --> <form id="new-container-form" title="New..." style="display:none"> <p>Create a new <span id="new_container_type">Container</span>...</p> <p id="new_pds_owner_controls"> <img class="new_pds_owner_icon" src="/static/webclient/image/icon_settings_user.png"> <label>Assign to Owner:</label> <span id="new_pds_owner"></span> <input type="hidden" name="owner" id="id_owner"> <p> <label for="id_name">Name:</label> <input type="text" name="name" size="45" required id="id_name"> </p> <p style="margin-bottom: 5px"> <label for="id_description">Description:</label><br /> <textarea name="description" cols="49" rows="2" id="id_description"> </textarea> </p> </form> <!-- hidden form for chgrp --> <form id="chgrp-form" title="Move to Group" action="/webclient/chgrp/" style="display:none" method="POST"><input type="hidden" name="csrfmiddlewaretoken" value="o6oNluQAPR60Bhwmdy2gz6lEymSblFedUaRkeZc1GCh38xaJdfexTmJ37H7GaCOM"> </form> <!-- hidden form for chown --> <form id="chown-form" title="Change Owner" action="/webclient/chown/" style="display:none" method="POST"><input type="hidden" name="csrfmiddlewaretoken" value="o6oNluQAPR60Bhwmdy2gz6lEymSblFedUaRkeZc1GCh38xaJdfexTmJ37H7GaCOM"> </form> </div> <div id="center_container"> <div id="trayhandle_left"> <div id="swapTree"><img id="lhid_trayhandle_icon_left" class="expanded-left" src="/static/webgateway/img/spacer.gif"></div> <div class="dragHandle" style="left: -2px"> <div id="dragLeft"></div> </div> </div> <div id="center_panel"> <div id="center_panel_header"> <div id="center_panel_chooser"> </div> </div> <div id="content_details" class="center_panel_content"> </div> </div> <div id="trayhandle_right"> <div id="swapMeta"><img id="lhid_trayhandle_icon_right" class="collapsed-right" src="/static/webgateway/img/spacer.gif"></div> <div class="dragHandle" style="right: -2px"> <div id="dragRight"></div> </div> </div> </div> <div id="right_panel"> <div> <div id="annotation_tabs" class="absolute_fill"> <ul id="annotation_tabs_list"> <li><a href="#metadata_general">General</a></li> <!-- include right tabs, as configured in settings.py under "omero.web.ui.right_tabs" --> <li><a href="#metadata_tab">Acquisition</a></li> <li><a href="#preview_tab">Preview</a></li> </ul> <div id="metadata_general" class="right_tab_content" ></div> <!-- include right tab bodies, as configured in settings.py under "omero.web.ui.right_tabs" --> <div id="metadata_tab" class="right_tab_content"></div> <div id="preview_tab" class="right_tab_content"></div> </div> </div> </div> </div> <!-- <div id="footer"> <p>© 2007-2011 University of Dundee & Open Microscopy Environment</p> </div> --> <!-- settings.BASE_INCLUDE_TEMPLATE included here --> </body> </html>