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 )