|
<!DOCTYPE html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>CountToPack</title> |
|
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet"> |
|
<style> |
|
body { |
|
font-family: 'Source Sans Pro', sans-serif; |
|
} |
|
.header { |
|
text-align: center; |
|
font-weight: bold; |
|
font-size: 18px; |
|
} |
|
#chart { |
|
text-align: center; |
|
} |
|
input:disabled+label { |
|
color: #ccc; |
|
} |
|
.shadow { |
|
border-radius: 3px; |
|
border: 1px #000 solid; |
|
box-shadow: 0 0 3px gray, inset 0 0 3px gray; |
|
background: #fff; |
|
} |
|
/* log */ |
|
.right { |
|
position: absolute; |
|
top:1%; |
|
right:1%; |
|
} |
|
#load { |
|
height:20px; |
|
padding:4px; |
|
} |
|
#legend { |
|
display: flex; |
|
justify-content: center; |
|
} |
|
/* option button */ |
|
.left { |
|
position: absolute; |
|
top:1%; |
|
left:1%; |
|
} |
|
.menu { |
|
white-space: nowrap; |
|
transition: width 1s, height 1s ease-in-out; |
|
width:50px; |
|
height:20px; |
|
padding:4px; |
|
} |
|
.menu div { |
|
background: white; |
|
} |
|
.menu .wrapper { |
|
overflow: hidden; |
|
white-space: nowrap; |
|
display: inline-block; |
|
transition: width 1s, height 1s ease-in-out; |
|
width:0%; |
|
height:0%; |
|
} |
|
.menu .wrapper .content { |
|
padding:2px; |
|
} |
|
.menu:hover { |
|
-webkit-transition: width 1s, height 1s ease-in-out; |
|
-moz-transition: width 1s, height 1s ease-in-out; |
|
-o-transition: width 1s, height 1s ease-in-out; |
|
transition: width 1s, height 1s ease-in-out; |
|
width:100%; |
|
height:100%; |
|
} |
|
.menu:hover .wrapper { |
|
-webkit-transition: width 1s, height 1s ease-in-out; |
|
-moz-transition: width 1s, height 1s ease-in-out; |
|
-o-transition: width 1s, height 1s ease-in-out; |
|
transition: width 1s, height 1s ease-in-out; |
|
width:100%; |
|
height:100%; |
|
} |
|
/* Tooltip */ |
|
#tip { |
|
position:absolute; |
|
z-index:3; |
|
padding:10px; |
|
white-space:nowrap; |
|
pointer-events:none; |
|
opacity:0; |
|
background-color: #FFF; |
|
} |
|
#tip label { |
|
font-weight: bold; |
|
display: inline-block; |
|
} |
|
</style> |
|
<script src="https://use.fontawesome.com/b6ac3d3b75.js"></script> |
|
<script src="https://d3js.org/d3-array.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-collection.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-color.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-dispatch.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-dsv.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-ease.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-hierarchy.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-request.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-scale.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-selection.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-timer.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-transition.v1.min.js"></script> |
|
|
|
</head> |
|
<body> |
|
<div class='left'> |
|
<div class='menu shadow'> |
|
<div><span class='fa fa-cog'></span>Menu</div> |
|
<div class='wrapper'> |
|
<div class='content'> |
|
<fieldset> |
|
<legend>Count:</legend> |
|
<input type='radio' name='mode' value='none' id='modenone' checked /><label for='modenone'>-none-</label> |
|
<input type='radio' name='mode' value='media' id='modemedia' /><label for='modemedia'>Media</label> |
|
<input type='radio' name='mode' value='annots' id='modeannot' /><label for='modeannot'>Annotations</label> |
|
</fieldset> |
|
<fieldset> |
|
<legend>Group by:</legend> |
|
<input type='radio' name='group' value='none' id='groupnone' checked /><label for='groupnone'>-none-</label> |
|
<input type='radio' name='group' value='type' id='grouptype' /><label for='grouptype'>Media Type</label> |
|
<input type='radio' name='group' value='user' id='groupuser' /><label for='groupuser'>Reviewer</label> |
|
</fieldset> |
|
<fieldset> |
|
<legend>Sub-group:</legend> |
|
<input type='radio' name='sub' value='none' id='subnone' checked /><label for='subnone'>-none-</label> |
|
<input type='radio' name='sub' value='type' id='subtype' /><label for='subtype'>Media Type</label> |
|
<input type='radio' name='sub' value='user' id='subuser' /><label for='subuser'>Reviewer</label> |
|
</fieldset> |
|
<!--<button type='button' onClick='update()'>Update</button>--> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div class='right shadow'> |
|
<div id='load'></div> |
|
</div> |
|
<div class='header'>iCLiKVAL Content Overview</div> |
|
<div class='header' id='mode'></div> |
|
<div id='chart'></div> |
|
<div id='legend'></div> |
|
<div id='tip' class='shadow'></div> |
|
<script type="text/javascript"> |
|
// params |
|
var p = { |
|
diameter: 600, // root circle diameter |
|
margin: 5, // root circle margin |
|
rmin: 55, // min bubble diameter to display full text |
|
catmax: 100, // max number of users requested |
|
labels: { |
|
// media type use colorbrewer palette |
|
root: {label: 'iCLiKVAL', fg: '#BBB', bg: '#EEE'}, |
|
iclikval: {label: 'system', fg: '#BBB', bg: '#EEE'}, |
|
audio: {label: 'Audio', fg: '#ff7f00', bg: '#fdbf6f'}, |
|
dataset: {label: 'Dataset', fg: '#33a02c', bg: '#b2df8a'}, |
|
image: {label: 'Image', fg: '#6a3d9a', bg: '#cab2d6'}, |
|
journal_article: {label: 'Journal Article', fg: '#1f78b4', bg: '#a6cee3'}, |
|
video: {label: 'Video', fg: '#e31a1c', bg: '#fb9a99'}, |
|
media: {label: 'Media'}, |
|
annots: {label: 'Annotations'} |
|
}, |
|
// users use badges palette |
|
bg: ['#F88', '#FA8', '#FF8', '#AF8', '#8F8', '#8FA', '#8FF', '#8AF', '#88F', '#A8F', '#F8F', '#F8A'], |
|
fg: ['#900', '#960', '#990', '#690', '#090', '#096', '#099', '#069', '#009', '#609', '#909', '#906'], |
|
color: d3.scaleOrdinal(d3.range(12)) |
|
}; |
|
// State |
|
var state = { |
|
mode: 'none', |
|
group: 'none', |
|
sub: 'none', |
|
enabled: { |
|
modenone: true, modemedia: false, modeannot: false, |
|
groupnone: true, grouptype: false, groupuser: false, |
|
subnone: true, subtype: false, subuser: false, |
|
} |
|
}; |
|
// tmp |
|
var uMap = {others: 0}; |
|
|
|
// RUN |
|
init(); |
|
|
|
function init() { |
|
console.log('INIT'); |
|
// menu onchange check + init at none |
|
d3.selectAll('input[type="radio"]').on('change', check); |
|
d3.selectAll('input[value="none"]').property('checked', true); |
|
// hide count none |
|
d3.select('#modenone').style('display', 'none'); |
|
d3.select('label[for="modenone"]').style('display', 'none'); |
|
// init the options |
|
check(); |
|
// loading msg |
|
d3.select('#load').html('<span class="fa fa-spinner fa-spin"></span>Loading...') |
|
// legend |
|
var div = d3.select('#legend') |
|
.style('display', 'none') |
|
.selectAll('div') |
|
.data(['audio', 'dataset', 'image', 'journal_article', 'video']) |
|
.enter().append('div') |
|
.style('display', 'flex') |
|
div.append('div') |
|
.style('border', d => `3px solid ${p.labels[d].fg}`) |
|
.style('color', d => p.labels[d].fg) |
|
.style('background', d => p.labels[d].bg) |
|
.style('width', '25px') |
|
.style('text-align', 'center') |
|
.style('margin', '0 3px') |
|
div.append('span').text(d => p.labels[d].label) |
|
// request media and annot |
|
var q = []; |
|
q.push(requestMedia()); |
|
q.push(requestAnnots()); |
|
return Promise.all(q) |
|
.then(() => { |
|
d3.select('#load').html('<span class="fa fa-check"></span>Loaded'); |
|
}).catch(err => { |
|
d3.select('#load').html('<span class="fa fa-exclamation"></span>Error') |
|
console.log('init error', err); |
|
}); |
|
} |
|
|
|
function check() { |
|
// console.log('CHECK'); |
|
// update selected |
|
state.mode = d3.select('input[name="mode"]:checked').node().value; |
|
state.group = d3.select('input[name="group"]:checked').node().value; |
|
state.sub = d3.select('input[name="sub"]:checked').node().value; |
|
// get inputs |
|
var gnone = d3.select('#groupnone'); |
|
var guser = d3.select('#groupuser'); |
|
var gtype = d3.select('#grouptype'); |
|
var snone = d3.select('#subnone'); |
|
var stype = d3.select('#subtype'); |
|
var suser = d3.select('#subuser'); |
|
|
|
// mode |
|
if(state.mode === 'media') { |
|
// deselect group user |
|
if (guser.property('checked')) { |
|
gtype.property('checked', true); |
|
} |
|
// deselect sub |
|
snone.property('checked', true); |
|
// disabled |
|
state.enabled.grouptype = true; |
|
state.enabled.groupuser = false; |
|
state.enabled.subtype = false; |
|
state.enabled.subuser = false; |
|
} else if(state.mode ==='annots') { |
|
// group |
|
state.enabled.grouptype = true; |
|
if(state.byuser) { |
|
state.enabled.groupuser = true; |
|
} |
|
|
|
if(state.group === 'type') { |
|
// deselect sub type |
|
if (stype.property('checked')) { |
|
snone.property('checked', true); |
|
} |
|
state.enabled.subtype = false; |
|
if(state.byuser) { |
|
state.enabled.subuser = true; |
|
} |
|
} else if(state.group === 'user') { |
|
// deselect sub user |
|
if (suser.property('checked')) { |
|
snone.property('checked', true); |
|
} |
|
state.enabled.subuser = false; |
|
state.enabled.subtype = true; |
|
} else { |
|
state.enabled.subuser = false; |
|
state.enabled.subtype = false; |
|
} |
|
} else { // group none |
|
gnone.property('checked', true); |
|
state.enabled.groupuser = false; |
|
state.enabled.grouptype = false; |
|
snone.property('checked', true); |
|
state.enabled.subuser = false; |
|
state.enabled.subtype = false; |
|
} |
|
// update state |
|
state.mode = d3.select('input[name="mode"]:checked').node().value; |
|
state.group = d3.select('input[name="group"]:checked').node().value; |
|
state.sub = d3.select('input[name="sub"]:checked').node().value; |
|
// update enabled |
|
Object.keys(state.enabled).forEach(k => { |
|
d3.select(`#${k}`).property('disabled', !state.enabled[k]); |
|
}) |
|
// legend |
|
if(state.group === 'type' || state.sub === 'type') { |
|
d3.select('#legend').style('display', 'flex'); |
|
} else { |
|
d3.select('#legend').style('display', 'none'); |
|
} |
|
// Tooltip |
|
if(state.mode === 'annots') {p.tipWidth = 95} |
|
else {p.tipWidth = 55} |
|
// update view |
|
update(); |
|
} |
|
|
|
function update() { |
|
// console.log('UPDATE'); |
|
var title = d3.select('#mode'); |
|
var data; |
|
if(state.mode === 'media') { |
|
title.text(`Media Count: ${state.media.value.toLocaleString()}`); |
|
// data = state.media; |
|
if(state.group === 'type') { |
|
data = state.media; |
|
} else { |
|
data = JSON.parse(JSON.stringify(state.media)); |
|
delete data.children; |
|
} |
|
} else if(state.mode === 'annots') { |
|
title.text(`Annotation Count: ${state.bytype.value.toLocaleString()}`) |
|
if(state.group === 'user') { |
|
if(state.sub === 'none') { |
|
data = JSON.parse(JSON.stringify(state.byuser)); |
|
data.children.forEach(f => { |
|
delete f.children; |
|
}); |
|
} else { |
|
data = state.byuser; |
|
} |
|
} else if(state.group === 'none'){ |
|
data = JSON.parse(JSON.stringify(state.bytype)); |
|
delete data.children; |
|
} else { |
|
if(state.sub === 'none') { |
|
data = JSON.parse(JSON.stringify(state.bytype)); |
|
data.children.forEach(f => { |
|
delete f.children; |
|
}); |
|
} else { |
|
data = state.bytype; |
|
} |
|
} |
|
} |
|
if (data) { |
|
toPack(data).then(pack => draw(pack)); |
|
} |
|
} |
|
|
|
function requestMedia() { |
|
var t0, t1; |
|
t0 = performance.now(); |
|
return queryMediaCount() |
|
.catch(err => { |
|
console.log('requestMedia delayed by 1min'); |
|
return Promise.resolve() |
|
.then(DelayPromise(60000)) |
|
.then(() => queryMediaCount()); |
|
}).catch(err => { |
|
console.log('Media error:', err); |
|
}).then(response => { |
|
t1 = performance.now(); |
|
console.log(`requestMedia [${t1-t0}ms]`); |
|
return parseMedia(response); |
|
}).then(data => save(data, 'media')); |
|
} |
|
|
|
function requestAnnots() { |
|
// count annot by media type |
|
var param = {group:['media_type']}; |
|
return queryAnnotCount(param) |
|
.then(response => parseByType(response)) |
|
.then(data => { |
|
save(data, 'bytype'); |
|
return byUsers(data); |
|
}); |
|
} |
|
|
|
function byUsers(data) { |
|
// create root |
|
var root = { |
|
name: 'root', |
|
value: 0, |
|
children: [], |
|
}; |
|
// query byusers |
|
var param = { |
|
group: ['reviewer'], |
|
options:{size: p.catmax} |
|
}; |
|
var q = data.children.map(m => { |
|
param.filter = {media_type: m.name}; |
|
return requestUsers(param, m, root); |
|
}); |
|
return Promise.all(q) |
|
.then(list => { |
|
var q = []; |
|
q.push(save(list[0], 'byuser')); |
|
q.push(save(data, 'bytype')); |
|
return Promise.all(q); |
|
}); |
|
} |
|
|
|
function requestUsers(p, n, root) { |
|
return queryAnnotCount(p) |
|
.then(response => parseByUser(response, n, root)); |
|
} |
|
|
|
function DelayPromise(delay) { |
|
return function(data) { |
|
return new Promise(function(resolve, reject) { |
|
setTimeout(() => resolve(data), delay); |
|
}); |
|
}; |
|
} |
|
|
|
function parseMedia(data) { |
|
return new Promise(function(resolve) { |
|
// create root |
|
var root = { |
|
name: 'root', |
|
value: data.result.count.total, |
|
children: [], |
|
}; |
|
// parse response |
|
var list = data.result.count.media_type; |
|
Object.keys(list).forEach(k => { |
|
root.children.push({name: k, value: list[k]}); |
|
}); |
|
resolve(root); |
|
}).catch(err => { |
|
console.log('Error Parse Media', err); |
|
}); |
|
} |
|
|
|
function parseByType(data) { |
|
return new Promise(function(resolve) { |
|
// create root |
|
var res = { |
|
name: 'root', |
|
value: 0, |
|
children: [] |
|
} |
|
// parse response |
|
res.children = data.result.map(m => { |
|
// add count to root |
|
res.value += m.count; |
|
// add type child |
|
return {name: m.group.media_type, value: m.count}; |
|
}); |
|
|
|
resolve(res); |
|
}); |
|
} |
|
|
|
function parseByUser(data, node, root) { |
|
return new Promise(function(resolve) { |
|
var u, uidx; |
|
var sum = 0; |
|
node.children = data.result.map(m => { |
|
u = m.group.reviewer; |
|
// add user |
|
if(uMap[u] === undefined) { |
|
uMap[u] = root.children.length; |
|
root.children.push({name: u, value: 0, children: []}); |
|
} |
|
uidx = uMap[u]; |
|
// add user/type |
|
root.children[uidx].children.push({name: node.name, value: m.count}); |
|
root.children[uidx].value += m.count; |
|
root.value += m.count; |
|
// add type/user child |
|
sum += m.count; |
|
return {name: m.group.reviewer, value: m.count} |
|
}); |
|
// check others |
|
if (sum < node.value) { |
|
var val = node.value - sum; |
|
// add type/others |
|
node.children.push({name: 'other', value: val}); |
|
// add others/type |
|
root.children[0].children.push({name: node.name, value: val}); |
|
root.children[0].value += val; |
|
root.value += val; |
|
} |
|
resolve(root); |
|
}).catch(err => { |
|
console.log('parseByUser error:', err); |
|
}) |
|
} |
|
|
|
function save(data, mode) { |
|
if(mode === 'media') { |
|
state.media = data; |
|
state.enabled.modemedia = true; |
|
if (state.mode === 'none') { |
|
d3.select('#modemedia').property('checked', true); |
|
d3.select('#grouptype').property('checked', true); |
|
} |
|
} else if(mode ==='bytype') { |
|
state.bytype = data; |
|
state.enabled.modeannot = true; |
|
if (state.mode === 'none') { |
|
d3.select('#modeannot').property('checked', true); |
|
d3.select('#grouptype').property('checked', true); |
|
} |
|
} else { |
|
state.byuser = data; |
|
} |
|
check(); |
|
} |
|
|
|
// Manage layout |
|
function toPack(data) { |
|
return new Promise(function(resolve) { |
|
console.log('PACK', data); |
|
// circle |
|
var diam = p.diameter - (2 * p.margin); |
|
// create pack layout |
|
var layout = d3.pack() |
|
.size([diam, diam]) |
|
.padding(5); |
|
// compute layout |
|
var pack = layout( |
|
d3.hierarchy(data) |
|
.sum(function(d) { return 10*Math.log10(d.value+1); }) // decibel - +1 !0 |
|
).descendants(); |
|
|
|
resolve(pack); |
|
}); |
|
} |
|
|
|
function draw(data) { |
|
console.log('DRAW', state.mode); |
|
if (data[0]) { |
|
// headers |
|
d3.select('#count') |
|
.text(data[0].data.value.toLocaleString()); |
|
|
|
// transitions |
|
const delay = 500; |
|
const t1 = d3.transition().duration(delay); |
|
const t2 = d3.transition().delay(delay).duration(delay); |
|
const t3 = d3.transition().delay(delay * 2).duration(delay); |
|
var sel, add; |
|
|
|
var svg = d3.select('#chart').selectAll('svg') |
|
.data([0]); |
|
svg.enter() |
|
.append('svg') |
|
.attr('height', p.diameter) |
|
.attr('width', p.diameter) |
|
.append('g') |
|
.style('font-weight', 'bold') |
|
.attr('transform', `translate(${p.margin},${p.margin})`); |
|
// nodes group |
|
sel = d3.select('#chart').select('svg') |
|
.select('g').selectAll('.node') |
|
.data(data, d => d.parent ? d.parent.data.name+d.data.name : d.data.name); |
|
// exit |
|
sel.exit().transition(t1) |
|
.remove(); |
|
// update |
|
sel.transition(t2) |
|
.attr('transform', d => `translate(${d.x},${d.y})`); |
|
// add .node + content |
|
add = sel.enter().append('g').attr('class', 'node') |
|
.attr('fill', d => getColor('fg', d.data.name)) |
|
.attr('transform', `translate(${p.diameter / 2},${p.diameter / 2})`); |
|
add.append('circle') |
|
.attr('r', 0) |
|
.attr('stroke-width', '3px') |
|
.attr('stroke', d => getColor('fg', d.data.name)) |
|
.attr('fill', d => getColor('bg', d.data.name)) |
|
.on('mouseover', function(d){ tip("show",d); }) |
|
.on('mouseout', function(d){ tip("hide",d); }) |
|
.on("mousemove", function(d) { tip("move"); }) |
|
add.append('text') |
|
.attr('class', 'type') |
|
.attr('dy', '0.5ex') |
|
.attr('y', '-2ex') |
|
.attr('text-anchor', 'middle') |
|
.attr('opacity', 0) |
|
.style('pointer-events', 'none') |
|
.text(d => getLabel(d)); |
|
add.append('text') |
|
.attr('class', 'mode') |
|
.attr('dy', '0.5ex') |
|
.attr('text-anchor', 'middle') |
|
.attr('opacity', 0) |
|
.style('pointer-events', 'none') |
|
add.append('text') |
|
.attr('class', 'count') |
|
.attr('dy', '0.5ex') |
|
.attr('text-anchor', 'middle') |
|
.attr('opacity', 0) |
|
.style('pointer-events', 'none') |
|
// update |
|
sel = add.merge(sel); |
|
sel.transition(t3) |
|
.attr('transform', d => `translate(${d.x},${d.y})`); |
|
|
|
// circles |
|
sel = d3.select('#chart').select('svg') |
|
.selectAll('circle') |
|
.data(data, d => d.parent ? d.parent.data.name+d.data.name : d.data.name); |
|
// exit |
|
sel.exit().transition(t1) |
|
.attr('r', 0) |
|
.remove(); |
|
// update |
|
sel.transition(t3) |
|
.attr('r', d => d.r); |
|
|
|
// types |
|
sel = d3.select('#chart').select('svg') |
|
.selectAll('.type') |
|
.data(data, d => d.parent ? d.parent.data.name+d.data.name : d.data.name); |
|
// exit |
|
sel.exit().transition(t1) |
|
.attr('opacity', 0) |
|
.remove(); |
|
// update |
|
sel.transition(t3) |
|
.attr('opacity', d => { |
|
if(!d.children && d.r > p.rmin) { |
|
return 1; |
|
} else { |
|
return 0; |
|
} |
|
}); |
|
// mode |
|
sel = d3.select('#chart').select('svg') |
|
.selectAll('.mode') |
|
.data(data, d => d.parent ? d.parent.data.name + d.data.name : d.data.name); |
|
// exit |
|
sel.exit().transition(t1) |
|
.attr('opacity', 0) |
|
.remove(); |
|
// update |
|
sel.transition(t3) |
|
.attr('opacity', d => { |
|
if(!d.children && d.r > p.rmin) { |
|
return 1; |
|
} else { |
|
return 0; |
|
} |
|
}) |
|
.text(p.labels[state.mode].label); |
|
|
|
// count |
|
sel = d3.select('#chart').select('svg') |
|
.selectAll('.count') |
|
.data(data, d => d.parent ? d.parent.data.name+d.data.name : d.data.name); |
|
// exit |
|
sel.exit().transition(t1) |
|
.attr('opacity', 0) |
|
.remove(); |
|
// update |
|
sel.transition(t3) |
|
.attr('y', d => d.r > p.rmin ? '2ex' : '0ex') |
|
.attr('opacity', d => d.children ? 0 : 1) |
|
.text(d => d.data.value.toLocaleString()); |
|
} |
|
} |
|
|
|
function getColor(g, name) { |
|
var col; |
|
if(p.labels[name]) { |
|
col = p.labels[name][g]; |
|
} else { |
|
col = p[g][p.color(name)]; |
|
} |
|
return col; |
|
} |
|
|
|
function getTip(d) { |
|
var str=''; |
|
// switch user + type |
|
if(!d.parent) { // Root |
|
str += '<b>iCLiKVAL Content</b>' |
|
} else if(state.sub === 'type' && !d.children) { |
|
str += '<label>Reviewer: </label>'; |
|
str += getLabel(d.parent); |
|
str += '<br/><label>Type: </label>'; |
|
str += getLabel(d); |
|
} else if(state.sub === 'user' && !d.children) { |
|
str += '<label>Reviewer: </label>'; |
|
str += getLabel(d); |
|
str += '<br/><label>Type: </label>'; |
|
str += getLabel(d.parent); |
|
} else if(state.group === 'type') { |
|
str += '<label>Type: </label>'; |
|
str += p.labels[d.data.name].label; |
|
} else { |
|
str += '<label>Reviewer: </label>'; |
|
str += getLabel(d); |
|
} |
|
// add mode + count |
|
str += `<br/><label>${p.labels[state.mode].label}: </label>`; |
|
str += d.data.value.toLocaleString(); |
|
return str; |
|
} |
|
|
|
function getLabel(d) { |
|
var str; |
|
if(p.labels[d.data.name]) { |
|
str = p.labels[d.data.name].label; |
|
} else { |
|
str = d.data.name; |
|
} |
|
return str; |
|
} |
|
|
|
function tip(mode,d) { |
|
if(mode === "show") { |
|
d3.select("#tip") |
|
.datum(d) |
|
.style("opacity",1) |
|
.html(d => getTip(d)); |
|
// width |
|
d3.select('#tip').selectAll('label').style('width', `${p.tipWidth}px`); |
|
} |
|
else if(mode === "hide") { |
|
d3.select("#tip").style("opacity",0) |
|
} |
|
else { // move |
|
d3.select("#tip").style("top", (d3.event.pageY+10)+"px") |
|
.style("left", (d3.event.pageX+10)+"px") |
|
} |
|
} |
|
|
|
function queryAnnotCount(p) { |
|
return new Promise(function(resolve, reject) { |
|
d3.request('https://api.iclikval.riken.jp/annotation-count') |
|
.header("Content-Type", "application/json") |
|
.response(xhr => JSON.parse(xhr.responseText)) |
|
.post(JSON.stringify(p),(err, res) => { |
|
if (err) { |
|
console.log(`error - queryAnnotCount: ${err}`); |
|
reject(err); |
|
} else { |
|
resolve(res); |
|
} |
|
}); |
|
}); |
|
}; |
|
|
|
function queryMediaCount() { |
|
return new Promise(function(resolve, reject) { |
|
d3.json('https://api.iclikval.riken.jp/media-count', (err, json) => { |
|
if (err) { |
|
console.log(`error - queryMediaCount: ${err}`); |
|
reject(err); |
|
} else { |
|
resolve(json); |
|
} |
|
}); |
|
}); |
|
}; |
|
|
|
</script> |
|
</body> |