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);
// Only add to the arrays and render classes when they aren't empty
if ( indivCount > 0 ) {
values.push(parseInt(indivCount, 10));
labels.push(name + ' (' + parseInt(indivCount, 10) +')');
uris.push(uri);
// Build the content of each list item, piecing together each component // Build the content of each list item, piecing together each component
listItem = '<li role="listitem">'; listItem = '<li role="listitem">';
listItem += '<a href="'+ indexUrl +'" title="View all '+ name +'content">'+ name +'</a>'; listItem += '<a href="'+ indexUrl +'" title="Browse all '+ name +' content">'+ name;
// Include the moniker only if it's not empty and not equal to the VClass name listItem += ' <span class="count-individuals">('+ indivCount +')</span></a>';
listItem += ' <span class="count-individuals">(${indivCount})</span>'
listItem += '</li>'; listItem += '</li>';
browseClassGroups.vClassesInClassGroup.append(listItem); 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(values, labels, uris) {
// Clear the existing bar chart
$('#visual-graph').empty();
barchart: function() {
var values = [],
labels = [];
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,15 +66,6 @@ ${stylesheets.add("/css/browseClassGroups.css")}
</#if> </#if>
</section> <!-- #browse-classes --> </section> <!-- #browse-classes -->
</section> <!-- #browse --> </section> <!-- #browse -->
<#else>
<#-- Would be nice to update classgroups-checkForData.ftl with macro so it could be used here as well -->
<#-- <#include "classgroups-checkForData.ftl"> -->
<h3>There is currently no content in the system</h3>
<#if !user.loggedIn>
<p>Please <a href="${urls.login}" title="log in to manage this site">log in</a> to manage content.</p>
</#if>
</#if>
<#---------------------------------------------------------------------------------- <#----------------------------------------------------------------------------------
requestedPage is currently provided by FreemarkerHttpServlet. Should this be moved requestedPage is currently provided by FreemarkerHttpServlet. Should this be moved
@ -87,25 +78,36 @@ ${stylesheets.add("/css/browseClassGroups.css")}
<script type="text/javascript"> <script type="text/javascript">
var browseData = { var browseData = {
baseUrl: '${domainName + urls.base}', baseUrl: '${domainName + urls.base}',
dataServiceUrl: '${domainName + urls.base}/dataservice?getLuceneIndividualsByVClass=1&vclassId=', dataServiceUrl: '${domainName + urls.base}/dataservice?getVClassesForVClassGroup=1&classgroupUri=',
defaultBrowseClassGroupUri: '${firstPopulatedClassGroup.uri}' defaultBrowseClassGroupUri: '${firstPopulatedClassGroup.uri!}'
}; };
</script> </script>
${scripts.add("/js/browseClassGroups.js")} ${scripts.add("/js/browseClassGroups.js")}
<#else>
<#-- Would be nice to update classgroups-checkForData.ftl with macro so it could be used here as well -->
<#-- <#include "classgroups-checkForData.ftl"> -->
<h3>There is currently no content in the system</h3>
<#if !user.loggedIn>
<p>Please <a href="${urls.login}" title="log in to manage this site">log in</a> to manage content.</p>
</#if>
</#if>
</#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>