NIHVIVO-592 Improvements to class group browse and bar graphs on home page.
This commit is contained in:
parent
6b6bd1194a
commit
81813bcb83
4 changed files with 114 additions and 84 deletions
|
@ -64,18 +64,18 @@ ul#browse-classgroups .count-classes {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
min-height: 230px;
|
min-height: 230px;
|
||||||
}
|
}
|
||||||
ul#classgroup-list {
|
ul#classes-in-classgroup {
|
||||||
float: left;
|
float: left;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
padding: 0 10px 15px 22px;
|
padding: 0 10px 15px 22px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
ul#classgroup-list.vis {
|
ul#classes-in-classgroup.vis {
|
||||||
width: 44%;
|
width: 44%;
|
||||||
border-right: 1px solid #DDE5E4;
|
border-right: 1px solid #DDE5E4;
|
||||||
}
|
}
|
||||||
ul#classgroup-list li {
|
ul#classes-in-classgroup li {
|
||||||
display: block;
|
display: block;
|
||||||
float: left;
|
float: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -84,17 +84,17 @@ ul#classgroup-list li {
|
||||||
height: 35px;
|
height: 35px;
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
}
|
}
|
||||||
ul#classgroup-list li:last-child {
|
ul#classes-in-classgroup li:last-child {
|
||||||
border-bottom: none
|
border-bottom: none
|
||||||
}
|
}
|
||||||
ul#classgroup-list a {
|
ul#classes-in-classgroup a {
|
||||||
display: block;
|
display: block;
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
color: #5e6363;
|
color: #5e6363;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
ul#classgroup-list .count-individuals {
|
ul#classes-in-classgroup .count-individuals {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
/* VISUALIZATION ------> */
|
/* VISUALIZATION ------> */
|
||||||
|
|
|
@ -5,17 +5,17 @@ var browseClassGroups = {
|
||||||
onLoad: function() {
|
onLoad: function() {
|
||||||
this.mergeFromTemplate();
|
this.mergeFromTemplate();
|
||||||
this.initObjects();
|
this.initObjects();
|
||||||
// this.bindEventListeners();
|
this.bindEventListeners();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add variables from menupage template
|
// Add variables from browse template
|
||||||
mergeFromTemplate: function() {
|
mergeFromTemplate: function() {
|
||||||
$.extend(this, browseData);
|
$.extend(this, browseData);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Create references to frequently used elements for convenience
|
// Create references to frequently used elements for convenience
|
||||||
initObjects: function() {
|
initObjects: function() {
|
||||||
this.vClassesInClassGroup = $('ul#classgroup-list');
|
this.vClassesInClassGroup = $('ul#classes-in-classgroup');
|
||||||
this.browseClassGroupLinks = $('#browse-classgroups li a');
|
this.browseClassGroupLinks = $('#browse-classgroups li a');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -26,6 +26,25 @@ var browseClassGroups = {
|
||||||
uri = $(this).attr("data-uri");
|
uri = $(this).attr("data-uri");
|
||||||
browseClassGroups.getVClasses(uri);
|
browseClassGroups.getVClasses(uri);
|
||||||
return false;
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Call the bar chart highlighter listener
|
||||||
|
this.chartHighlighterListener();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Listener for bar chart highlighting -- separate from the rest because it needs to be callable
|
||||||
|
chartHighlighterListener: function() {
|
||||||
|
// This essentially replicates the native Raphael hover behavior (see chart.hover below)
|
||||||
|
// but allows us to trigger it via jQuery from the list of classes adjacent to the chart
|
||||||
|
$('ul#classes-in-classgroup li a').hover(function() {
|
||||||
|
var classIndex = $('ul#classes-in-classgroup li a').index(this);
|
||||||
|
$('#visual-graph svg path').eq(classIndex).attr('fill', '#ccc');
|
||||||
|
$('#visual-graph svg text').eq(classIndex).toggle();
|
||||||
|
return false;
|
||||||
|
}, function() {
|
||||||
|
var classIndex = $('ul#classes-in-classgroup li a').index(this);
|
||||||
|
$('#visual-graph svg path').eq(classIndex).attr('fill', '#999');
|
||||||
|
$('#visual-graph svg text').eq(classIndex).toggle();
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -36,6 +55,7 @@ var browseClassGroups = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Where all the magic happens -- gonna fetch me some classes
|
||||||
getVClasses: function(classgroupUri, alpha) {
|
getVClasses: function(classgroupUri, alpha) {
|
||||||
url = this.dataServiceUrl + encodeURIComponent(classgroupUri);
|
url = this.dataServiceUrl + encodeURIComponent(classgroupUri);
|
||||||
if ( alpha && alpha != "all") {
|
if ( alpha && alpha != "all") {
|
||||||
|
@ -45,72 +65,92 @@ var browseClassGroups = {
|
||||||
// First wipe currently displayed classes
|
// First wipe currently displayed classes
|
||||||
this.vClassesInClassGroup.empty();
|
this.vClassesInClassGroup.empty();
|
||||||
|
|
||||||
|
var values = [],
|
||||||
|
labels = [],
|
||||||
|
uris = [];
|
||||||
|
|
||||||
$.getJSON(url, function(results) {
|
$.getJSON(url, function(results) {
|
||||||
$.each(results.vClasses, function(i, item) {
|
$.each(results.classes, function(i, item) {
|
||||||
name = results.vClasses[i].name;
|
name = results.classes[i].name;
|
||||||
uri = results.vClasses[i].URI;
|
uri = results.classes[i].URI;
|
||||||
indivCount = results.vClasses[i].individualCount;
|
indivCount = results.classes[i].entityCount;
|
||||||
indexUrl = browseClassGroups.baseUrl + '/individuallist?vclassId=' + encodeURIComponent(uri);
|
indexUrl = browseClassGroups.baseUrl + '/individuallist?vclassId=' + encodeURIComponent(uri);
|
||||||
// Build the content of each list item, piecing together each component
|
// Only add to the arrays and render classes when they aren't empty
|
||||||
listItem = '<li role="listitem">';
|
if ( indivCount > 0 ) {
|
||||||
listItem += '<a href="'+ indexUrl +'" title="View all '+ name +'content">'+ name +'</a>';
|
values.push(parseInt(indivCount, 10));
|
||||||
// Include the moniker only if it's not empty and not equal to the VClass name
|
labels.push(name + ' (' + parseInt(indivCount, 10) +')');
|
||||||
listItem += ' <span class="count-individuals">(${indivCount})</span>'
|
uris.push(uri);
|
||||||
listItem += '</li>';
|
|
||||||
browseClassGroups.vClassesInClassGroup.append(listItem);
|
// Build the content of each list item, piecing together each component
|
||||||
|
listItem = '<li role="listitem">';
|
||||||
|
listItem += '<a href="'+ indexUrl +'" title="Browse all '+ name +' content">'+ name;
|
||||||
|
listItem += ' <span class="count-individuals">('+ indivCount +')</span></a>';
|
||||||
|
listItem += '</li>';
|
||||||
|
browseClassGroups.vClassesInClassGroup.append(listItem);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
// set selected class group
|
// Set selected class group
|
||||||
browseClassGroups.selectedClassGroup(results.vclassGroup.URI);
|
browseClassGroups.selectedClassGroup(results.classGroupUri);
|
||||||
|
|
||||||
|
// Update the graph
|
||||||
|
graphClassGroups.barchart(values, labels, uris);
|
||||||
|
|
||||||
|
// Call the bar highlighter listener
|
||||||
|
browseClassGroups.chartHighlighterListener();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedClassGroup: function(vclassUri) {
|
// Toggle the active class group so it's clear which is selected
|
||||||
|
selectedClassGroup: function(classGroupUri) {
|
||||||
// Remove active class on all vClasses
|
// Remove active class on all vClasses
|
||||||
$('#browse-childClasses li a.selected').removeClass('selected');
|
$('#browse-classgroups li a.selected').removeClass('selected');
|
||||||
// Can't figure out why using this.selectedBrowseVClass doesn't work here
|
|
||||||
// this.selectedBrowseVClass.removeClass('selected');
|
|
||||||
|
|
||||||
// Add active class for requested vClass
|
// Add active class for requested vClass
|
||||||
$('#browse-childClasses li a[data-uri="'+ vclassUri +'"]').addClass('selected');
|
$('#browse-classgroups li a[data-uri="'+ classGroupUri +'"]').addClass('selected');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var graphClassGroups = {
|
var graphClassGroups = {
|
||||||
|
// Build the bar chart using gRaphael
|
||||||
barchart: function() {
|
barchart: function(values, labels, uris) {
|
||||||
var values = [],
|
// Clear the existing bar chart
|
||||||
labels = [];
|
$('#visual-graph').empty();
|
||||||
uris = [];
|
|
||||||
$("tr").each(function() {
|
|
||||||
values.push(parseInt($("td", this).text(), 10));
|
|
||||||
labels.push($("th", this).text());
|
|
||||||
uris.push($("th", this).attr("data-uri"));
|
|
||||||
});
|
|
||||||
var height = values.length * 37;
|
var height = values.length * 37;
|
||||||
|
|
||||||
// Create the canvas
|
// Create the canvas
|
||||||
var r = Raphael("pieViz", 300, height + 10);
|
var r = Raphael("visual-graph", 300, height + 10);
|
||||||
|
|
||||||
// Hide the table containing the data to be graphed
|
|
||||||
$('table.graph-data').addClass('hidden');
|
|
||||||
var chart = r.g.hbarchart(0, 16, 300, height, [values], {type:"soft", singleColor:"#999"});
|
var chart = r.g.hbarchart(0, 16, 300, height, [values], {type:"soft", singleColor:"#999"});
|
||||||
|
|
||||||
// Add the class names as labels and then hide them
|
// Add the class names as labels and then hide them
|
||||||
chart.label(labels, true);
|
chart.label(labels, true);
|
||||||
// getting a JS error in the console when trying to add the class
|
// Getting a JS error in the console when trying to add the class
|
||||||
// "setting a property that has only a getter"
|
// "setting a property that has only a getter"
|
||||||
// $('svg text').addClass('hidden');
|
// $('svg text').addClass('hidden');
|
||||||
// so using .hide() instead
|
// so using .hide() instead
|
||||||
$('svg text').hide();
|
$('svg text').hide();
|
||||||
|
|
||||||
// Was unable to append <a> within <svg> -- was always hidden and couldn't get it to display
|
// Was unable to append <a> within <svg> -- was always hidden and couldn't get it to display
|
||||||
// So using jQuery click to add links
|
// so using jQuery click to add links
|
||||||
$('rect').click(function() {
|
$('rect').click(function() {
|
||||||
var index = $('rect').index(this);
|
var index = $('rect').index(this);
|
||||||
var uri = uris[index];
|
var uri = uris[index];
|
||||||
var link = browseClassGroups.baseUrl + '/individuallist?vclassId=' + encodeURIComponent(uri);
|
var link = browseClassGroups.baseUrl + '/individuallist?vclassId=' + encodeURIComponent(uri);
|
||||||
window.location = link;
|
window.location = link;
|
||||||
})
|
});
|
||||||
|
|
||||||
|
// Add title attributes to each <rect> in the bar chart
|
||||||
|
$('rect').each(function() {
|
||||||
|
var index = $('rect').index(this);
|
||||||
|
var label = labels[index];
|
||||||
|
var countStart = label.lastIndexOf(' (');
|
||||||
|
var label = label.substring(0, countStart);
|
||||||
|
var title = 'View all '+ label +' content';
|
||||||
|
|
||||||
|
// Add a title attribute
|
||||||
|
$(this).attr('title', title);
|
||||||
|
});
|
||||||
|
|
||||||
// On hover
|
// On hover
|
||||||
// 1. Change bar color
|
// 1. Change bar color
|
||||||
|
@ -121,14 +161,14 @@ var graphClassGroups = {
|
||||||
$('rect').hover(function() {
|
$('rect').hover(function() {
|
||||||
var index = $('rect').index(this);
|
var index = $('rect').index(this);
|
||||||
$('svg text').eq(index).show();
|
$('svg text').eq(index).show();
|
||||||
$('#classgroup-list li a').eq(index).addClass('selected');
|
$('#classes-in-classgroup li a').eq(index).addClass('selected');
|
||||||
})
|
})
|
||||||
}, function() {
|
}, function() {
|
||||||
this.bar.attr({fill: "#999"});
|
this.bar.attr({fill: "#999"});
|
||||||
$('rect').hover(function() {
|
$('rect').hover(function() {
|
||||||
var index = $('rect').index(this);
|
var index = $('rect').index(this);
|
||||||
$('svg text').eq(index).hide();
|
$('svg text').eq(index).hide();
|
||||||
$('#classgroup-list li a').eq(index).removeClass('selected');
|
$('#classes-in-classgroup li a').eq(index).removeClass('selected');
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -136,6 +176,5 @@ var graphClassGroups = {
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
browseClassGroups.onLoad();
|
browseClassGroups.onLoad();
|
||||||
// browseClassGroups.defaultClassGroup();
|
browseClassGroups.defaultClassGroup();
|
||||||
graphClassGroups.barchart();
|
|
||||||
});
|
});
|
|
@ -28,7 +28,7 @@ ${stylesheets.add("/css/browseClassGroups.css")}
|
||||||
<#elseif classGroup.uri == group.uri>
|
<#elseif classGroup.uri == group.uri>
|
||||||
<#assign activeGroup = selected />
|
<#assign activeGroup = selected />
|
||||||
</#if>
|
</#if>
|
||||||
<li role="listitem"><a data-uri="${group.uri}" ${activeGroup}href="${urls.base}/${currentPage}?classgroupUri=${group.uri?url}#browse">${group.publicName?capitalize} <span class="count-classes">(${group.individualCount})</span></a></li>
|
<li role="listitem"><a data-uri="${group.uri}" ${activeGroup}href="${urls.base}/${currentPage}?classgroupUri=${group.uri?url}#browse" title="Browse ${group.publicName?capitalize}">${group.publicName?capitalize} <span class="count-classes">(${group.individualCount})</span></a></li>
|
||||||
</#if>
|
</#if>
|
||||||
</#list>
|
</#list>
|
||||||
</#assign>
|
</#assign>
|
||||||
|
@ -43,11 +43,11 @@ ${stylesheets.add("/css/browseClassGroups.css")}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<#-- If requesting the home page without any additional URL parameters, select the first populated class group-->
|
<#-- If requesting the home page without any additional URL parameters, select the first populated class group-->
|
||||||
<#assign defaultSelectedClassGroup = firstPopulatedClassGroup! />
|
<#assign defaultSelectedClassGroup = firstPopulatedClassGroup />
|
||||||
|
|
||||||
<section id="browse-classes" role="navigation">
|
<section id="browse-classes" role="navigation">
|
||||||
<nav>
|
<nav>
|
||||||
<ul id="classgroup-list" class="vis" role="list">
|
<ul id="classes-in-classgroup" class="vis" role="list">
|
||||||
<#if classes??>
|
<#if classes??>
|
||||||
<#-- We don't need to send parameters because the data we need is delivered as template variables -->
|
<#-- We don't need to send parameters because the data we need is delivered as template variables -->
|
||||||
<@classesInClassgroup />
|
<@classesInClassgroup />
|
||||||
|
@ -66,6 +66,24 @@ ${stylesheets.add("/css/browseClassGroups.css")}
|
||||||
</#if>
|
</#if>
|
||||||
</section> <!-- #browse-classes -->
|
</section> <!-- #browse-classes -->
|
||||||
</section> <!-- #browse -->
|
</section> <!-- #browse -->
|
||||||
|
|
||||||
|
<#----------------------------------------------------------------------------------
|
||||||
|
requestedPage is currently provided by FreemarkerHttpServlet. Should this be moved
|
||||||
|
to PageController? Maybe we should have Java provide the domain name directly
|
||||||
|
instead of the full URL of the requested page? Chintan was also asking for a
|
||||||
|
template variable with the domain name for an AJAX request with visualizations.
|
||||||
|
------------------------------------------------------------------------------------>
|
||||||
|
<#assign domainName = requestedPage?substring(0, requestedPage?index_of("/", 7)) />
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var browseData = {
|
||||||
|
baseUrl: '${domainName + urls.base}',
|
||||||
|
dataServiceUrl: '${domainName + urls.base}/dataservice?getVClassesForVClassGroup=1&classgroupUri=',
|
||||||
|
defaultBrowseClassGroupUri: '${firstPopulatedClassGroup.uri!}'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
${scripts.add("/js/browseClassGroups.js")}
|
||||||
<#else>
|
<#else>
|
||||||
<#-- Would be nice to update classgroups-checkForData.ftl with macro so it could be used here as well -->
|
<#-- Would be nice to update classgroups-checkForData.ftl with macro so it could be used here as well -->
|
||||||
<#-- <#include "classgroups-checkForData.ftl"> -->
|
<#-- <#include "classgroups-checkForData.ftl"> -->
|
||||||
|
@ -75,37 +93,21 @@ ${stylesheets.add("/css/browseClassGroups.css")}
|
||||||
<p>Please <a href="${urls.login}" title="log in to manage this site">log in</a> to manage content.</p>
|
<p>Please <a href="${urls.login}" title="log in to manage this site">log in</a> to manage content.</p>
|
||||||
</#if>
|
</#if>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
<#----------------------------------------------------------------------------------
|
|
||||||
requestedPage is currently provided by FreemarkerHttpServlet. Should this be moved
|
|
||||||
to PageController? Maybe we should have Java provide the domain name directly
|
|
||||||
instead of the full URL of the requested page? Chintan was also asking for a
|
|
||||||
template variable with the domain name for an AJAX request with visualizations.
|
|
||||||
------------------------------------------------------------------------------------>
|
|
||||||
<#assign domainName = requestedPage?substring(0, requestedPage?index_of("/", 7)) />
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
var browseData = {
|
|
||||||
baseUrl: '${domainName + urls.base}',
|
|
||||||
dataServiceUrl: '${domainName + urls.base}/dataservice?getLuceneIndividualsByVClass=1&vclassId=',
|
|
||||||
defaultBrowseClassGroupUri: '${firstPopulatedClassGroup.uri}'
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
${scripts.add("/js/browseClassGroups.js")}
|
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
|
|
||||||
<#macro classesInClassgroup classes=classes classGroup=classGroup>
|
<#macro classesInClassgroup classes=classes classGroup=classGroup>
|
||||||
<#list classes as class>
|
<#list classes as class>
|
||||||
<#if (class.individualCount > 0)>
|
<#if (class.individualCount > 0)>
|
||||||
<li role="listitem"><a href="${urls.base}/individuallist?vclassId=${class.uri?url}">${class.name} <span class="count-individuals"> (${class.individualCount})</span></a></li>
|
<li role="listitem"><a href="${urls.base}/individuallist?vclassId=${class.uri?url}" title="Browse all ${class.name} content">${class.name} <span class="count-individuals"> (${class.individualCount})</span></a></li>
|
||||||
</#if>
|
</#if>
|
||||||
</#list>
|
</#list>
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
|
|
||||||
<#macro pieChart classes=classes classGroup=classGroup>
|
<#macro pieChart classes=classes classGroup=classGroup>
|
||||||
<section id="visual-graph" role="region">
|
<section id="visual-graph" role="region">
|
||||||
<table>
|
<table class="graph-data">
|
||||||
<#list classes?sort_by("individualCount") as class>
|
<#list classes?sort_by("individualCount") as class>
|
||||||
<#assign countPercentage = (class.individualCount / classGroup.individualCount) * 100 />
|
<#assign countPercentage = (class.individualCount / classGroup.individualCount) * 100 />
|
||||||
<#if (class.individualCount > 0 && countPercentage?round > 0)>
|
<#if (class.individualCount > 0 && countPercentage?round > 0)>
|
||||||
|
@ -123,22 +125,11 @@ ${stylesheets.add("/css/browseClassGroups.css")}
|
||||||
${scripts.add("/themes/wilma/js/jquery_plugins/raphael/raphael.js", "/themes/wilma/js/jquery_plugins/raphael/pie.js")}
|
${scripts.add("/themes/wilma/js/jquery_plugins/raphael/raphael.js", "/themes/wilma/js/jquery_plugins/raphael/pie.js")}
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
|
|
||||||
<#macro visualGraph classes=classes classGroup=classGroup>
|
<#macro visualGraph classes=classes classGroup=classGroup>
|
||||||
<section id="visual-graph" class="barchart" role="region">
|
<section id="visual-graph" class="barchart" role="region">
|
||||||
<table class="graph-data">
|
<#-- Will be populated dynamically via AJAX request -->
|
||||||
<#list classes as class>
|
|
||||||
<#if (class.individualCount > 0)>
|
|
||||||
<tr>
|
|
||||||
<th data-uri="${class.uri}">${class.name} (${class.individualCount})</th>
|
|
||||||
<td>${class.individualCount}</td>
|
|
||||||
</tr>
|
|
||||||
</#if>
|
|
||||||
</#list>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<section id="pieViz" role="region"></section>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<#-- ${scripts.add("/themes/wilma/js/jquery_plugins/raphael/raphael.js", "/themes/wilma/js/jquery_plugins/raphael/pie.js")} -->
|
|
||||||
${scripts.add("/js/raphael/raphael.js", "/js/raphael/g.raphael.js", "/js/raphael/g.bar.js")}
|
${scripts.add("/js/raphael/raphael.js", "/js/raphael/g.raphael.js", "/js/raphael/g.bar.js")}
|
||||||
</#macro>
|
</#macro>
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
<section id="browse-classes" role="navigation">
|
<section id="browse-classes" role="navigation">
|
||||||
<nav>
|
<nav>
|
||||||
<ul id="classgroup-list" role="list">
|
<ul id="classes-in-classgroup" role="list">
|
||||||
<#list classes as class>
|
<#list classes as class>
|
||||||
<#if (class.individualCount > 0)>
|
<#if (class.individualCount > 0)>
|
||||||
<li role="listitem"><a href="${urls.base}/${currentPage}?classgroupUri=${classGroup.uri?url}&vclassUri=${class.uri?url}">${class.name} <span class="count-individuals"> (${class.individualCount})</span></a></li>
|
<li role="listitem"><a href="${urls.base}/${currentPage}?classgroupUri=${classGroup.uri?url}&vclassUri=${class.uri?url}">${class.name} <span class="count-individuals"> (${class.individualCount})</span></a></li>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue