DiaColloDB-WWW

 view release on metacpan or  search on metacpan

share/htdocs/diacollo.js  view on Meta::CPAN

			}
                    }
                },
                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'];
    if (d != null && d != '') {
	title += ' ['+d.replace(/:/,'-')+']';
    }
    var s = user_query[prefix+'slice'];
    if (s != null && s != '') {
	title += ' /'+s;
    }
    */
    return Boolean(parens) ? ('('+title+')') : title;
}


//----------------------------------------------------------------------
// d3: common variables & utilities

var dforce, dcloud, dcur, dsnapto, dkeys, dlabels, itemid, items;
var dcpScoreRange; //-- [minScore,maxScore]
var dcpValueNull;  //-- null value
var dcpSizeRange;  //-- [minSize,maxSize]
var dcpItemSize;   //-- interpolating accessor for item size (cloud:font-size, bubble:radius)
var d3InfoCur;     //-- currently selected info-popup data

var brushInterp;  //-- function variable for brush interpolation
var brushSnap;    //-- function variable for brush snap

// data = dcpParseFlat(dataStr,opts)
//  + parses profile data into flat d3-friendly format
//  + returns true on success; sets $("#status .msg").text() and returns false on error
//  + sets globals:
//     qinfo   = data.qinfo
//     dkeys   = [$date0,...]  (slice key-strings, raw,     e.g. "1900-1900" or "0-1750")
//     dlabels = [$date0,...]  (slice key-strings, trimmed, e.g. "1900"      or   "1750")
//     items   = [$itemData0,...]
//     itemid  = {$itemKey0:$itemId0, ...}
//     dcur    = $currentSliceIndex  //-- may be fractional if inbetween slices
//     dcpScoreRange = [min,max]
//     dcpSizeRange  = [min,max]
//     dcpItemSize = function(d,dcur) { ... }
//     dcpDateInterp = function(dcur) { ... }
//  + where items[itemId] =
//     item    = {id:$itemId, item:$itemKey, label:$itemLabel, score:[...], value:[...], avalue:[...], sizes:[...], opacity:[...], maxSize:maxSize?}
//  + array-valued item data keys are indexed by dlabels[] index
//  + calls setupDebugTable() after parsing qinfo()
//  + options:
//     mode: MODE,   //-- parse mode (known values: "bubble", "cloud")
var d3data = null;
function dcpParseFlat(data,opts) {
    //-- status message
    dcpStatusMsg("loading","Parsing...");

    //-- parse JSON data
    if (!(data instanceof Object)) { data = $.parseJSON(data); }
    d3data = data;
    qinfo = data.qinfo;
    setupDebugTable(qinfo);
    if (data.profiles.length == 0) {
	dcpErrorMsg("Error: no data to display!");
	return null;
    }

    //-- options
    if (opts==null)      { opts={}; }
    if (opts.mode==null) { opts.mode="bubble"; }

    var isBubble   = opts.mode == "bubble";
    var isCloud    = opts.mode == "cloud";

    //-- initialize
    dkeys   = [];
    dlabels = [];
    itemid  = {};
    items   = [];
    dcur    = 0; //-- current subprofile index

    //-- get data range
    data.profiles.forEach(function (p) { p.range = d3.extent(d3.values(p[p.score])); });
    var smin = d3.min(data.profiles, function(p) { return p.range[0]; });
    var smax = d3.max(data.profiles, function(p) { return p.range[1]; });
    if (smin > 0)    { smin = 0; }
    if (smax < smin) { smax = smin; }
    if (smin==smax)  { smin -= 1e-5; smax += 1e-5; }
    var amax      = Math.max(Math.abs(smin),Math.abs(smax));
    dcpScoreRange = [smin,smax];
    dcpValueNull  = dcpScoreValue(0);

    //-- setup scales: size/radius ("sizes" key)
    dcpSizeRange = (isBubble
		    ? [8,56]    //-- bubble: radius range
		    : [12,78]   //-- cloud: font-size range (in pixels)
		   );
    dcpItemSize = (isAbsDiff
		   ? function(item,pos) { return dcpSizeRange[0] + linterp(item.avalue,pos)*(dcpSizeRange[1]-dcpSizeRange[0]); }
		   : function(item,pos) { return dcpSizeRange[0] + linterp(item.value,pos) *(dcpSizeRange[1]-dcpSizeRange[0]); }
		  );

    //-- setup formatters (for details popup)
    var ffmt = d3.format(",r");
    var sfmt = d3.format(user_query.score=="ld" ? ".4r"
			 : (user_query.score=="f" ? ",r"
			    : ".4s"));

    //-- parse data
    var pi, p, pscores, iid, idata, iscore, ivalue, avalue;
    for (pi=0; pi < data.profiles.length; ++pi) {
	p = data.profiles[pi];
	p.label = String(p.label);
	dkeys.push(p.label);

	//-- simplify label
	p.label = p.label.replace(/^([0-9]+)-\1/,'$1').replace(/^0-/,'').replace(/-0$/,'');
	dlabels.push(p.label);
	pscores = p[p.score];

	for (var item in pscores) {
	    if ((iid=itemid[item]) != null) {
		//-- existing item
		idata = items[iid];
	    } else {
		//-- new item
		itemid[item] = iid = items.length;
		items.push(idata={"id":iid,
				  "item":item,
				  "label":item.replace(/\t/g, '/'),
				  "text":item.replace(/\t.*$/,''),
				  score:[], value:[], avalue:[], sizes:[], opacity:[]
				 });
		if (isDiff) {
		    idata.N1 = []; //ffmt(p.prf1.N);
		    idata.N2 = []; //ffmt(p.prf2.N);
		    idata.af1 = [];
		    idata.bf1 = [];
		    idata.af2 = [];
		    idata.bf2 = [];
		    idata.af12 = [];
		    idata.bf12 = [];
		    idata.ascore=[];
		    idata.bscore=[];
		} else {
		    idata.N   = []; //ffmt(p.N);
		    idata.f1  = [];
		    idata.f2  = [];
		    idata.f12 = [];
		}
	    }

	    idata.score[pi] = iscore = pscores[item];
	    idata.value[pi] = ivalue = dcpScoreValue(iscore);
	    idata.avalue[pi] = avalue = Math.abs(iscore)/amax;
	    idata.sizes[pi] = dcpItemSize(idata,pi);
	    idata.opacity[pi] = 1;

	    if (isDiff) {
		idata.N1[pi] = ffmt(p.prf1.N);
		idata.N2[pi] = ffmt(p.prf2.N);
		idata.af1[pi] = ffmt(p.prf1.f1);
		idata.bf1[pi] = ffmt(p.prf2.f1);

share/htdocs/diacollo.js  view on Meta::CPAN

	dcur = 0;
    }

    return data;
}

//----------------------------------------------------------------------
// d3: common: interpolating accessors
function vinterp(frac, x0,x1, missing) {
    if (missing==null) missing=0;
    return ((1.0-frac)*(x0==null ? missing : x0)) + (frac*(x1==null ? missing : x1));
}
function linterp(l,pos,missing) {
    return vinterp(pos-Math.floor(pos), l[Math.floor(pos)], l[Math.ceil(pos)], missing);
}
function dcpItemScore(item,pos) { return linterp(item.score, pos); }
function dcpItemValue(item,pos) { return linterp(item.value, pos, dcpValueNull); }
function dcpItemAbsValue(item,pos) { return linterp(item.avalue, pos); }
//dcpItemSize : function variable
function dcpItemScale(item,pos) { return dcpItemSize(item,pos) / item.maxSize; }

function dcpScoreValue(score) {
    return (score-dcpScoreRange[0]) / (dcpScoreRange[1]-dcpScoreRange[0]);
}

//-- dcpItemSat, dcpItemVal: for "old" rainbow-style colors
//   + green takes up too much space in these for some reason (~ 4 score points on for diff [-8..8])
//   + better differentiation using colorbrewer colors and d3 scale, not as pretty for html though
var dcpItemSat = 1;
var dcpItemVal = 1;
function dcpItemColor(item,pos) { return heatcolorf(dcpItemValue(item,pos), dcpItemSat, dcpItemVal); }
function dcpMinColor() { return heatcolorf(0, dcpItemSat, dcpItemVal); }
function dcpMaxColor() { return heatcolorf(1, dcpItemSat, dcpItemVal); }

function dcpItemOpacity(item,pos,max) {
    return (max==null ? 1 : max)*linterp(item.opacity,pos);
}

// interpolator = dcpDateInterpolator(dlabel0,dlabel1)
//  + returned function is called as "interpolatedDateLabel = interpolator(t)" with 0 <= t <= 1
function dcpDateInterpolator(dlabel0,dlabel1) {
    var d0 = dlabel0.split("-");
    var d1 = dlabel1.split("-");
    if (d0.length==1) {
	return d3.interpolateRound(Number(d0[0]),Number(d1[0]));
    } else {
	var interp = d0.map(function(e,i){ return d3.interpolateRound(Number(d0[i]),Number(d1[i])); });
	return function(t) { return interp.map(function(i) { return i(t) }).join("-"); };
    }
}

//--------------------------------------------------------------
// d3: common: node titles (bubble,cloud)
function d3NodeTitleText(d) {
    return (d.label + " ~ " + dcpItemScore(d,dcur)
	    + (user_query.debug ? (": " + JSON.stringify({id:d.id, value:dcpItemValue(d,dcur), avalue:dcpItemAbsValue(d,dcur), size:dcpItemSize(d,dcur)})) : '')
	   );
}

//--------------------------------------------------------------
// d3: info popup
function d3InfoPopup(d,opts) {
    var dsnap  = Math.round(dcur);
    var dlabel = dlabels[dsnap];
    

    //-- save current info item
    d3InfoCur = {data:d};

    var dopts = {
	title: d.label,
	autoOpen: true,
	modal: false,
	minHeight: 64,
	minWidth: 300,
	height: "auto",
	width: "auto",
	show: {effect:"scale",percent:100, duration:150},
	hide: {effect:"scale",percent:0, duration:150},
	close: function(e,ui) {
	    d3InfoCur = null;
	    d3.selectAll(".node").classed("selected",false);
	    $(".content").focus();
	}
    };
    if (!$("#profileDataPopup").is(":visible")) {
	dopts.position = {at:"center",of:this};
    }
    for (var o in opts) {
	if (o == null) {
	    delete dopts[o];
	} else {
	    dopts[o] = opts[o];
	}
    }

    var content = (''
		   +'<span class="ui-helper-hidden-accessible"><input type="text"/></span>' //-- disable ugly jquery-ui autofocus
		   +'<table class="dcslide">'
		   +'<tr><th>slice:</th><td class="slice">' + dlabel +'</td></tr>'
		   + '<th>score:</th><td class="score">' + d.score[dsnap] + '</td></tr>'
		  );
    var tr      = [dkeys[dsnap]].concat(d.item.split("\t"));
    if (isDiff) {
	d3InfoCur.kwic = {"tr":tr,"ilabel":0,
			   "qtemplate":qinfo.aqtemplate,"text":"KWIC:A","dtrim":/[^0-9].*$/,"dslice":user_query.slice,
			   title:"DDC KWIC search for point pairs (QUERY)",
			   classes:"textButtonSmall kwic"
			  };
	d3InfoCur.bkwic = {"tr":tr,"ilabel":0,
			   "qtemplate":qinfo.bqtemplate,"text":"KWIC:B","dtrim":/^.*[^0-9]/,"dslice":user_query.bslice,
			   title:"DDC KWIC search for point pairs (~QUERY)",
			   classes:"textButtonSmall bkwic"
			  };
	content += (''
 		    +'<tr><th>search:</th><td>'
		    + kwiclink(d3InfoCur.kwic)
		    + '&#xa0;'
		    + kwiclink(d3InfoCur.bkwic)
		    + '</td></tr>'
		    +'<tr><th>details:</th><td class="diff details">'+(
			'<table>'
			    +'<tr><th>N(a/b):</th><td class="num N1">'+d.N1[dsnap]+'</td><td>/</td><td class="num N2">'+d.N2[dsnap]+'</tr>'
			    +'<tr><th>f1(a/b):</th><td class="num af1">'+d.af1[dsnap]+'</td><td>/</td><td class="num bf1">'+d.bf1[dsnap]+'</tr>'
			    +'<tr><th>f2(a/b):</th><td class="num af2">'+d.af2[dsnap]+'</td><td>/</td><td class="num bf2">'+d.bf2[dsnap]+'</tr>'
			    +'<tr><th>f12(a/b):</th><td class="num af12">'+d.af12[dsnap]+'</td><td>/</td><td class="num bf12">'+d.bf12[dsnap]+'</tr>'
			    +'<tr><th>score(a/b):</th><td class="num ascore">'+d.ascore[dsnap]+'</td><td>/</td><td class="num bscore">'+d.bscore[dsnap]+'</tr>'
		    	    +'</table>'
		    )
		   );
    } else {
	d3InfoCur.kwic = {"tr":tr,"ilabel":0,"qtemplate":qinfo.qtemplate,"text":"KWIC",
			  title:"DDC KWIC search for point pairs",
			  classes:"textButtonSmall kwic"
			 };
	content += (''
		    +'<tr><th>search:</th><td>'
		    + kwiclink(d3InfoCur.kwic)
		    + '</td></tr>'
		    +'<tr><th>details:</th><td class="details">'+(
			'<table>'
			    +'<tr><th>N:</th><td class="num N">'+d.N[dsnap]+'</td></tr>'
			    +'<tr><th>f1:</th><td class="num f1">'+d.f1[dsnap]+'</td></tr>'
			    +'<tr><th>f2:</th><td class="num f2">'+d.f2[dsnap]+'</td></tr>'
			    +'<tr><th>f12:</th><td class="num f12">'+d.f12[dsnap]+'</td></tr>'
		    	    +'</table>'
		    )
		   );
    }
    var dlg = $("#profileDataPopup").html(content).dialog(dopts);

    //-- add node-class
    d3.selectAll(".node").classed("selected",false);
    d3.select("#g"+d.id).classed("selected",true);

    return false;
}

//--------------------------------------------------------------
// d3: info popup: update
function d3InfoPopupUpdate(snapto) {
    //-- maybe update info box
    if (d3InfoCur != null && snapto != dsnapto) {
	var dlg   = $("#profileDataPopup");
	var d     = d3InfoCur.data;
	dlg.find(".slice").text(String(dlabels[snapto]));
	dlg.find(".score").text(String(d.score[snapto]));
	if (d3InfoCur.kwic != null)  { d3InfoCur.kwic.tr[0]  = dkeys[snapto]; dlg.find(".kwic").prop('href',kwicurl(d3InfoCur.kwic)); }
	if (d3InfoCur.bkwic != null) { d3InfoCur.bkwic.tr[0] = dkeys[snapto]; dlg.find(".bkwic").prop('href',kwicurl(d3InfoCur.bkwic)); }
	if (isDiff) {
	    dlg.find(".N1").text(String(d.N1[snapto]));
	    dlg.find(".N2").text(String(d.N2[snapto]));
	    dlg.find(".af1").text(String(d.af1[snapto]));
	    dlg.find(".bf1").text(String(d.bf1[snapto]));
	    dlg.find(".af2").text(String(d.af2[snapto]));
	    dlg.find(".bf2").text(String(d.bf2[snapto]));
	    dlg.find(".af12").text(String(d.af12[snapto]));
	    dlg.find(".bf12").text(String(d.bf12[snapto]));
	    dlg.find(".ascore").text(String(d.ascore[snapto]));
	    dlg.find(".bscore").text(String(d.bscore[snapto]));
	} else {
	    dlg.find(".N").text(String(d.N[snapto]));
	    dlg.find(".f1").text(String(d.f1[snapto]));
	    dlg.find(".f2").text(String(d.f2[snapto]));
	    dlg.find(".f12").text(String(d.f12[snapto]));
	}
	//dlg.parent().stop(true,true).effect("highlight");
	d3.select("#g"+d.id).classed("selected",true);
    }
}

//--------------------------------------------------------------
// d3: brush-slider (date-slice selector)
// + see http://bl.ocks.org/mbostock/6452972
//    dbrush = d3brush(dlabels, selector, opts)
// + sets global dbrush=brush, bhandle=handle, bsvg=brush-svg
var dbrush, bhandle, bsvg;
function dcpTransportSlider(dlabels, parent_selector, opts) {

    //-- setup defaults
    if (opts.id==null) opts.id = "d3slider";
    if (opts.width==null) opts.width = 800;
    if (opts.height==null) opts.height = 50;
    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("dcpTransportSlider(dlabels="+JSON.stringify(dlabels)+", 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 xscale = d3.scale.linear()  //d3.v4: d3.scaleLinear()
	.domain([0,dlabels.length-1])
	.range([0,width])
	.clamp(true);

share/htdocs/diacollo.js  view on Meta::CPAN

    var snapto = Math.round(dcur);
    var dcur0 = Math.floor(dcur);
    var dcur1 = Math.ceil(dcur);
    var dfrac = dcur-dcur0;
    var ditems = items.filter(function(i) { return i.value[dcur0] != null || i.value[dcur1] != null; });
    ditems.forEach(function(i) { i.gravity = linterp(i.value,dcur); });
    dforce.nodes(ditems);

    var psvg = d3.select(d3BodySelector);
    var nodes = psvg.selectAll(".node")
	.data(ditems, function(d) { return d.id; });

    //-- UPDATE: update existing elements
    //var updated = nodes;

    //-- ENTER: append new elements
    var entered = nodes.enter()
	.append("g")
	.attr("class", "node")
	.attr("id", function(d) { return "g"+d.id; })
	.attr("opacity",0)
	.on("dblclick", d3InfoPopup)
	.call(dforce.drag);
    entered
	.append("title");
    entered
	.append("circle")
	.attr("opacity",bubbleOpacity)
	.attr("r",dcpSizeRange[0]);
    entered
	//.filter(function(d) { return d.avalue >= 0.25 }) //-- only annotate nodes with 75%+ absolute score-quantiles
	.append("text")
	.text(function(d) { return d.text; });

    //-- ENTER+UPDATE: update old or new nodes
    nodes.selectAll("title").text(d3NodeTitleText);
    var updated = nodes.transition()
	.duration(dur)
	.ease(easeby);
    updated
	.attr("opacity",function(d) { return dcpItemOpacity(d,dcur); })
	.selectAll("circle")
	.attr("r", function(d) { return d.r = dcpItemSize(d,dcur); })
	.style("fill", function(d) { return dcpItemColor(d,dcur); })
    ;

    //-- EXIT: remove old elements as needed
    var exited = nodes.exit()
	.transition()
	.duration(dur)
	.ease(easeby)
	.remove();
    exited
	.attr("opacity",0)
	.selectAll("circle")
	.attr("r",dcpSizeRange[0]);

    //-- (re-)start the force layout
    dforce.start();

    //-- update info popup and current snap-to value
    d3InfoPopupUpdate(snapto);
    dsnapto = snapto;
}


//--------------------------------------------------------------
// d3: bubble: collision detection: text "radius" (really horizontal only)
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"};

share/htdocs/diacollo.js  view on Meta::CPAN

	};
    };

    var nodes = d3.select(d3BodySelector+" > g")
	.selectAll(".node")
	.data(ditems, function(d) { return d.id; });

    //-- UPDATE: update existing elements
    //var updated = nodes;

    //-- ENTER: append new elements
    var entered = nodes.enter()
	.append("g")
	.attr("id", function(d) { return "g"+d.id; })
	.attr("class","node")
	.attr("transform", function(d) { return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; })
	.on("click", d3InfoPopup);
    
    //-- enter: title
    entered.append("title");

    //-- enter: text
    entered.append("text")
    //-- enter: text: constant attributes
	.text(function(d) { return d.text; })
        //-- these get set by css [but we might need them for svg export?]
        .attr("text-anchor","middle")
	.style("font-family",dcpCloudFont.family)
	.style("font-weight",dcpCloudFont.weight)
	.style("font-style",dcpCloudFont.style)
	.style("fill",dcpMinColor())
	.attr("opacity",0)
	.style("font-size",function(d) { (d.curSize=dcpSizeRange[0])+"px"; }) //-- use dcpSizeRange[0] rather than 0 minimum size; 0 --> collisions!
    ;

    //-- ENTER+UPDATE: update old or new nodes
    nodes.selectAll("title").text(d3NodeTitleText);
    nodes.transition()
	.duration(dur)
	.ease(easeby)
	.selectAll("text")
	.attr("opacity", function(d) { return dcpItemOpacity(d,dcur) })
	.style("fill", function(d) { return dcpItemColor(d,dcur) })
	//.style("font-size", function(d) { return ~~dcpItemSize(d,dcur)+"px"; })
	.tween("font-size",tween_font_size)
    ;


    //-- EXIT: remove old elements as needed
    var exited = nodes.exit()
	.transition()
	.duration(dur)
	.ease(easeby)
	.remove();
    exited.selectAll("text")
	.attr("opacity", 0)
	.style("fill", dcpMinColor())
	.tween("font-size", tween_font_size)
    ;

    //-- maybe update info popup
    d3InfoPopupUpdate(snapto);
    dsnapto = snapto;
}

