NIHVIVO-592 Class group browse for home page. Still waiting on update to JSONServlet (NIHVIVO-1674) so classes in class group can be returned for AJAX requests. Introducing alternative bar graph in lieu of pie chart.

This commit is contained in:
nac26 2011-01-24 18:24:24 +00:00
parent 9f3a4343d2
commit 2be51ef25c
7 changed files with 795 additions and 28 deletions

View file

@ -159,5 +159,8 @@ webapp/web/js/selectivizr.js
# PROBLEM: Can't find any info on licensing. # PROBLEM: Can't find any info on licensing.
webapp/web/js/jquery_plugins/supersleight.js webapp/web/js/jquery_plugins/supersleight.js
# See /doc/3rd-party-licenses.txt for LICENSE file
webapp/web/js/raphael/*
# See /doc/3rd-party-licenses.txt for LICENSE file # See /doc/3rd-party-licenses.txt for LICENSE file
webapp/web/js/sparql/prototype.js webapp/web/js/sparql/prototype.js

View file

@ -0,0 +1,123 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
/* Styles used for class group browse (browse-classgroups.ftl) */
#browse {
clear: both;
width: 920px;
margin: 0 auto;
border: 1px solid #dfe6e6;
background: #f7f9f9;
overflow: hidden;
padding-bottom: 30px;
}
#browse h4 {
width: 13%;
height: 30px;
margin-bottom: 27px;
padding-left: 15px;
font-weight: normal;
line-height: 38px;
color: #fff;
background: #5e6363;
font-size: 20px;
}
/* BROWSE CLASS GROUPS ------> */
ul#browse-classgroups {
float: left;
width: 200px;
border: 1px solid #dde4e3;
border-right: none;
background: #f1f2ee;
margin-left: 34px;
padding: 0 20px 23px 20px;
margin-top: 10px;
padding-top: 10px;
}
ul#browse-classgroups li {
display: block;
border-bottom: 1px solid #dde4e3;
font-size: 18px;
width: 200px;
height: 35px;
line-height: 35px;
}
ul#browse-classgroups li:last-child {
border-bottom: none
}
ul#browse-classgroups a {
display: block;
padding-left: 15px;
width: 200px;
height: 35px;
color: #5e6363;
text-decoration: none;
}
ul#browse-classgroups .count-classes {
font-size: 14px
}
/* BROWSE CLASSES IN CLASS GROUP ------> */
#browse-classes {
float: left;
width: 610px;
border: 1px solid #dde6e5;
background: #fff;
min-height: 230px;
}
ul#classgroup-list {
float: left;
width: 90%;
padding: 0 10px 15px 22px;
margin-top: 20px;
margin-bottom: 10px;
}
ul#classgroup-list.vis {
width: 44%;
border-right: 1px solid #DDE5E4;
}
ul#classgroup-list li {
display: block;
float: left;
width: 100%;
border-bottom: 1px solid #dde4e3;
font-size: 14px;
height: 35px;
line-height: 35px;
}
ul#classgroup-list li:last-child {
border-bottom: none
}
ul#classgroup-list a {
display: block;
padding-left: 15px;
height: 35px;
color: #5e6363;
text-decoration: none;
}
ul#classgroup-list .count-individuals {
font-size: 12px;
}
/* VISUALIZATION ------> */
#visual-graph {
float: left;
width: 308px;
}
#visual-graph h5 {
padding: 20px 0 12px 12px;
width: auto;
font-size: 18px;
font-weight: normal;
}
#pieViz {
width: 308px;
height: 308px;
}
svg text {
font-family: inherit !important;
font-size: 12px !important;
color: #5E6363 !important;
/* display: none;*/
}
svg rect {
cursor: pointer;
}

View file

