; d3.labeling = function() { var labeling = {}, className, labels, callbacks = [], callbackpos = -1, updateLabels = false, _break = false, legend = []; labeling.select = function(_className) { className = _className; labels = getLabels(); return labeling; } var getLabels = function() { if (typeof className === "string") { return svg.selectAll(className); } else { return className.call(this); } } labeling.remove = function() { callbacks.push(labeling_remove); updateLabels = true; return labeling; } labeling.scale = function(_scaleFactor) { var scaleFactor = _scaleFactor || .75; // currying: https://medium.com/@kbrainwave/currying-in-javascript-ce6da2d324fe var fn = function(label) { return labeling_scale(label, scaleFactor); } callbacks.push(fn); return labeling; } labeling.legend = function() { callbacks.push(labeling_legend); updateLabels = true; return labeling; } var labeling_remove = function(label) { label.remove(); } var labeling_scale = function(label,factor) { var x = +label.attr("x"), y = +label.attr("y"), scaleFactor = factor < 1 ? 1 - factor : - (factor - 1), transform = "translate(" + (x * scaleFactor) + "," + (y * scaleFactor) + ") scale(" + factor + ")"; label.attr("transform", transform); } var labeling_legend = function(label) { legend.push(label.text()); label.text(legend.length); } var next = function(label) { var box = getBox(label); var pos = label.attr("data-pos") || 0; pos++; if (pos === 8) { var calls = label.attr("data-calls") || -1; calls++; label.attr("data-calls", calls); if (calls < callbacks.length) { var fn = callbacks[calls]; fn.call(this, label); } pos = 0; box = getBox(label); // prevent infinite loop if (!callbacks.length) { _break = true; } } label.attr("data-pos", pos); var x = +label.attr("x"), y = +label.attr("y"); switch(pos) { case 0: case 1: y += box.height/2; break; case 2: case 3: x -= box.width/2; break; case 4: case 5: y -= box.height/2; break; case 6: case 7: x += box.width/2; break; } label.attr("x", x).attr("y", y); } var overlaps = function(a, b) { return ( (a.left <= b.left && b.left <= a.right) || (a.left <= b.right && b.right <= a.right) ) && ( (a.top <= b.top && b.top <= a.bottom) || (a.top <= b.bottom && b.bottom <= a.bottom) ); } var getBox = function(d) { return d[0][0].getBoundingClientRect(); } labeling.align = function() { var overlappeds; do { overlappeds = 0; labels.each(function() { var current = this; var box_text = getBox(d3.select(current)); var _overlappeds = labels[0].filter(function(d) { if (d === current) return false; return overlaps(box_text, getBox(d3.select(d))); }); overlappeds += _overlappeds.length; _overlappeds.forEach(function(el) { next(d3.select(el)); }); }); if (updateLabels) { labels = getLabels(className); } } while (overlappeds > 0 && !_break); return labeling; } labeling.getLegend = function() { return legend.map(function(d, i) { return { key: i + 1, name: d } }); } return labeling; }