diff --git a/idr_gallery/gallery_settings.py b/idr_gallery/gallery_settings.py
index 9a8d78f8..efcbe543 100644
--- a/idr_gallery/gallery_settings.py
+++ b/idr_gallery/gallery_settings.py
@@ -31,11 +31,21 @@
["BASE_URL",
None,
str_slash,
- ("Base URL to use for JSON AJAX requests."
+ ("Base URL to use for non-gallery JSON AJAX requests."
" e.g. 'https://demo.openmicroscopy.org'."
" This allows data to be loaded from another OMERO server."
" The default behaviour is to use the current server.")],
+ "omero.web.gallery.gallery_index":
+ ["GALLERY_INDEX",
+ None,
+ str_slash,
+ ("Base gallery URL to use for gallery JSON AJAX requests."
+ " e.g. 'https://idr.openmicroscopy.org/' is gallery index on IDR."
+ " This allows data to be loaded from another OMERO server, e.g. run"
+ " locally or on test server, but load data from IDR."
+ " Default behaviour is to use current server webgallery_index")],
+
"omero.web.gallery.category_queries":
["CATEGORY_QUERIES",
('{}'),
@@ -143,6 +153,19 @@
" If a 'regex' and 'template' are specified, we try"
" name.replace(regex, template).")],
+ "omero.web.gallery.index_json_url":
+ ["INDEX_JSON_URL",
+ 'https://raw.githubusercontent.com/will-moore/idr.openmicroscopy.org/idr_index_data/_data/idr_index.json',
+ str,
+ "URL to load JSON, with a 'tabs' list of {'title':'', 'text':'', 'videos':[]}"
+ ],
+ "omero.web.gallery.idr_studies_url":
+ ["IDR_STUDIES_URL",
+ 'https://raw.githubusercontent.com/IDR/idr.openmicroscopy.org/master/_data/studies.tsv',
+ str,
+ "URL to IDR studies as a tsv table"
+ ],
+
}
process_custom_settings(sys.modules[__name__], 'GALLERY_SETTINGS_MAPPING')
diff --git a/idr_gallery/static/idr_gallery/autocomplete.js b/idr_gallery/static/idr_gallery/autocomplete.js
new file mode 100644
index 00000000..f0447225
--- /dev/null
+++ b/idr_gallery/static/idr_gallery/autocomplete.js
@@ -0,0 +1,152 @@
+
+// ------ AUTO-COMPLETE -------------------
+
+document.getElementById('maprConfig').onchange = (event) => {
+ document.getElementById('maprQuery').value = '';
+ let value = event.target.value.replace('mapr_', '');
+ let placeholder = `Type to filter values...`;
+ if (mapr_settings[value]) {
+ placeholder = `Type ${mapr_settings[value]['default'][0]}...`;
+ }
+ document.getElementById('maprQuery').placeholder = placeholder;
+ // Show all autocomplete options...
+ $("#maprQuery").focus();
+
+ // render();
+}
+
+function showAutocomplete(event) {
+ var configId = document.getElementById("maprConfig").value;
+ var autoCompleteValue = event.target.value;
+
+ if (configId.indexOf('mapr_') != 0) {
+ // If not MAPR search, show all auto-complete results
+ autoCompleteValue = '';
+ }
+
+ $("#maprQuery").autocomplete("search", autoCompleteValue);
+}
+
+document.getElementById('maprQuery').onfocus = function (event) {
+ showAutocomplete(event);
+};
+
+document.getElementById('maprQuery').onclick = function (event) {
+ showAutocomplete(event);
+};
+
+function showSpinner() {
+ document.getElementById('spinner').style.visibility = 'visible';
+}
+function hideSpinner() {
+ document.getElementById('spinner').style.visibility = 'hidden';
+}
+
+// Initial setup...
+$("#maprQuery")
+ .keyup(event => {
+ if (event.which == 13) {
+ let configId = document.getElementById("maprConfig").value;
+ document.location.href = `search/?query=${configId}:${event.target.value}`;
+ }
+ })
+ .autocomplete({
+ autoFocus: false,
+ delay: 1000,
+ source: function (request, response) {
+
+ // if configId is not from mapr, we filter on mapValues...
+ let configId = document.getElementById("maprConfig").value;
+ if (configId.indexOf('mapr_') != 0) {
+
+ let matches;
+ if (configId === 'Name') {
+ matches = model.getStudiesNames(request.term);
+ } else if (configId === 'Group') {
+ matches = model.getStudiesGroups(request.term);
+ } else {
+ matches = model.getKeyValueAutoComplete(configId, request.term);
+ }
+ response(matches);
+ return;
+ }
+
+ // Don't handle empty query for mapr
+ if (request.term.length == 0) {
+ return;
+ }
+
+ // Auto-complete to filter by mapr...
+ configId = configId.replace('mapr_', '');
+ let case_sensitive = false;
+
+ let requestData = {
+ case_sensitive: case_sensitive,
+ }
+ let url;
+ if (request.term.length === 0) {
+ // Try to list all top-level values.
+ // This works for 'wild-card' configs where number of values is small e.g. Organism
+ // But will return empty list for e.g. Gene
+ url = `${BASE_URL}mapr/api/${configId}/`;
+ requestData.orphaned = true
+ } else {
+ // Find auto-complete matches
+ url = `${BASE_URL}mapr/api/autocomplete/${configId}/`;
+ requestData.value = case_sensitive ? request.term : request.term.toLowerCase();
+ requestData.query = true; // use a 'like' HQL query
+ }
+ showSpinner();
+ $.ajax({
+ dataType: "json",
+ type: 'GET',
+ url: url,
+ data: requestData,
+ success: function (data) {
+ hideSpinner();
+ if (request.term.length === 0) {
+ // Top-level terms in 'maps'
+ if (data.maps && data.maps.length > 0) {
+ let terms = data.maps.map(m => m.id);
+ terms.sort();
+ response(terms);
+ }
+ }
+ else if (data.length > 0) {
+ response($.map(data, function (item) {
+ return item;
+ }));
+ } else {
+ response([{ label: 'No results found.', value: -1 }]);
+ }
+ },
+ error: function (data) {
+ hideSpinner();
+ response([{ label: 'Loading auto-complete terms failed. Server may be busy.', value: -1 }]);
+ }
+ });
+ },
+ minLength: 0,
+ open: function () { },
+ close: function () {
+ // $(this).val('');
+ return false;
+ },
+ focus: function (event, ui) { },
+ select: function (event, ui) {
+ if (ui.item.value == -1) {
+ // Ignore 'No results found'
+ return false;
+ }
+ // show temp message in case loading search page is slow
+ $(this).val("loading search results...");
+ // Load search page...
+ let configId = document.getElementById("maprConfig").value;
+ document.location.href = `search/?query=${configId}:${ui.item.value}`;
+ return false;
+ }
+ }).data("ui-autocomplete")._renderItem = function (ul, item) {
+ return $("
")
+ .append("" + item.label + "")
+ .appendTo(ul);
+ }
\ No newline at end of file
diff --git a/idr_gallery/static/idr_gallery/categories.js b/idr_gallery/static/idr_gallery/categories.js
index a7005c3b..7a494c2c 100644
--- a/idr_gallery/static/idr_gallery/categories.js
+++ b/idr_gallery/static/idr_gallery/categories.js
@@ -1,369 +1,319 @@
-"use strict";
-
-// Copyright (C) 2019-2020 University of Dundee & Open Microscopy Environment.
+// Copyright (C) 2019-2022 University of Dundee & Open Microscopy Environment.
// All rights reserved.
+
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
+
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
+
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
+
// NB: SOURCE FILES are under /src. Compiled files are under /static/
-// loaded below
-var mapr_settings = {}; // Model for loading Projects, Screens and their Map Annotations
-var model = new StudiesModel(); // ----- event handling --------
+// loaded below
+let mapr_settings = {};
-document.getElementById('maprConfig').onchange = function (event) {
- document.getElementById('maprQuery').value = '';
- var value = event.target.value.replace('mapr_', '');
- var placeholder = "Type to filter values...";
+// Model for loading Projects, Screens and their Map Annotations
+let model = new StudiesModel();
- if (mapr_settings[value]) {
- placeholder = "Type ".concat(mapr_settings[value]['default'][0], "...");
- }
+model.subscribe('thumbnails', (event, data) => {
+ // Will get called when each batch of thumbnails is loaded
+ renderThumbnails(data);
+});
- document.getElementById('maprQuery').placeholder = placeholder; // Show all autocomplete options...
- $("#maprQuery").focus();
- render();
-};
+let getTooltipContent = (reference) => {
+ return reference.querySelector(".idr_tooltip").innerHTML;
+}
-document.getElementById('maprQuery').onfocus = function (event) {
- $("#maprQuery").autocomplete("search", event.target.value);
-}; // ------ AUTO-COMPLETE -------------------
+function renderStudyContainers(containers) {
+ return ['screen', 'project'].map(objType => {
+ let studies = containers[objType];
+ let count = studies.length;
+ if (count == 0) return;
+ // Link to first Project or Screen
+ return `${count} ${objType == 'project' ? 'Experiment' : 'Screen'}${count === 1 ? '' : 's'}`;
+ }).filter(Boolean).join(", ");
+}
+function studyHtml(study, studyObj) {
+ let idrId = study.Name.split("-")[0];
+ let authors = model.getStudyValue(study, "Publication Authors") || " ";
+ authors = authors.split(",")[0];
+ let title = escapeHTML(getStudyTitle(model, study));
+ let pubmed = studyObj["pubmed_id"];
+ return `
+