clustergrammer
Version:
This is a clustergram implemented in D3.js. I started from the example http://bost.ocks.org/mike/miserables/ and added the following features
208 lines (190 loc) • 6.81 kB
JavaScript
module.exports = function save_svg_png(){
/* eslint-disable */
// (function() {
var out$ = typeof exports != 'undefined' && exports || this;
var doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
function isExternal(url) {
return url && url.lastIndexOf('http',0) == 0 && url.lastIndexOf(window.location.host) == -1;
}
function inlineImages(el, callback) {
var images = el.querySelectorAll('image');
var left = images.length;
if (left == 0) {
callback();
}
for (var i = 0; i < images.length; i++) {
(function(image) {
var href = image.getAttributeNS("http://www.w3.org/1999/xlink", "href");
if (href) {
if (isExternal(href.value)) {
console.warn("Cannot render embedded images linking to external hosts: "+href.value);
return;
}
}
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
href = href || image.getAttribute('href');
img.src = href;
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
image.setAttributeNS("http://www.w3.org/1999/xlink", "href", canvas.toDataURL('image/png'));
left--;
if (left == 0) {
callback();
}
}
img.onerror = function() {
console.log("Could not load "+href);
left--;
if (left == 0) {
callback();
}
}
})(images[i]);
}
}
function styles(el, selectorRemap) {
var css = "";
var sheets = document.styleSheets;
for (var i = 0; i < sheets.length; i++) {
try {
var rules = sheets[i].cssRules;
} catch (e) {
console.warn("Stylesheet could not be loaded: "+sheets[i].href);
continue;
}
if (rules != null) {
for (var j = 0; j < rules.length; j++) {
var rule = rules[j];
if (typeof(rule.style) != "undefined") {
var match = null;
try {
match = el.querySelector(rule.selectorText);
} catch(err) {
console.warn('Invalid CSS selector "' + rule.selectorText + '"', err);
}
if (match) {
var selector = selectorRemap ? selectorRemap(rule.selectorText) : rule.selectorText;
css += selector + " { " + rule.style.cssText + " }\n";
} else if(rule.cssText.match(/^@font-face/)) {
css += rule.cssText + '\n';
}
}
}
}
}
return css;
}
function getDimension(el, clone, dim) {
var v = (el.viewBox.baseVal && el.viewBox.baseVal[dim]) ||
(clone.getAttribute(dim) !== null && !clone.getAttribute(dim).match(/%$/) && parseInt(clone.getAttribute(dim))) ||
el.getBoundingClientRect()[dim] ||
parseInt(clone.style[dim]) ||
parseInt(window.getComputedStyle(el).getPropertyValue(dim));
return (typeof v === 'undefined' || v === null || isNaN(parseFloat(v))) ? 0 : v;
}
function reEncode(data) {
data = encodeURIComponent(data);
data = data.replace(/%([0-9A-F]{2})/g, function(match, p1) {
var c = String.fromCharCode('0x'+p1);
return c === '%' ? '%25' : c;
});
return decodeURIComponent(data);
}
out$.svgAsDataUri = function(el, options, cb) {
options = options || {};
options.scale = options.scale || 1;
var xmlns = "http://www.w3.org/2000/xmlns/";
inlineImages(el, function() {
var outer = document.createElement("div");
var clone = el.cloneNode(true);
var width, height;
if(el.tagName == 'svg') {
width = options.width || getDimension(el, clone, 'width');
height = options.height || getDimension(el, clone, 'height');
} else if(el.getBBox) {
var box = el.getBBox();
width = box.x + box.width;
height = box.y + box.height;
clone.setAttribute('transform', clone.getAttribute('transform').replace(/translate\(.*?\)/, ''));
var svg = document.createElementNS('http://www.w3.org/2000/svg','svg')
svg.appendChild(clone)
clone = svg;
} else {
console.error('Attempted to render non-SVG element', el);
return;
}
clone.setAttribute("version", "1.1");
clone.setAttributeNS(xmlns, "xmlns", "http://www.w3.org/2000/svg");
clone.setAttributeNS(xmlns, "xmlns:xlink", "http://www.w3.org/1999/xlink");
clone.setAttribute("width", width * options.scale);
clone.setAttribute("height", height * options.scale);
clone.setAttribute("viewBox", [
options.left || 0,
options.top || 0,
width,
height
].join(" "));
outer.appendChild(clone);
var css = styles(el, options.selectorRemap);
var s = document.createElement('style');
s.setAttribute('type', 'text/css');
s.innerHTML = "<![CDATA[\n" + css + "\n]]>";
var defs = document.createElement('defs');
defs.appendChild(s);
clone.insertBefore(defs, clone.firstChild);
var svg = doctype + outer.innerHTML;
var uri = 'data:image/svg+xml;base64,' + window.btoa(reEncode(svg));
if (cb) {
cb(uri);
}
});
}
out$.svgAsPngUri = function(el, options, cb) {
out$.svgAsDataUri(el, options, function(uri) {
var image = new Image();
image.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext('2d');
if(options && options.backgroundColor){
context.fillStyle = options.backgroundColor;
context.fillRect(0, 0, canvas.width, canvas.height);
}
context.drawImage(image, 0, 0);
var a = document.createElement('a'), png;
try {
png = canvas.toDataURL('image/png');
} catch (e) {
if (e instanceof SecurityError) {
console.error("Rendered SVG images cannot be downloaded in this browser.");
return;
} else {
throw e;
}
}
cb(png);
}
image.src = uri;
});
}
out$.saveSvgAsPng = function(el, name, options) {
options = options || {};
out$.svgAsPngUri(el, options, function(uri) {
var a = document.createElement('a');
a.download = name;
a.href = uri;
document.body.appendChild(a);
a.addEventListener("click", function(e) {
a.parentNode.removeChild(a);
});
a.click();
});
}
// })();
return out$.saveSvgAsPng;
};