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:
parent
9f3a4343d2
commit
2be51ef25c
7 changed files with 795 additions and 28 deletions
|
@ -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
|
||||||
|
|
123
webapp/web/css/browseClassGroups.css
Normal file
123
webapp/web/css/browseClassGroups.css
Normal 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;
|
||||||
|
}
|
141
webapp/web/js/browseClassGroups.js
Normal file
141
webapp/web/js/browseClassGroups.js
Normal 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();
|
||||||
|
});
|
405
webapp/web/js/raphael/g.bar.js
Normal file
405
webapp/web/js/raphael/g.bar.js
Normal 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;
|
||||||
|
};
|
7
webapp/web/js/raphael/g.raphael.js
Normal file
7
webapp/web/js/raphael/g.raphael.js
Normal file
File diff suppressed because one or more lines are too long
7
webapp/web/js/raphael/raphael.js
Normal file
7
webapp/web/js/raphael/raphael.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -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>
|
Loading…
Add table
Reference in a new issue