@ -0,0 +1,141 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
var browseClassGroups = {
// Initial page setup
onLoad: function() {
this.mergeFromTemplate();
this.initObjects();
// this.bindEventListeners();
},
// Add variables from menupage template
mergeFromTemplate: function() {
$.extend(this, browseData);
},
// Create references to frequently used elements for convenience
initObjects: function() {
this.vClassesInClassGroup = $('ul#classgroup-list');
this.browseClassGroupLinks = $('#browse-classgroups li a');
},
// Event listeners. Called on page load
bindEventListeners: function() {
// Listener for classGroup switching
this.browseClassGroupLinks.click(function() {
uri = $(this).attr("data-uri");
browseClassGroups.getVClasses(uri);
return false;
})
},
// Load classes and chart for default class group as defined by template
defaultClassGroup: function() {
if ( this.defaultBrowseClassGroupURI != "false" ) {
this.getVClasses(this.defaultBrowseClassGroupUri);
}
},
getVClasses: function(classgroupUri, alpha) {
url = this.dataServiceUrl + encodeURIComponent(classgroupUri);
if ( alpha && alpha != "all") {
url = url + '&alpha=' + alpha;
}
// First wipe currently displayed classes
this.vClassesInClassGroup.empty();
$.getJSON(url, function(results) {
$.each(results.vClasses, function(i, item) {
name = results.vClasses[i].name;
uri = results.vClasses[i].URI;
indivCount = results.vClasses[i].individualCount;
indexUrl = browseClassGroups.baseUrl + '/individuallist?vclassId=' + encodeURIComponent(uri);
// Build the content of each list item, piecing together each component
listItem = '<li role="listitem">';
listItem += '<a href="'+ indexUrl +'" title="View all '+ name +'content">'+ name +'</a>';
// Include the moniker only if it's not empty and not equal to the VClass name
listItem += ' <span class="count-individuals">(${indivCount})</span>'
listItem += '</li>';
browseClassGroups.vClassesInClassGroup.append(listItem);
})
// set selected class group
browseClassGroups.selectedClassGroup(results.vclassGroup.URI);
});
},
selectedClassGroup: function(vclassUri) {
// Remove active class on all vClasses
$('#browse-childClasses 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
$('#browse-childClasses li a[data-uri="'+ vclassUri +'"]').addClass('selected');
}
};
var graphClassGroups = {
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;
// Create the canvas
var r = Raphael("pieViz", 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"});
// Add the class names as labels and then hide them
chart.label(labels, true);
// getting a JS error in the console when trying to add the class
// "setting a property that has only a getter"
// $('svg text').addClass('hidden');
// so using .hide() instead
$('svg text').hide();
// Was unable to append <a> within <svg> -- was always hidden and couldn't get it to display
// So using jQuery click to add links
$('rect').click(function() {
var index = $('rect').index(this);
var uri = uris[index];
var link = browseClassGroups.baseUrl + '/individuallist?vclassId=' + encodeURIComponent(uri);
window.location = link;
})
// On hover
// 1. Change bar color
// 2. Reveal label
// 3. Highlight class name in main list
chart.hover(function() {
this.bar.attr({fill: "#ccc"});
$('rect').hover(function() {
var index = $('rect').index(this);
$('svg text').eq(index).show();
$('#classgroup-list li a').eq(index).addClass('selected');
})
}, function() {
this.bar.attr({fill: "#999"});
$('rect').hover(function() {
var index = $('rect').index(this);
$('svg text').eq(index).hide();
$('#classgroup-list li a').eq(index).removeClass('selected');
})
});
}
};
$(document).ready(function() {
browseClassGroups.onLoad();
// browseClassGroups.defaultClassGroup();
graphClassGroups.barchart();
});

View file

@ -0,0 +1,405 @@
/*!
* g.Raphael 0.4.1 - Charting library, based on Raphaël
*
* Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
*/
Raphael.fn.g.barchart = function (x, y, width, height, values, opts) {
opts = opts || {};
var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square",
gutter = parseFloat(opts.gutter || "20%"),
chart = this.set(),
bars = this.set(),
covers = this.set(),
covers2 = this.set(),
total = Math.max.apply(Math, values),
stacktotal = [],
paper = this,
multi = 0,
colors = opts.colors || this.g.colors,
singleColor = opts.singleColor,
len = values.length;
if (this.raphael.is(values[0], "array")) {
total = [];
multi = len;
len = 0;
for (var i = values.length; i--;) {
bars.push(this.set());
total.push(Math.max.apply(Math, values[i]));
len = Math.max(len, values[i].length);
}
if (opts.stacked) {
for (var i = len; i--;) {
var tot = 0;
for (var j = values.length; j--;) {
tot +=+ values[j][i] || 0;
}
stacktotal.push(tot);
}
}
for (var i = values.length; i--;) {
if (values[i].length < len) {
for (var j = len; j--;) {
values[i].push(0);
}
}
}
total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
}
total = (opts.to) || total;
var barwidth = width / (len * (100 + gutter) + gutter) * 100,
barhgutter = barwidth * gutter / 100,
barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
stack = [],
X = x + barhgutter,
Y = (height - 2 * barvgutter) / total;
if (!opts.stretch) {
barhgutter = Math.round(barhgutter);
barwidth = Math.floor(barwidth);
}
!opts.stacked && (barwidth /= multi || 1);
for (var i = 0; i < len; i++) {
stack = [];
for (var j = 0; j < (multi || 1); j++) {
var h = Math.round((multi ? values[j][i] : values[i]) * Y),
top = y + height - barvgutter - h,
bar = this.g.finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type).attr({stroke: "none", fill: singleColor ? singleColor : (colors[multi ? j : i]) });
if (multi) {
bars[j].push(bar);
} else {
bars.push(bar);
}
bar.y = top;
bar.x = Math.round(X + barwidth / 2);
bar.w = barwidth;
bar.h = h;
bar.value = multi ? values[j][i] : values[i];
if (!opts.stacked) {
X += barwidth;
} else {
stack.push(bar);
}
}
if (opts.stacked) {
var cvr;
covers2.push(cvr = this.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(this.g.shim));
cvr.bars = this.set();
var size = 0;
for (var s = stack.length; s--;) {
stack[s].toFront();
}
for (var s = 0, ss = stack.length; s < ss; s++) {
var bar = stack[s],
cover,
h = (size + bar.value) * Y,
path = this.g.finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1);
cvr.bars.push(bar);
size && bar.attr({path: path});
bar.h = h;
bar.y = y + height - barvgutter - !!size * .5 - h;
covers.push(cover = this.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr(this.g.shim));
cover.bar = bar;
cover.value = bar.value;
size += bar.value;
}
X += barwidth;
}
X += barhgutter;
}
covers2.toFront();
X = x + barhgutter;
if (!opts.stacked) {
for (var i = 0; i < len; i++) {
for (var j = 0; j < (multi || 1); j++) {
var cover;
covers.push(cover = this.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr(this.g.shim));
cover.bar = multi ? bars[j][i] : bars[i];
cover.value = cover.bar.value;
X += barwidth;
}
X += barhgutter;
}
}
chart.label = function (labels, isBottom) {
labels = labels || [];
this.labels = paper.set();
var L, l = -Infinity;
if (opts.stacked) {
for (var i = 0; i < len; i++) {
var tot = 0;
for (var j = 0; j < (multi || 1); j++) {
tot += multi ? values[j][i] : values[i];
if (j == multi - 1) {
var label = paper.g.labelise(labels[i], tot, total);
L = paper.g.text(bars[0][i].x, y + height - barvgutter / 2, label).insertBefore(covers[i * (multi || 1) + j]);
var bb = L.getBBox();
if (bb.x - 7 < l) {
L.remove();
} else {
this.labels.push(L);
l = bb.x + bb.width;
}
}
}
}
} else {
for (var i = 0; i < len; i++) {
for (var j = 0; j < (multi || 1); j++) {
var label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total);
L = paper.g.text(bars[i * (multi || 1) + j].x, isBottom ? y + height - barvgutter / 2 : bars[i * (multi || 1) + j].y - 10, label).insertBefore(covers[i * (multi || 1) + j]);
var bb = L.getBBox();
if (bb.x - 7 < l) {
L.remove();
} else {
this.labels.push(L);
l = bb.x + bb.width;
}
}
}
}
return this;
};
chart.hover = function (fin, fout) {
covers2.hide();
covers.show();
fout = fout || function () {};
covers.mouseover(fin).mouseout(fout);
return this;
};
chart.hoverColumn = function (fin, fout) {
covers.hide();
covers2.show();
fout = fout || function () {};
covers2.mouseover(fin).mouseout(fout);
return this;
};
chart.click = function (f) {
covers2.hide();
covers.show();
covers.click(f);
return this;
};
chart.each = function (f) {
if (!Raphael.is(f, "function")) {
return this;
}
for (var i = covers.length; i--;) {
f.call(covers[i]);
}
return this;
};
chart.eachColumn = function (f) {
if (!Raphael.is(f, "function")) {
return this;
}
for (var i = covers2.length; i--;) {
f.call(covers2[i]);
}
return this;
};
chart.clickColumn = function (f) {
covers.hide();
covers2.show();
covers2.click(f);
return this;
};
chart.push(bars, covers, covers2);
chart.bars = bars;
chart.covers = covers;
return chart;
};
Raphael.fn.g.hbarchart = function (x, y, width, height, values, opts) {
opts = opts || {};
var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square",
gutter = parseFloat(opts.gutter || "20%"),
chart = this.set(),
bars = this.set(),
covers = this.set(),
covers2 = this.set(),
total = Math.max.apply(Math, values),
stacktotal = [],
paper = this,
multi = 0,
colors = opts.colors || this.g.colors,
singleColor = opts.singleColor,
len = values.length;
if (this.raphael.is(values[0], "array")) {
total = [];
multi = len;
len = 0;
for (var i = values.length; i--;) {
bars.push(this.set());
total.push(Math.max.apply(Math, values[i]));
len = Math.max(len, values[i].length);
}
if (opts.stacked) {
for (var i = len; i--;) {
var tot = 0;
for (var j = values.length; j--;) {
tot +=+ values[j][i] || 0;
}
stacktotal.push(tot);
}
}
for (var i = values.length; i--;) {
if (values[i].length < len) {
for (var j = len; j--;) {
values[i].push(0);
}
}
}
total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
}
total = (opts.to) || total;
var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
bargutter = Math.floor(barheight * gutter / 100),
stack = [],
Y = y + bargutter,
X = (width - 1) / total;
!opts.stacked && (barheight /= multi || 1);
for (var i = 0; i < len; i++) {
stack = [];
for (var j = 0; j < (multi || 1); j++) {
var val = multi ? values[j][i] : values[i],
bar = this.g.finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type).attr({stroke: "none", fill: singleColor ? singleColor : (colors[multi ? j : i])});
if (multi) {
bars[j].push(bar);
} else {
bars.push(bar);
}
bar.x = x + Math.round(val * X);
bar.y = Y + barheight / 2;
bar.w = Math.round(val * X);
bar.h = barheight;
bar.value = +val;
if (!opts.stacked) {
Y += barheight;
} else {
stack.push(bar);
}
}
if (opts.stacked) {
var cvr = this.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(this.g.shim);
covers2.push(cvr);
cvr.bars = this.set();
var size = 0;
for (var s = stack.length; s--;) {
stack[s].toFront();
}
for (var s = 0, ss = stack.length; s < ss; s++) {
var bar = stack[s],
cover,
val = Math.round((size + bar.value) * X),
path = this.g.finger(x, bar.y, val, barheight - 1, false, type, 1);
cvr.bars.push(bar);
size && bar.attr({path: path});
bar.w = val;
bar.x = x + val;
covers.push(cover = this.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(this.g.shim));
cover.bar = bar;
size += bar.value;
}
Y += barheight;
}
Y += bargutter;
}
covers2.toFront();
Y = y + bargutter;
if (!opts.stacked) {
for (var i = 0; i < len; i++) {
for (var j = 0; j < (multi || 1); j++) {
var cover = this.rect(x, Y, width, barheight).attr(this.g.shim);
covers.push(cover);
cover.bar = multi ? bars[j][i] : bars[i];
cover.value = cover.bar.value;
Y += barheight;
}
Y += bargutter;
}
}
chart.label = function (labels, isInside) {
labels = labels || [];
this.labels = paper.set();
var L, l = -Infinity;
if (opts.stacked) {
for (var i = 0; i < len; i++) {
var tot = 0;
for (var j = 0; j < (multi || 1); j++) {
tot += multi ? values[j][i] : values[i];
if (j == multi - 1) {
var label = paper.g.labelise(labels[i], tot, total);
L = paper.g.text(0, bars[j][i].y, label).insertBefore(covers[i * (multi || 1) + j]);
var bb = L.getBBox();
if(isInside)
L.translate(x + bb.width / 2 + 2, bb.height / 4);
else
L.translate(x - bb.width / 2 - 2, bb.height / 4);
}
}
}
} else {
for (var i = 0; i < len; i++) {
for (var j = 0; j < (multi || 1); j++) {
var label = paper.g.labelise(labels[i], multi ? values[j][i] : values[i], total);
L = paper.g.text(0, bars[j][i].y, label).insertBefore(covers[i * (multi || 1) + j]);
var bb = L.getBBox();
if(isInside)
L.translate(x + bb.width / 2 + 2, bb.height / 4);
else
L.translate(x - bb.width / 2 - 2, bb.height / 4);
}
}
}
return this;
};
chart.hover = function (fin, fout) {
covers2.hide();
covers.show();
fout = fout || function () {};
covers.mouseover(fin).mouseout(fout);
return this;
};
chart.hoverColumn = function (fin, fout) {
covers.hide();
covers2.show();
fout = fout || function () {};
covers2.mouseover(fin).mouseout(fout);
return this;
};
chart.each = function (f) {
if (!Raphael.is(f, "function")) {
return this;
}
for (var i = covers.length; i--;) {
f.call(covers[i]);
}
return this;
};
chart.eachColumn = function (f) {
if (!Raphael.is(f, "function")) {
return this;
}
for (var i = covers2.length; i--;) {
f.call(covers2[i]);
}
return this;
};
chart.click = function (f) {
covers2.hide();
covers.show();
covers.click(f);
return this;
};
chart.clickColumn = function (f) {
covers.hide();
covers2.show();
covers2.click(f);
return this;
};
chart.push(bars, covers, covers2);
chart.bars = bars;
chart.covers = covers;
return chart;
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,16 +1,14 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> <#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#-- Browse classgroups on the home page. Could potentially become a widget --> <#-- Browse class groups on the home page. Could potentially become a widget -->
<@allClassGroups /> ${stylesheets.add("/css/browseClassGroups.css")}
<#macro allClassGroups> <#macro allClassGroups classGroups>
<section id="browse" role="region"> <#-- Loop through classGroups first so we can account for situations when all class groups are empty -->
<h4>Browse by</h4> <#assign selected = 'class="selected" ' />
<#assign classGroupList>
<ul id="browse-classgroups" role="list"> <#list classGroups as group>
<#assign selected = 'class="selected" ' />
<#list vClassGroups as group>
<#-- Only display populated class groups --> <#-- Only display populated class groups -->
<#if (group.individualCount > 0)> <#if (group.individualCount > 0)>
<#-- Catch the first populated class group. Will be used later as the default selected class group --> <#-- Catch the first populated class group. Will be used later as the default selected class group -->
@ -30,28 +28,71 @@
<#elseif classGroup.uri == group.uri> <#elseif classGroup.uri == group.uri>
<#assign activeGroup = selected /> <#assign activeGroup = selected />
</#if> </#if>
<li role="listitem"><a ${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">${group.publicName?capitalize} <span class="count-classes">(${group.individualCount})</span></a></li>
</#if> </#if>
</#list> </#list>
</ul> </#assign>
<#-- If requesting the home page without any additional URL parameters, select the first populated class group--> <#-- Display the class group browse only if we have at least one populated class group -->
<#assign defaultSelectedClassGroup = firstPopulatedClassGroup /> <#if firstPopulatedClassGroup??>
<section id="browse" role="region">
<h4>Browse by</h4>
<section id="browse-classes" role="navigation"> <ul id="browse-classgroups" role="list">
<nav> ${classGroupList}
<ul id="classgroup-list" role="list"> </ul>
<#if classes??>
<#-- We don't need to send parameters because the data we need is delivered as template variables --> <#-- If requesting the home page without any additional URL parameters, select the first populated class group-->
<@classesInClassgroup /> <#assign defaultSelectedClassGroup = firstPopulatedClassGroup! />
<#else>
<#-- We need to pass the data to the macro because the only template variable provided by default is vClassGroups --> <section id="browse-classes" role="navigation">
<@classesInClassgroup classes=defaultSelectedClassGroup.classes classGroup=defaultSelectedClassGroup /> <nav>
</#if> <ul id="classgroup-list" class="vis" role="list">
</ul> <#if classes??>
</nav> <#-- We don't need to send parameters because the data we need is delivered as template variables -->
</section> <!-- #browse-classes --> <@classesInClassgroup />
</section> <!-- #browse --> <#else>
<#-- We need to pass the data to the macro because the only template variable provided by default is classGroups -->
<@classesInClassgroup classes=defaultSelectedClassGroup.classes classGroup=defaultSelectedClassGroup />
</#if>
</ul>
</nav>
<#if classes??>
<#-- We don't need to send parameters because the data we need is delivered as template variables -->
<@visualGraph />
<#else>
<#-- We need to pass the data to the macro because the only template variable provided by default is classGroups -->
<@visualGraph classes=defaultSelectedClassGroup.classes classGroup=defaultSelectedClassGroup />
</#if>
</section> <!-- #browse-classes -->
</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
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>
@ -61,3 +102,43 @@
</#if> </#if>
</#list> </#list>
</#macro> </#macro>
<#macro pieChart classes=classes classGroup=classGroup>
<section id="visual-graph" role="region">
<table>
<#list classes?sort_by("individualCount") as class>
<#assign countPercentage = (class.individualCount / classGroup.individualCount) * 100 />
<#if (class.individualCount > 0 && countPercentage?round > 0)>
<tr>
<th>${class.name}</th>
<td>${countPercentage?round}%</td>
</tr>
</#if>
</#list>
</table>
<section id="pieViz" role="region"></section>
</section>
${scripts.add("/themes/wilma/js/jquery_plugins/raphael/raphael.js", "/themes/wilma/js/jquery_plugins/raphael/pie.js")}
</#macro>
<#macro visualGraph classes=classes classGroup=classGroup>
<section id="visual-graph" class="barchart" role="region">
<table class="graph-data">
<#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>
<#-- ${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")}
</#macro>