NIHVIVO-592 Improvements to class group browse and bar graphs on home page.

This commit is contained in:
nac26 2011-01-28 18:21:17 +00:00
parent 6b6bd1194a
commit 81813bcb83
4 changed files with 114 additions and 84 deletions

View file

@ -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 ------> */

View file

@ -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();
}); });

View file

@ -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>

View file

@ -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>