//--------------------------------------------------------------
// d3: cloud: debug (show all at max size)
function dcpCloudDebug(dur,easeby,opacity,doInterp) {
    if (dcloud==null) return; //-- not defined yet
    if (dur==null) { dur = 1000; }
    if (easeby==null) { easeby = "linear"; }
    if (opacity==null) { opacity = 1; }
    if (doInterp==null) { doInterp = false; }
    debug_log("dcpCloudDebug("+dur+","+","+easeby+","+opacity+","+doInterp+")");

    var ditems = items;

    function tween_font_size(d) {
	var interp = d3.interpolateRound(d.curSize, d.maxSize);
	return function(t) {
	    $(this).css("font-size",(d.curSize=interp(t))+"px");
	};
    };

    var nodes = d3.select(d3BodySelector+" > g")
	.selectAll(".node")
	.data(ditems, function(d) { return d.id; });

    //-- UPDATE: update existing elements
    //var updated = nodes;

    //-- ENTER: append new elements
    var entered = nodes.enter()
	.append("g")
	.attr("id", function(d) { return "g"+d.id; })
	.attr("class","node")
	.attr("transform", function(d) { return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; })
	.on("click", d3InfoPopup);
    entered
	.append("title")
    ;
    
    //-- enter: text
    entered.append("text")
    //-- enter: text: constant attributes
	.text(function(d) { return d.text; })
        //-- these get set by css [but we might need them for svg export?]
        .attr("text-anchor","middle")
	.style("font-family",dcpCloudFont.family)
	.style("font-weight",dcpCloudFont.weight)
	.style("font-style",dcpCloudFont.style)
	.style("fill",dcpMinColor())
	.attr("opacity",0)
	.style("font-size",function(d) { (d.curSize=dcpSizeRange[0])+"px"; })
    ;

    //-- ENTER+UPDATE: update old or new nodes
    var ntrans=0;
    nodes.selectAll("text")
	.transition()
	.duration(dur)



( run in 1.188 second using v1.01-cache-2.11-cpan-364913b4093 )