DiaColloDB-WWW
view release on metacpan or search on metacpan
share/htdocs/diacollo.js view on Meta::CPAN
else {
//-- motion chart: profile
cdata.addColumn('number', 'f2');
cdata.addColumn('number', 'f12');
cdata.addColumn('number', 'score');
data.profiles.forEach(function(p) {
var year = Number(p.label);
var scoref = p.score;
for (var key in p[scoref]) {
var item = key.replace(/\t/g,'/');
cdata.addRow([item, year, p.f2[key], p.f12[key], p[scoref][key]]);
}
});
cstate = '{"showTrails":false,"xLambda":0,"yLambda":0}';
}
//-- plot the chart
var chart = new google.visualization.MotionChart(document.getElementById('profileDataChart'));
chart.draw(cdata, {width:600, height:480, state:cstate});
}
//----------------------------------------------------------------------
var hitem2key = {};
function dcpFormatHiChart(data, jqXHR) {
//-- parse data
//data = $.parseJSON(data);
//qinfo = data.qinfo;
if ( !(data = dcpParseFlat(data,{mode:"bubble"})) ) { return; }
if (data.profiles.length == 0) {
dcpErrorMsg("Error: no data to display!");
return;
}
dcpStatusMsg("loading","Rendering...");
//-- hichart: enable "download" icon
$("#d3icons > a").hide();
$("#profileDataD3, #d3icons, #exportBtn").fadeIn();
//-- setup plot data
var cdata = { //-- chart data
chart: {
type: (user_query.debug ? 'line' : 'spline'),
zoomType: 'x'
},
credits: {
enabled: false
},
title: {
text:"DiaCollo Profile"+(isDiff ? " Diff" : "")
},
subtitle: {
text: (isDiff ? (chartTitleString('',1)+' - '+chartTitleString('b',1)) : chartTitleString())
},
xAxis: {
title: { text: 'Date (slice)' },
},
yAxis: {
title: { text: 'Score'+(isDiff ? (' Diff ('+user_query.diff+')') : '')+' ('+scoreNames[user_query.score]+')' }
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
borderWidth: 0,
padding: 5
//,itemStyle: { "color": "#333333", "cursor": "pointer", "fontSize": "12px", "fontWeight": "normal" }
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function (e) {
dcur = dlabels.indexOf(String(this.label).replace(/\./g,"-"));
var idata = items[ itemid[this.series.name.replace(/\//g,"\t")] ];
var dopts = {};
if (!$("#profileDataPopup").is(":visible")) {
dopts.position = {at:"center", of:e}
}
d3InfoPopup(idata, dopts);
}
}
},
marker: {
lineWidth: 1
}
}
},
series: []
};
//-- create hicharts series
var item, di, score;
items.forEach(function(item) {
item.hiseries = { name:item.label, data:[] };
for (var di in dlabels) {
score = item.score[di];
item.hiseries.data.push({x:Number(String(dlabels[di]).replace(/-/g,".")), y:(score==null ? null : score), label:dlabels[di]});
}
cdata.series.push(item.hiseries);
});
//-- setup plot area
$(".rawURL").hide();
$("#profileDataChart").addClass("hcParent").show();
//-- plot the chart
$("#profileDataChart").addClass("hcChart").highcharts(cdata).show();
dcpClearMsg();
}
//----------------------------------------------------------------------
// str = chartTitleString(prefix,parens)
function chartTitleString(prefix,parens) {
if (prefix==null) { prefix = ''; }
var q = user_query[prefix+'query'];
var title = q;
/*
var d = user_query[prefix+'date'];
share/htdocs/diacollo.js view on Meta::CPAN
if (d3.event.sourceEvent) { // not a programmatic event
value = speedBrush.y().invert(d3.mouse(this)[1]);
}
d3SpeedBrushMove(value);
}
function d3OnSpeedBrushEnd() {
//d3SpeedBrushDebug("d3OnSpeedBrushEnd()");
speedNode.classed("brushing",false);
}
//----------------------------------------------------------------------
// d3: play/pause transport: callbacks
function dcpPlay(playing,force) {
if (playing==null) playing = dcpPlaying;
if (force==null) force=false;
//exportMenuHide(0);
//-- setup buttons
var btn = d3.selectAll(".btn");
btn.selectAll(".play").style("opacity",Number(!playing));
btn.selectAll(".pause,.stop").style("opacity",Number(playing));
if (!force && playing==dcpPlaying) return;
dcpPlaying = playing;
//-- maybe start playing
if (playing) {
//-- play
if (dlabels.length < 2) {
alert("Play animation only available for multi-slice profiles!");
return dcpPlay(false);
}
var pos0 = dbrush.extent()[0];
var pos1 = dlabels.length-1;
if (pos0 == pos1) { pos0 = 0; }
var totaldur = 15000 / dcpSpeed; //-- play-length for total sequence (in ms; google motion chart ~15s; [2s=.133x .. 40s=2.6x])
var interp = d3.interpolate(pos0,pos1);
d3.select("#d3slider")
.transition()
.duration((totaldur/(dlabels.length-1))*(pos1-pos0))
.ease("linear")
.tween("brush", function() {
//-- hack: check dcpPlaying to avoid transition interference (keybd interrupting play)
return function(t) { if (dcpPlaying) dcpBrushMove(interp(t)); }
})
.each("end",function() { dcpPlay(false); });
}
else {
//-- stop
d3.select("#d3slider")
.transition()
.duration(0)
.tween("brush", function() { return function(t) { ; } })
//.call(dbrush.event)
;
}
}
//----------------------------------------------------------------------
// d3: legend (color-scale / "y axis")
function dcpLegend(parent_selector,opts) {
//-- defaults
if (opts==null) opts={};
if (opts.id==null) { opts.id="d3legend"; }
if (opts.width==null) { opts.width = 50; }
if (opts.height==null) { opts.height = 450; }
if (opts.opacity==null) { opts.opacity = 1; }
var margin = opts.margin==null ? {} : opts.margin;
if (margin.left==null) margin.left = 0;
if (margin.right==null) margin.right = 0;
if (margin.top==null) margin.top = 0;
if (margin.bottom==null) margin.bottom = 0;
if (opts.translate==null) opts.translate = {x:0,y:0};
//debug_log("dcpLegend(parent_selector="+JSON.stringify(parent_selector)+", opts="+JSON.stringify(opts)+")");
//-- common variables
var width = opts.width - margin.left - margin.right;
var height = opts.height - margin.top - margin.bottom;
var yscale = d3.scale.linear()
.domain(dcpScoreRange.reverse())
.range([0,height])
.clamp(true);
var parent = d3.select(parent_selector);
var svg = parent.append("g")
.attr("id",opts.id)
.classed("d3legend",true)
.classed("scale",true)
.attr("width", opts.width)
.attr("height", opts.height)
.attr("transform","translate("+(margin.left+opts.translate.x)+","+(margin.top+opts.translate.y)+")");
svg.append("title")
.text("Legend: node colors by collocate score"+(isDiff ? " difference" : ""));
svg.append("g")
.attr("class", "y axis")
.call(d3.svg.axis()
.scale(yscale)
.orient("right")
.tickSize(12)
.tickFormat(d3.format(".2s"))
.tickPadding(5));
var cmin = dcpMinColor(); //-- ensure heatcolor_scale is defined (if applicable)
var rw = 14;
if (heatcolor_scale==null) {
//-- no d3 color-scale defined: add quantized visual scale via filled rectangles
var boxes = svg.selectAll(".y.axis")
.append("g")
.attr("class","boxes");
var rh = 5;
var nrect = ~~(height/rh);
for (var i=0; i < nrect; ++i) {
boxes.append("rect")
//.attr("id","ybox"+i)
.attr("height",rh)
.attr("width", rw)
.attr("x",0)
.attr("y",i*rh)
.style("fill", heatcolorf(1-i/nrect, dcpItemSat, dcpItemVal))
.style("opacity",opts.opacity)
;
}
}
else {
//-- d3 color-scale defined: add visual scale via linear-gradient fill
var cscale = heatcolor_scale;
var pfmt = d3.format("%");
var box = svg.selectAll(".y.axis")
.append("g")
.attr("class","gradbox");
box.append("linearGradient")
.attr("id", "colorGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", 0).attr("y1", 0)
.attr("x2", 0).attr("y2", height)
.selectAll("stop")
.data(cscale.domain().map(function(f,i) { return {offset:pfmt(1.0-f), color:cscale(f)}; }))
.enter().append("stop")
.attr("offset", function(d) { return d.offset; })
.attr("stop-color", function(d) { return d.color; });
box.append("rect")
.attr("height",height)
.attr("width", rw)
.attr("x",0)
.attr("y",0)
.style("fill", "url(#colorGradient)")
share/htdocs/diacollo.js view on Meta::CPAN
if(!source.match(/^<svg[^>]*"http\:\/\/www\.w3\.org\/1999\/xlink"/)){
source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
}
//-- add xml declaration
source = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\r\n' + source;
//-- convert svg source to URI data scheme
//var url = "data:image/svg+xml;charset=utf-8,"+encodeURIComponent(source);
var url = "data:image/svg+xml;charset=utf-8,"+encodeURIComponent(source);
//-- set url value to the export button's element's href attribute
$("#exportBtn")
//.prop("target","_tab")
.prop("download","diacollo.svg")
.prop("href",url);
//-- go get it (must use DOM click() method, not jQuery if using exportTarget != event.target)
/*
exportMenuHide();
document.getElementById('exportTarget').click();
*/
//-- direct-click button: just return true
return true;
}
//--------------------------------------------------------------
// d3: export: utils: get css string from selected stylsheets
// + see http://stackoverflow.com/questions/1679507/getting-all-css-used-in-html-file
function getcss(wantIntern, hrefRegex, selectorRegex) {
var css = ""; //variable to hold all the css that we extract
//-- add internal styles
if (wantIntern) {
$("style").each(function(i,s) {
css += s.innerHTML;
});
}
//-- check for selected external stylesheets
if (hrefRegex==null) hrefRegex = /./;
if (selectorRegex==null) selectorRegex = /./;
for (var si = 0; si < document.styleSheets.length; si++) {
var sheet = document.styleSheets[si];
if (String(sheet.href).search(hrefRegex) == -1) continue;
//-- loop over all the styling rules in this external stylesheet
for (var ri = 0; ri < sheet.cssRules.length; ri++) {
if (sheet.cssRules[ri].selectorText.search(selectorRegex) != -1) {
css += sheet.cssRules[ri].cssText; //-- extract the styling rule
}
}
}
return css;
}
//----------------------------------------------------------------------
// d3: common: controls & geometry
// + opts:
// legendOpacity:OPACITY //-- legend color-scale opacity
// width:WIDTH, //-- total width (default=window.innerWidth-16)
// height:HEIGHT, //-- total height (default=500)
// bodyWidth:WIDTH //-- body,slider width
// buttonsWidth:WIDTH //-- buttons width
// legendWidth:WIDTH //-- legend width
// + returns: opts + keys
// bodyWidth:WIDTH //-- body width
// bodyHeight:HEIGHT //-- body height
function d3SetupCommon(opts) {
//-- options
if (opts.legendOpacity==null) opts.legendOpacity=1;
//-- common variables
if (opts==null) opts={};
if (opts.height==null) opts.height = 500;
if (opts.buttonsWidth==null) opts.buttonsWidth = 90; //$("#d3buttons").width(); //-- not defined yet!
if (opts.legendWidth==null) opts.legendWidth = 50; //$("#d3legend").width(); //-- not defined yet!
if (opts.bodyHeight==null) opts.bodyHeight = opts.height - 50 - 5; //-- -5:ypad
//debug_log("d3SetupCommon:pre: "+JSON.stringify(opts));
//-- cleanup any stale content & create new svg with proper height (may add scrollbars!)
$("#d3content").remove();
var svg = d3.select("#profileDataD3")
.append("svg")
.attr("id","d3content")
//.attr("width",opts.width) //-- delayed in case scrollbars were added
.attr("height", opts.height);
//-- get width (temporary show)
$("#profileDataD3").show();
if (opts.width==null) opts.width = $(".outer").width();
if (opts.sliderWidth==null) opts.sliderWidth = opts.width - opts.buttonsWidth ;//- 20; //-- 10px goofiness margin (firefox, mobile emulation)
if (opts.bodyWidth==null) opts.bodyWidth = opts.width-opts.legendWidth ;//- 20; //-- 10px goofiness margin (firefox, mobile emulation)
$("#profileDataD3").hide();
svg.attr("width",opts.width);
//debug_log("d3SetupCommon+width: "+JSON.stringify(opts));
//-- transport: buttons + brush-slider
var transport = svg.append("g")
.attr("id","d3transport")
.attr("width",opts.width)
.attr("height",50);
var sliderPadX = 50;
dcpTransportButtons("#d3transport", {id:"d3buttons", width:opts.buttonsWidth, height:50});
dcpTransportSlider(dlabels, "#d3transport", { id:"d3slider", width:opts.sliderWidth, height:50,
margin:{top:0, bottom:0, left:sliderPadX, right:sliderPadX},
translate:{x:opts.buttonsWidth, y:0}
});
//-- legend + body
dcpLegend("#d3content", { id:"d3legend", width:opts.legendWidth, height:opts.bodyHeight, opacity:opts.legendOpacity,
margin:{top:10,bottom:10,left:0,right:0},
translate:{x:0,y:50},
});
var main = svg.append("g")
.attr("id","d3main")
.attr("transform", "translate("+(opts.legendWidth)+","+55+")")
;
var bg = main.append("rect")
.attr("id","d3background")
.attr("width",opts.bodyWidth)
.attr("height",opts.bodyHeight)
;
var body = main.append("g")
.attr("id","d3body")
.attr("width",opts.bodyWidth)
.attr("height",opts.bodyHeight)
;
var frame = main.append("rect")
.attr("id", "d3frame")
.attr("rx",$("#d3buttons .btn .border").attr("rx"))
.attr("ry",$("#d3buttons .btn .border").attr("ry"))
.attr("width",opts.bodyWidth-1)
.attr("height",opts.bodyHeight-1)
;
//-- key bindings
dcpBrushKeys({".content":1});
$("#d3icons").fadeIn();
//-- status
$("#statusRel").addClass("d3");
dcpStatusMsg("loading","Rendering...");
return opts;
}
//----------------------------------------------------------------------
// d3: bubble: force-drirected graph chart;
// + see https://githu //-- common variablesb.com/mbostock/d3/wiki/Force-Layout (force layout demo)
// + see https://gist.github.com/mbostock/3231298 (collision detection demo)
var bubbleOpacity = 0.9;
function dcpFormatBubble(data, jqXHR) {
//-- parse data
if ( !(data = dcpParseFlat(data,{mode:"bubble"})) ) { return; }
var cfg = d3SetupCommon({legendOpacity:bubbleOpacity});
var width = cfg.bodyWidth;
var height = cfg.bodyHeight;
//-- setup d3 force chart
var psvg = d3.select("#d3body")
.classed("d3chart",true)
.classed("d3force",true)
;
d3.select("#d3main")
.append("title")
.text("Bubble-graph for selected date-slice. Click+drag to move bubbles, double-click to display details.");
var force = dforce = d3.layout.force()
.links([])
.nodes([])
.charge(0)
.size([width, height])
.on("tick", function(e) {
//-- circle collision detection
var nodes = force.nodes();
var q = d3.geom.quadtree(nodes);
var i = -1;
var n = nodes.length;
while (++i < n) q.visit(collide(nodes[i], width, height));
psvg.selectAll(".node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
})
;
dcpForceSnap();
//dcpBrushMove(dcur);
//dcpErrorMsg("-- work in progress --");
dcpClearMsg();
$(".rawURL").hide();
$("#profileDataD3").fadeIn();
//dcpShowPrefetchHint(); //-- show "pre-fetched data" hint if appropriate
return;
}
//--------------------------------------------------------------
// d3: bubble: force layout update (snap)
var dcpSnapEase = "elastic"; //-- pretty, but "elastic" causes bogus negative radius values
//var dcpSnapEase = "cubic-out";
function dcpForceSnap(value) {
if (value==null) value=dcur;
dcpPlay(false);
dcpForceInterp(Math.round(value), 500, dcpSnapEase); //-- "elastic" causes bogus negative radius values
}
//--------------------------------------------------------------
// d3: bubble: force layout update (interp)
var d3BodySelector = '#d3body';
function dcpForceInterp(value,dur,easeby) {
if (dforce==null) return; //-- not defined yet
share/htdocs/diacollo.js view on Meta::CPAN
function itemTextRadius(item) {
if (Number(item.textRadius) > 0) return item.textRadius;
return item.textRadius = d3.select("#g"+item.id+" text").node().getBBox().width/2;
}
//--------------------------------------------------------------
// d3: bubble: collision detection
// + see https://gist.github.com/mbostock/3231298 (orig, circles only)
// + see http://stackoverflow.com/questions/29844823/force-layout-collision-detection-with-group-nodes (for group nodes)
// + using canvas-constraints from http://bl.ocks.org/mbostock/1129492
function collide(node, canvasWidth, canvasHeight) {
var nodeElt = d3.select("#g"+node.id).node();
var bbox = nodeElt.getBBox();
var pad = 2;
//-- fit to canvas hack from http://bl.ocks.org/mbostock/1129492
node.x = Math.max(node.r, Math.min(canvasWidth - bbox.width/2, node.x));
node.y = Math.max(node.r, Math.min(canvasHeight - bbox.height/2, node.y));
var
rx = bbox.width/2 + pad,
ry = bbox.height/2 + pad,
r = Math.max(rx,ry),
//r = ry,
nx1 = node.x - rx, //rx
nx2 = node.x + rx, //rx
ny1 = node.y - ry, //ry
ny2 = node.y + ry; //ry
return function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== node)) {
//-- collision detection: circles
var x = node.x - quad.point.x,
y = node.y - quad.point.y,
dl = Math.sqrt(x * x + y * y),
dr = r + Math.max(quad.point.r, itemTextRadius(quad.point));
//dr = r + quad.point.r;
if (dl < dr) {
dl = (dl - dr) / dl * .5;
node.x -= x *= dl;
node.y -= y *= dl;
quad.point.x += x;
quad.point.y += y;
}
}
//return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
};
}
//----------------------------------------------------------------------
// d3: cloud: tag-cloud layout
// + see https://github.com/jasondavies/d3-cloud
var dcpCloudFont = {family:"Impact",weight:"normal",style:"normal"};
//var dcpCloudFont = {family:"Arial",weight:"bold",style:"normal"};
//var dcpCloudFont = {family:"Arial Black",weight:"bold",style:"normal"};
//var dcpCloudFont = {family:"sans-serif",weight:"bold",style:"normal"};
function dcpFormatCloud(data, jqXHR) {
//-- parse data
if ( !(data = dcpParseFlat(data,{mode:"cloud"})) ) { return; }
var cfg = d3SetupCommon({legendOpacity:1});
var width = cfg.bodyWidth;
var height = cfg.bodyHeight;
//-- for old heat-colors
dcpItemSat = 1;
dcpItemVal = 0.9;
//-- setup cloud svg item
var psvg = d3.select("#d3body")
.classed("d3chart",true)
.classed("d3cloud",true)
;
d3.select("#d3main")
.append("title")
.text("Tag-cloud for selected date-slice. Click a word to display details.");
//-- (re-)initialize cloud layout
dcpCloudSetup(psvg);
}
//--------------------------------------------------------------
// d3: cloud: setup / rescale
//-- random seed (d3.cloud still isn't deterministic even with constant seed!)
var seed = Math.floor(Math.random() * 65535);
var xrandom = function() { var x = Math.sin(seed++) * 10000; return x - Math.floor(x); }
xrandom = Math.random;
var dcpCloudScale = 1;
var dcpCloudSizeMax = null;
function dcpCloudSetup(psvg) {
if (psvg==null) { psvg=d3.select(d3BodySelector); }
//xrandom = Math.random;
//seed = 42;
//debug_log("random seed = " + seed);
//-- size-scaling: re-populate item.size; item.maxSize
if (dcpCloudSizeMax==null) {
dcpCloudSizeMax = dcpSizeRange[1];
}
dcpSizeRange[1] = dcpCloudSizeMax * dcpCloudScale;
items.forEach(function(item) {
item.sizes = item.sizes.map(function(x,i) { return ~~dcpItemSize(item,i); });
item.maxSize = Math.round( d3.max(item.sizes) );
});
//-- rotations
var rotations
= [0];
//= [0,90];
//= [0,90,45,-45];
//= [0,45,-45];
//= [45,-45];
var main = d3.select("#d3main");
var cloud = dcloud = d3.layout.cloud()
.size([Number(psvg.attr("width")),Number(psvg.attr("height"))+25]) //-- weird unused space at bottom: tweaking height+25
.words(items)
.padding(1)
.random(xrandom)
( run in 2.075 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )