g-chartframe
Version:
FT styled frames for D3 graphics
2,174 lines • 70.7 kB
JavaScript
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var saveSvgAsPng = createCommonjsModule(function (module, exports) {
(function () {
var out$ = exports || typeof undefined != 'undefined' || this || window;
out$.default = out$;
var xmlNs = 'http://www.w3.org/2000/xmlns/';
var xhtmlNs = 'http://www.w3.org/1999/xhtml';
var svgNs = 'http://www.w3.org/2000/svg';
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" [<!ENTITY nbsp " ">]>';
var urlRegex = /url\(["']?(.+?)["']?\)/;
var fontFormats = {
woff2: 'font/woff2',
woff: 'font/woff',
otf: 'application/x-font-opentype',
ttf: 'application/x-font-ttf',
eot: 'application/vnd.ms-fontobject',
sfnt: 'application/font-sfnt',
svg: 'image/svg+xml'
};
var isElement = function isElement(obj) {
return obj instanceof HTMLElement || obj instanceof SVGElement;
};
var requireDomNode = function requireDomNode(el) {
if (!isElement(el)) throw new Error('an HTMLElement or SVGElement is required; got ' + el);
};
var requireDomNodePromise = function requireDomNodePromise(el) {
return new Promise(function (resolve, reject) {
if (isElement(el)) resolve(el);else reject(new Error('an HTMLElement or SVGElement is required; got ' + el));
});
};
var isExternal = function isExternal(url) {
return url && url.lastIndexOf('http', 0) === 0 && url.lastIndexOf(window.location.host) === -1;
};
var getFontMimeTypeFromUrl = function getFontMimeTypeFromUrl(fontUrl) {
var formats = Object.keys(fontFormats).filter(function (extension) {
return fontUrl.indexOf('.' + extension) > 0;
}).map(function (extension) {
return fontFormats[extension];
});
if (formats) return formats[0];
console.error('Unknown font format for ' + fontUrl + '. Fonts may not be working correctly.');
return 'application/octet-stream';
};
var arrayBufferToBase64 = function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
for (var i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}return window.btoa(binary);
};
var getDimension = function getDimension(el, clone, dim) {
var v = el.viewBox && 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;
};
var getDimensions = function getDimensions(el, clone, width, height) {
if (el.tagName === 'svg') return {
width: width || getDimension(el, clone, 'width'),
height: height || getDimension(el, clone, 'height')
};else if (el.getBBox) {
var _el$getBBox = el.getBBox(),
x = _el$getBBox.x,
y = _el$getBBox.y,
_width = _el$getBBox.width,
_height = _el$getBBox.height;
return {
width: x + _width,
height: y + _height
};
}
};
var reEncode = function reEncode(data) {
return decodeURIComponent(encodeURIComponent(data).replace(/%([0-9A-F]{2})/g, function (match, p1) {
var c = String.fromCharCode('0x' + p1);
return c === '%' ? '%25' : c;
}));
};
var uriToBlob = function uriToBlob(uri) {
var byteString = window.atob(uri.split(',')[1]);
var mimeString = uri.split(',')[0].split(':')[1].split(';')[0];
var buffer = new ArrayBuffer(byteString.length);
var intArray = new Uint8Array(buffer);
for (var i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i);
}
return new Blob([buffer], { type: mimeString });
};
var query = function query(el, selector) {
if (!selector) return;
try {
return el.querySelector(selector) || el.parentNode && el.parentNode.querySelector(selector);
} catch (err) {
console.warn('Invalid CSS selector "' + selector + '"', err);
}
};
var detectCssFont = function detectCssFont(rule, href) {
// Match CSS font-face rules to external links.
// @font-face {
// src: local('Abel'), url(https://fonts.gstatic.com/s/abel/v6/UzN-iejR1VoXU2Oc-7LsbvesZW2xOQ-xsNqO47m55DA.woff2);
// }
var match = rule.cssText.match(urlRegex);
var url = match && match[1] || '';
if (!url || url.match(/^data:/) || url === 'about:blank') return;
var fullUrl = url.startsWith('../') ? href + '/../' + url : url.startsWith('./') ? href + '/.' + url : url;
return {
text: rule.cssText,
format: getFontMimeTypeFromUrl(fullUrl),
url: fullUrl
};
};
var inlineImages = function inlineImages(el) {
return Promise.all(Array.from(el.querySelectorAll('image')).map(function (image) {
var href = image.getAttributeNS('http://www.w3.org/1999/xlink', 'href') || image.getAttribute('href');
if (!href) return Promise.resolve(null);
if (isExternal(href)) {
href += (href.indexOf('?') === -1 ? '?' : '&') + 't=' + new Date().valueOf();
}
return new Promise(function (resolve, reject) {
var canvas = document.createElement('canvas');
var img = new Image();
img.crossOrigin = 'anonymous';
img.src = href;
img.onerror = function () {
return reject(new Error('Could not load ' + href));
};
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0);
image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', canvas.toDataURL('image/png'));
resolve(true);
};
});
}));
};
var cachedFonts = {};
var inlineFonts = function inlineFonts(fonts) {
return Promise.all(fonts.map(function (font) {
return new Promise(function (resolve, reject) {
if (cachedFonts[font.url]) return resolve(cachedFonts[font.url]);
var req = new XMLHttpRequest();
req.addEventListener('load', function () {
// TODO: it may also be worth it to wait until fonts are fully loaded before
// attempting to rasterize them. (e.g. use https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet)
var fontInBase64 = arrayBufferToBase64(req.response);
var fontUri = font.text.replace(urlRegex, 'url("data:' + font.format + ';base64,' + fontInBase64 + '")') + '\n';
cachedFonts[font.url] = fontUri;
resolve(fontUri);
});
req.addEventListener('error', function (e) {
console.warn('Failed to load font from: ' + font.url, e);
cachedFonts[font.url] = null;
resolve(null);
});
req.addEventListener('abort', function (e) {
console.warn('Aborted loading font from: ' + font.url, e);
resolve(null);
});
req.open('GET', font.url);
req.responseType = 'arraybuffer';
req.send();
});
})).then(function (fontCss) {
return fontCss.filter(function (x) {
return x;
}).join('');
});
};
var cachedRules = null;
var styleSheetRules = function styleSheetRules() {
if (cachedRules) return cachedRules;
return cachedRules = Array.from(document.styleSheets).map(function (sheet) {
try {
return { rules: sheet.cssRules, href: sheet.href };
} catch (e) {
console.warn('Stylesheet could not be loaded: ' + sheet.href, e);
return {};
}
});
};
var inlineCss = function inlineCss(el, options) {
var _ref = options || {},
selectorRemap = _ref.selectorRemap,
modifyStyle = _ref.modifyStyle,
modifyCss = _ref.modifyCss,
fonts = _ref.fonts,
excludeUnusedCss = _ref.excludeUnusedCss;
var generateCss = modifyCss || function (selector, properties) {
var sel = selectorRemap ? selectorRemap(selector) : selector;
var props = modifyStyle ? modifyStyle(properties) : properties;
return sel + '{' + props + '}\n';
};
var css = [];
var detectFonts = typeof fonts === 'undefined';
var fontList = fonts || [];
styleSheetRules().forEach(function (_ref2) {
var rules = _ref2.rules,
href = _ref2.href;
if (!rules) return;
Array.from(rules).forEach(function (rule) {
if (typeof rule.style != 'undefined') {
if (query(el, rule.selectorText)) css.push(generateCss(rule.selectorText, rule.style.cssText));else if (detectFonts && rule.cssText.match(/^@font-face/)) {
var font = detectCssFont(rule, href);
if (font) fontList.push(font);
} else if (!excludeUnusedCss) {
css.push(rule.cssText);
}
}
});
});
return inlineFonts(fontList).then(function (fontCss) {
return css.join('\n') + fontCss;
});
};
var downloadOptions = function downloadOptions() {
if (!navigator.msSaveOrOpenBlob && !('download' in document.createElement('a'))) {
return { popup: window.open() };
}
};
out$.prepareSvg = function (el, options, done) {
requireDomNode(el);
var _ref3 = options || {},
_ref3$left = _ref3.left,
left = _ref3$left === undefined ? 0 : _ref3$left,
_ref3$top = _ref3.top,
top = _ref3$top === undefined ? 0 : _ref3$top,
w = _ref3.width,
h = _ref3.height,
_ref3$scale = _ref3.scale,
scale = _ref3$scale === undefined ? 1 : _ref3$scale,
_ref3$responsive = _ref3.responsive,
responsive = _ref3$responsive === undefined ? false : _ref3$responsive,
_ref3$excludeCss = _ref3.excludeCss,
excludeCss = _ref3$excludeCss === undefined ? false : _ref3$excludeCss;
return inlineImages(el).then(function () {
var clone = el.cloneNode(true);
clone.style.backgroundColor = (options || {}).backgroundColor || el.style.backgroundColor;
var _getDimensions = getDimensions(el, clone, w, h),
width = _getDimensions.width,
height = _getDimensions.height;
if (el.tagName !== 'svg') {
if (el.getBBox) {
if (clone.getAttribute('transform') != null) {
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.setAttribute('viewBox', [left, top, width, height].join(' '));
if (!clone.getAttribute('xmlns')) clone.setAttributeNS(xmlNs, 'xmlns', svgNs);
if (!clone.getAttribute('xmlns:xlink')) clone.setAttributeNS(xmlNs, 'xmlns:xlink', 'http://www.w3.org/1999/xlink');
if (responsive) {
clone.removeAttribute('width');
clone.removeAttribute('height');
clone.setAttribute('preserveAspectRatio', 'xMinYMin meet');
} else {
clone.setAttribute('width', width * scale);
clone.setAttribute('height', height * scale);
}
Array.from(clone.querySelectorAll('foreignObject > *')).forEach(function (foreignObject) {
foreignObject.setAttributeNS(xmlNs, 'xmlns', foreignObject.tagName === 'svg' ? svgNs : xhtmlNs);
});
if (excludeCss) {
var outer = document.createElement('div');
outer.appendChild(clone);
var src = outer.innerHTML;
if (typeof done === 'function') done(src, width, height);else return { src: src, width: width, height: height };
} else {
return inlineCss(el, options).then(function (css) {
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.innerHTML = '<![CDATA[\n' + css + '\n]]>';
var defs = document.createElement('defs');
defs.appendChild(style);
clone.insertBefore(defs, clone.firstChild);
var outer = document.createElement('div');
outer.appendChild(clone);
var src = outer.innerHTML.replace(/NS\d+:href/gi, 'xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href');
if (typeof done === 'function') done(src, width, height);else return { src: src, width: width, height: height };
});
}
});
};
out$.svgAsDataUri = function (el, options, done) {
requireDomNode(el);
return out$.prepareSvg(el, options).then(function (_ref4) {
var src = _ref4.src,
width = _ref4.width,
height = _ref4.height;
var svgXml = 'data:image/svg+xml;base64,' + window.btoa(reEncode(doctype + src));
if (typeof done === 'function') {
done(svgXml, width, height);
}
return svgXml;
});
};
out$.svgAsPngUri = function (el, options, done) {
requireDomNode(el);
var _ref5 = options || {},
_ref5$encoderType = _ref5.encoderType,
encoderType = _ref5$encoderType === undefined ? 'image/png' : _ref5$encoderType,
_ref5$encoderOptions = _ref5.encoderOptions,
encoderOptions = _ref5$encoderOptions === undefined ? 0.8 : _ref5$encoderOptions,
canvg = _ref5.canvg;
var convertToPng = function convertToPng(_ref6) {
var src = _ref6.src,
width = _ref6.width,
height = _ref6.height;
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var pixelRatio = window.devicePixelRatio || 1;
canvas.width = width * pixelRatio;
canvas.height = height * pixelRatio;
canvas.style.width = canvas.width + 'px';
canvas.style.height = canvas.height + 'px';
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
if (canvg) canvg(canvas, src);else context.drawImage(src, 0, 0);
var png = void 0;
try {
png = canvas.toDataURL(encoderType, encoderOptions);
} catch (e) {
if (typeof SecurityError !== 'undefined' && e instanceof SecurityError || e.name === 'SecurityError') {
console.error('Rendered SVG images cannot be downloaded in this browser.');
return;
} else throw e;
}
if (typeof done === 'function') done(png, canvas.width, canvas.height);
return Promise.resolve(png);
};
if (canvg) return out$.prepareSvg(el, options).then(convertToPng);else return out$.svgAsDataUri(el, options).then(function (uri) {
return new Promise(function (resolve, reject) {
var image = new Image();
image.onload = function () {
return resolve(convertToPng({
src: image,
width: image.width,
height: image.height
}));
};
image.onerror = function () {
reject('There was an error loading the data URI as an image on the following SVG\n' + window.atob(uri.slice(26)) + 'Open the following link to see browser\'s diagnosis\n' + uri);
};
image.src = uri;
});
});
};
out$.download = function (name, uri, options) {
if (navigator.msSaveOrOpenBlob) navigator.msSaveOrOpenBlob(uriToBlob(uri), name);else {
var saveLink = document.createElement('a');
if ('download' in saveLink) {
saveLink.download = name;
saveLink.style.display = 'none';
document.body.appendChild(saveLink);
try {
var blob = uriToBlob(uri);
var url = URL.createObjectURL(blob);
saveLink.href = url;
saveLink.onclick = function () {
return requestAnimationFrame(function () {
return URL.revokeObjectURL(url);
});
};
} catch (e) {
console.error(e);
console.warn('Error while getting object URL. Falling back to string URL.');
saveLink.href = uri;
}
saveLink.click();
document.body.removeChild(saveLink);
} else if (options && options.popup) {
options.popup.document.title = name;
options.popup.location.replace(uri);
}
}
};
out$.saveSvg = function (el, name, options) {
var downloadOpts = downloadOptions(); // don't inline, can't be async
return requireDomNodePromise(el).then(function (el) {
return out$.svgAsDataUri(el, options || {});
}).then(function (uri) {
return out$.download(name, uri, downloadOpts);
});
};
out$.saveSvgAsPng = function (el, name, options) {
var downloadOpts = downloadOptions(); // don't inline, can't be async
return requireDomNodePromise(el).then(function (el) {
return out$.svgAsPngUri(el, options || {});
}).then(function (uri) {
return out$.download(name, uri, downloadOpts);
});
};
})();
});
var saveSvgAsPng_1 = saveSvgAsPng.saveSvgAsPng;
var xhtml = "http://www.w3.org/1999/xhtml";
var namespaces = {
svg: "http://www.w3.org/2000/svg",
xhtml: xhtml,
xlink: "http://www.w3.org/1999/xlink",
xml: "http://www.w3.org/XML/1998/namespace",
xmlns: "http://www.w3.org/2000/xmlns/"
};
function namespace(name) {
var prefix = name += "", i = prefix.indexOf(":");
if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name; // eslint-disable-line no-prototype-builtins
}
function creatorInherit(name) {
return function() {
var document = this.ownerDocument,
uri = this.namespaceURI;
return uri === xhtml && document.documentElement.namespaceURI === xhtml
? document.createElement(name)
: document.createElementNS(uri, name);
};
}
function creatorFixed(fullname) {
return function() {
return this.ownerDocument.createElementNS(fullname.space, fullname.local);
};
}
function creator(name) {
var fullname = namespace(name);
return (fullname.local
? creatorFixed
: creatorInherit)(fullname);
}
function none() {}
function selector(selector) {
return selector == null ? none : function() {
return this.querySelector(selector);
};
}
function selection_select(select) {
if (typeof select !== "function") select = selector(select);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
if ("__data__" in node) subnode.__data__ = node.__data__;
subgroup[i] = subnode;
}
}
}
return new Selection(subgroups, this._parents);
}
function array(x) {
return typeof x === "object" && "length" in x
? x // Array, TypedArray, NodeList, array-like
: Array.from(x); // Map, Set, iterable, string, or anything else
}
function empty() {
return [];
}
function selectorAll(selector) {
return selector == null ? empty : function() {
return this.querySelectorAll(selector);
};
}
function arrayAll(select) {
return function() {
var group = select.apply(this, arguments);
return group == null ? [] : array(group);
};
}
function selection_selectAll(select) {
if (typeof select === "function") select = arrayAll(select);
else select = selectorAll(select);
for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
subgroups.push(select.call(node, node.__data__, i, group));
parents.push(node);
}
}
}
return new Selection(subgroups, parents);
}
function matcher(selector) {
return function() {
return this.matches(selector);
};
}
function childMatcher(selector) {
return function(node) {
return node.matches(selector);
};
}
var find = Array.prototype.find;
function childFind(match) {
return function() {
return find.call(this.children, match);
};
}
function childFirst() {
return this.firstElementChild;
}
function selection_selectChild(match) {
return this.select(match == null ? childFirst
: childFind(typeof match === "function" ? match : childMatcher(match)));
}
var filter = Array.prototype.filter;
function children() {
return this.children;
}
function childrenFilter(match) {
return function() {
return filter.call(this.children, match);
};
}
function selection_selectChildren(match) {
return this.selectAll(match == null ? children
: childrenFilter(typeof match === "function" ? match : childMatcher(match)));
}
function selection_filter(match) {
if (typeof match !== "function") match = matcher(match);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
subgroup.push(node);
}
}
}
return new Selection(subgroups, this._parents);
}
function sparse(update) {
return new Array(update.length);
}
function selection_enter() {
return new Selection(this._enter || this._groups.map(sparse), this._parents);
}
function EnterNode(parent, datum) {
this.ownerDocument = parent.ownerDocument;
this.namespaceURI = parent.namespaceURI;
this._next = null;
this._parent = parent;
this.__data__ = datum;
}
EnterNode.prototype = {
constructor: EnterNode,
appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
querySelector: function(selector) { return this._parent.querySelector(selector); },
querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
};
function constant(x) {
return function() {
return x;
};
}
function bindIndex(parent, group, enter, update, exit, data) {
var i = 0,
node,
groupLength = group.length,
dataLength = data.length;
// Put any non-null nodes that fit into update.
// Put any null nodes into enter.
// Put any remaining data into enter.
for (; i < dataLength; ++i) {
if (node = group[i]) {
node.__data__ = data[i];
update[i] = node;
} else {
enter[i] = new EnterNode(parent, data[i]);
}
}
// Put any non-null nodes that don’t fit into exit.
for (; i < groupLength; ++i) {
if (node = group[i]) {
exit[i] = node;
}
}
}
function bindKey(parent, group, enter, update, exit, data, key) {
var i,
node,
nodeByKeyValue = new Map,
groupLength = group.length,
dataLength = data.length,
keyValues = new Array(groupLength),
keyValue;
// Compute the key for each node.
// If multiple nodes have the same key, the duplicates are added to exit.
for (i = 0; i < groupLength; ++i) {
if (node = group[i]) {
keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + "";
if (nodeByKeyValue.has(keyValue)) {
exit[i] = node;
} else {
nodeByKeyValue.set(keyValue, node);
}
}
}
// Compute the key for each datum.
// If there a node associated with this key, join and add it to update.
// If there is not (or the key is a duplicate), add it to enter.
for (i = 0; i < dataLength; ++i) {
keyValue = key.call(parent, data[i], i, data) + "";
if (node = nodeByKeyValue.get(keyValue)) {
update[i] = node;
node.__data__ = data[i];
nodeByKeyValue.delete(keyValue);
} else {
enter[i] = new EnterNode(parent, data[i]);
}
}
// Add any remaining nodes that were not bound to data to exit.
for (i = 0; i < groupLength; ++i) {
if ((node = group[i]) && (nodeByKeyValue.get(keyValues[i]) === node)) {
exit[i] = node;
}
}
}
function datum(node) {
return node.__data__;
}
function selection_data(value, key) {
if (!arguments.length) return Array.from(this, datum);
var bind = key ? bindKey : bindIndex,
parents = this._parents,
groups = this._groups;
if (typeof value !== "function") value = constant(value);
for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
var parent = parents[j],
group = groups[j],
groupLength = group.length,
data = array(value.call(parent, parent && parent.__data__, j, parents)),
dataLength = data.length,
enterGroup = enter[j] = new Array(dataLength),
updateGroup = update[j] = new Array(dataLength),
exitGroup = exit[j] = new Array(groupLength);
bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
// Now connect the enter nodes to their following update node, such that
// appendChild can insert the materialized enter node before this node,
// rather than at the end of the parent node.
for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
if (previous = enterGroup[i0]) {
if (i0 >= i1) i1 = i0 + 1;
while (!(next = updateGroup[i1]) && ++i1 < dataLength);
previous._next = next || null;
}
}
}
update = new Selection(update, parents);
update._enter = enter;
update._exit = exit;
return update;
}
function selection_exit() {
return new Selection(this._exit || this._groups.map(sparse), this._parents);
}
function selection_join(onenter, onupdate, onexit) {
var enter = this.enter(), update = this, exit = this.exit();
enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
if (onupdate != null) update = onupdate(update);
if (onexit == null) exit.remove(); else onexit(exit);
return enter && update ? enter.merge(update).order() : update;
}
function selection_merge(selection) {
if (!(selection instanceof Selection)) throw new Error("invalid merge");
for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
if (node = group0[i] || group1[i]) {
merge[i] = node;
}
}
}
for (; j < m0; ++j) {
merges[j] = groups0[j];
}
return new Selection(merges, this._parents);
}
function selection_order() {
for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
if (node = group[i]) {
if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
next = node;
}
}
}
return this;
}
function selection_sort(compare) {
if (!compare) compare = ascending;
function compareNode(a, b) {
return a && b ? compare(a.__data__, b.__data__) : !a - !b;
}
for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
if (node = group[i]) {
sortgroup[i] = node;
}
}
sortgroup.sort(compareNode);
}
return new Selection(sortgroups, this._parents).order();
}
function ascending(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
function selection_call() {
var callback = arguments[0];
arguments[0] = this;
callback.apply(null, arguments);
return this;
}
function selection_nodes() {
return Array.from(this);
}
function selection_node() {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
var node = group[i];
if (node) return node;
}
}
return null;
}
function selection_size() {
let size = 0;
for (const node of this) ++size; // eslint-disable-line no-unused-vars
return size;
}
function selection_empty() {
return !this.node();
}
function selection_each(callback) {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
if (node = group[i]) callback.call(node, node.__data__, i, group);
}
}
return this;
}
function attrRemove(name) {
return function() {
this.removeAttribute(name);
};
}
function attrRemoveNS(fullname) {
return function() {
this.removeAttributeNS(fullname.space, fullname.local);
};
}
function attrConstant(name, value) {
return function() {
this.setAttribute(name, value);
};
}
function attrConstantNS(fullname, value) {
return function() {
this.setAttributeNS(fullname.space, fullname.local, value);
};
}
function attrFunction(name, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.removeAttribute(name);
else this.setAttribute(name, v);
};
}
function attrFunctionNS(fullname, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
else this.setAttributeNS(fullname.space, fullname.local, v);
};
}
function selection_attr(name, value) {
var fullname = namespace(name);
if (arguments.length < 2) {
var node = this.node();
return fullname.local
? node.getAttributeNS(fullname.space, fullname.local)
: node.getAttribute(fullname);
}
return this.each((value == null
? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
? (fullname.local ? attrFunctionNS : attrFunction)
: (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
}
function defaultView(node) {
return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
|| (node.document && node) // node is a Window
|| node.defaultView; // node is a Document
}
function styleRemove(name) {
return function() {
this.style.removeProperty(name);
};
}
function styleConstant(name, value, priority) {
return function() {
this.style.setProperty(name, value, priority);
};
}
function styleFunction(name, value, priority) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.style.removeProperty(name);
else this.style.setProperty(name, v, priority);
};
}
function selection_style(name, value, priority) {
return arguments.length > 1
? this.each((value == null
? styleRemove : typeof value === "function"
? styleFunction
: styleConstant)(name, value, priority == null ? "" : priority))
: styleValue(this.node(), name);
}
function styleValue(node, name) {
return node.style.getPropertyValue(name)
|| defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
}
function propertyRemove(name) {
return function() {
delete this[name];
};
}
function propertyConstant(name, value) {
return function() {
this[name] = value;
};
}
function propertyFunction(name, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) delete this[name];
else this[name] = v;
};
}
function selection_property(name, value) {
return arguments.length > 1
? this.each((value == null
? propertyRemove : typeof value === "function"
? propertyFunction
: propertyConstant)(name, value))
: this.node()[name];
}
function classArray(string) {
return string.trim().split(/^|\s+/);
}
function classList(node) {
return node.classList || new ClassList(node);
}
function ClassList(node) {
this._node = node;
this._names = classArray(node.getAttribute("class") || "");
}
ClassList.prototype = {
add: function(name) {
var i = this._names.indexOf(name);
if (i < 0) {
this._names.push(name);
this._node.setAttribute("class", this._names.join(" "));
}
},
remove: function(name) {
var i = this._names.indexOf(name);
if (i >= 0) {
this._names.splice(i, 1);
this._node.setAttribute("class", this._names.join(" "));
}
},
contains: function(name) {
return this._names.indexOf(name) >= 0;
}
};
function classedAdd(node, names) {
var list = classList(node), i = -1, n = names.length;
while (++i < n) list.add(names[i]);
}
function classedRemove(node, names) {
var list = classList(node), i = -1, n = names.length;
while (++i < n) list.remove(names[i]);
}
function classedTrue(names) {
return function() {
classedAdd(this, names);
};
}
function classedFalse(names) {
return function() {
classedRemove(this, names);
};
}
function classedFunction(names, value) {
return function() {
(value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
};
}
function selection_classed(name, value) {
var names = classArray(name + "");
if (arguments.length < 2) {
var list = classList(this.node()), i = -1, n = names.length;
while (++i < n) if (!list.contains(names[i])) return false;
return true;
}
return this.each((typeof value === "function"
? classedFunction : value
? classedTrue
: classedFalse)(names, value));
}
function textRemove() {
this.textContent = "";
}
function textConstant(value) {
return function() {
this.textContent = value;
};
}
function textFunction(value) {
return function() {
var v = value.apply(this, arguments);
this.textContent = v == null ? "" : v;
};
}
function selection_text(value) {
return arguments.length
? this.each(value == null
? textRemove : (typeof value === "function"
? textFunction
: textConstant)(value))
: this.node().textContent;
}
function htmlRemove() {
this.innerHTML = "";
}
function htmlConstant(value) {
return function() {
this.innerHTML = value;
};
}
function htmlFunction(value) {
return function() {
var v = value.apply(this, arguments);
this.innerHTML = v == null ? "" : v;
};
}
function selection_html(value) {
return arguments.length
? this.each(value == null
? htmlRemove : (typeof value === "function"
? htmlFunction
: htmlConstant)(value))
: this.node().innerHTML;
}
function raise() {
if (this.nextSibling) this.parentNode.appendChild(this);
}
function selection_raise() {
return this.each(raise);
}
function lower() {
if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
}
function selection_lower() {
return this.each(lower);
}
function selection_append(name) {
var create = typeof name === "function" ? name : creator(name);
return this.select(function() {
return this.appendChild(create.apply(this, arguments));
});
}
function constantNull() {
return null;
}
function selection_insert(name, before) {
var create = typeof name === "function" ? name : creator(name),
select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
return this.select(function() {
return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
});
}
function remove() {
var parent = this.parentNode;
if (parent) parent.removeChild(this);
}
function selection_remove() {
return this.each(remove);
}
function selection_cloneShallow() {
var clone = this.cloneNode(false), parent = this.parentNode;
return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
}
function selection_cloneDeep() {
var clone = this.cloneNode(true), parent = this.parentNode;
return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
}
function selection_clone(deep) {
return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
}
function selection_datum(value) {
return arguments.length
? this.property("__data__", value)
: this.node().__data__;
}
function contextListener(listener) {
return function(event) {
listener.call(this, event, this.__data__);
};
}
function parseTypenames(typenames) {
return typenames.trim().split(/^|\s+/).map(function(t) {
var name = "", i = t.indexOf(".");
if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
return {type: t, name: name};
});
}
function onRemove(typename) {
return function() {
var on = this.__on;
if (!on) return;
for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
this.removeEventListener(o.type, o.listener, o.options);
} else {
on[++i] = o;
}
}
if (++i) on.length = i;
else delete this.__on;
};
}
function onAdd(typename, value, options) {
return function() {
var on = this.__on, o, listener = contextListener(value);
if (on) for (var j = 0, m = on.length; j < m; ++j) {
if ((o = on[j]).type === typename.type && o.name === typename.name) {
this.removeEventListener(o.type, o.listener, o.options);
this.addEventListener(o.type, o.listener = listener, o.options = options);
o.value = value;
return;
}
}
this.addEventListener(typename.type, listener, options);
o = {type: typename.type, name: typename.name, value: value, listener: listener, options: options};
if (!on) this.__on = [o];
else on.push(o);
};
}
function selection_on(typename, value, options) {
var typenames = parseTypenames(typename + ""), i, n = typenames.length, t;
if (arguments.length < 2) {
var on = this.node().__on;
if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
for (i = 0, o = on[j]; i < n; ++i) {
if ((t = typenames[i]).type === o.type && t.name === o.name) {
return o.value;
}
}
}
return;
}
on = value ? onAdd : onRemove;
for (i = 0; i < n; ++i) this.each(on(typenames[i], value, options));
return this;
}
function dispatchEvent(node, type, params) {
var window = defaultView(node),
event = window.CustomEvent;
if (typeof event === "function") {
event = new event(type, params);
} else {
event = window.document.createEvent("Event");
if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
else event.initEvent(type, false, false);
}
node.dispatchEvent(event);
}
function dispatchConstant(type, params) {
return function() {
return dispatchEvent(this, type, params);
};
}
function dispatchFunction(type, params) {
return function() {
return dispatchEvent(this, type, params.apply(this, arguments));
};
}
function selection_dispatch(type, params) {
return this.each((typeof params === "function"
? dispatchFunction
: dispatchConstant)(type, params));
}
function* selection_iterator() {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
if (node = group[i]) yield node;
}
}
}
var root = [null];
function Selection(groups, parents) {
this._groups = groups;
this._parents = parents;
}
function selection_selection() {
return this;
}
Selection.prototype = {
constructor: Selection,
select: selection_select,
selectAll: selection_selectAll,
selectChild: selection_selectChild,
selectChildren: selection_selectChildren,
filter: selection_filter,
data: selection_data,
enter: selection_enter,
exit: selection_exit,
join: selection_join,
merge: selection_merge,
selection: selection_selection,
order: selection_order,
sort: selection_sort,
call: selection_call,
nodes: selection_nodes,
node: selection_node,
size: selection_size,
empty: selection_empty,
each: selection_each,
attr: selection_attr,
style: selection_style,
property: selection_property,
classed: selection_classed,
text: selection_text,
html: selection_html,
raise: selection_raise,
lower: selection_lower,
append: selection_append,
insert: selection_insert,
remove: selection_remove,
clone: selection_clone,
datum: selection_datum,
on: selection_on,
dispatch: selection_dispatch,
[Symbol.iterator]: selection_iterator
};
function select(selector) {
return typeof selector === "string"
? new Selection([[document.querySelector(selector)]], [document.documentElement])
: new Selection([[selector]], root);
}
function chartFrame(configObject) {
var autoPosition = false;
var a11yDesc = "A graphic by the Financial Times";
var a11yPlotPresentation = true;
var a11yTitle = "A chart";
var backgroundColor;
var containerClass = "g-chartframe";
var copyright = "© FT";
var copyrightStyle = false;
var goalposts = false; // goalpost is the bit at the top and bottom of pritn charts
var blackbar = false; // blackbar the short black bar above web graphics
var whitebar = false; // whitebar the short white bar above social graphics
var fullYear = false;
var showDownloadPngButtons = true;
var graphicHeight = 400;
var graphicWidth = 500;
var plot;
var plotAdjuster = 0;
var rem = 18;
var subtitle = "some supporting information, units perhaps";
var subtitleLineHeight = 20;
var subtitleStyle = {};
var source = "Source: research";
var sourceLineHeight = 16;
var sourcePlotYOffset = 46;
var sourceStyle = {};
var title = "Title: A description of the charts purpose";
var titleLineHeight = 32;
var titleStyle = {};
var watermarkLocation = "icons.svg#ft-logo";
var watermarkMarkup = "";
var watermarkOffsetX = 40;
var watermarkOffsetY = 0;
var watermarkWidth = 124;
var watermarkHeight = 10;
var units = "px";
var margin = {
top: 100,
left: 1,
bottom: 20,
right: 20
};
var subtitlePosition = {
x: 1,
y: 67
};
var sourcePosition = {
x: 1
};
var titlePosition = {
x: 1,
y: 30
};
var convertFrom = {
mm: function mm(x) {
return x * 2.83464480558843;
},
px: function px(x) {
return x;
}
};
var custom = {};
/* istanbul ignore next This is already well tested. */
function attributeStyle(parent, style) {
Object.keys(style).forEach(function (attribute) {
parent.attr(attribute, style[attribute]);
});
}
function frame(p) {
// overall graphic properties
p.attr("class", containerClass).attr("font-family", "MetricWeb,sans-serif");
p.attr("role", "img");
/* istanbul ignore next This is already well tested. */
if (p.node().nodeName.toLowerCase() === "svg") {
p.attr("width", graphicWidth).attr("height", graphicHeight).attr("viewBox", ["0 0", graphicWidth, graphicHeight].join(" "));
/* istanbul ignore next This is already well tested. */
if (a11yTitle !== false || title !== false) {
p.append("title").text(a11yTitle || title).attr("id", "".concat(containerClass, "__chart-a11y-title"));
p.attr("aria-labelledby", "".concat(containerClass, "__chart-a11y-title"));
}
if (a11yDesc !== false) {
p.append("desc").text(a11yDesc).attr("id", "".concat(containerClass, "__chart-a11y-desc"));
p.attr("aria-labelledby", "".concat(p.attr("aria-labelledby") ? "".concat(p.attr("aria-labelledby"), " ") : "").concat(containerClass, "__chart-a11y-desc"));
}
} // background
if (backgroundColor !== undefined && !p.select("#chart-background").size()) {
// @TODO remove second guard; see #62.
p.append("rect").attr("role", "presentation").attr("id", "chart-background").attr("class", "chart-background").attr("x", 0).attr("y", 0).attr("width", graphicWidth).attr("height", graphicHeight).attr("fill", backgroundColor);
} // 'blackbar' (the short black bar above web graphics) @TODO remove second guard; see #62.
if (blackbar && !p.selectAll("rect.black-bar").size()) {
p.append("rect").attr("class", "black-bar").attr("width", 60).attr("height", 4).style("fill", blackbar);
}
if (whitebar && !p.selectAll("rect.white-bar").size()) {
p.append("rect").attr("class", "white-bar").attr("width", 60).attr("height", 4).style("fill", whitebar).attr("transform", "translate(".concat(margin.left, ",").concat(margin.left, ")"));
} // 'goalposts' (the bit at the top and the bottom of print charts) @TODO remove second guard; see #62.
if (goalposts && !p.selectAll("path.chart-goalposts").size()) {
var goalpostPaths = ["M 0, ".concat(graphicHeight, " L ").concat(graphicWidth, ", ").concat(graphicHeight), "M 0, 15 L 0, 0 L ".concat(graphicWidth, ", 0 L ").concat(graphicWidth, ", 15")];
p.append("path").attr("class", "chart-goalposts").attr("d", goalpostPaths).attr("stroke-width", 0.3).attr("fill", "none").attr("stroke", goalposts);
}
/* istanbul ignore next This is already well tested. */
var titleLineCount = title ? title.split("|").length : 0;
/* istanbul ignore next This is already well tested. */
var subtitleLineCount = subtitle ? subtitle.split("|").length : 0;
/* istanbul ignore next This is already well tested. */
var sourceLineCount = source ? source.split("|").length : 0; // title; @TODO remove existence guard see #62
if (title && !p.select("text.chart-title").size()) {
p.append("text").attr("class", "chart-title").attr("id", "".concat(containerClass, "title")).call(function (titleText) {
titleText.selectAll("tspan").data(title.split("|")).enter().append("tspan").text(function (d) {
return d;
}).attr("y", function (d, i) {
return titlePosition.y + i * titleLineHeight;
}).attr("x", titlePosition.x).call(attributeStyle, titleStyle);
});
} // @TODO remove existence guard see #62
/* istanbul ignore next This is already well tested. */
if (subtitle && !p.select("text.chart-subtitle").size()) {
// subtitle
p.append("text").attr("id", "".concat(containerClass, "subtitle")).attr("class", "chart-subtitle").call(function (subtitleText) {
subtitleText.selectAll("tspan").data(subtitle.split("|")).enter().append("tspan").text(function (d) {
return d;
}).attr("id", "".concat(containerClass, "subtitle")).attr("y", function (d, i) {
if (titleLineCount > 1) {
return titlePosition.y + titleLineCount * titleLineHeight + subtitleLineHeight * i;
}
return subtitlePosition.y + i * subtitleLineHeight;
}).attr("x", subtitlePosition.x).call(attributeStyle, subtitleStyle);
});
} // source; @TODO remove second existence check see #62
/* istanbul ignore next This is already well tested. */
if (source && !p.selectAll("text.chart-source").size()) {
p.append("text").attr("class", "chart-source").attr("id", "".concat(containerClass, "source")).call(function (sourceText) {
sourceText.selectAll("tspan").data(source.split("|")).enter().append("tspan").text(function (d) {
return d;
}).attr("id", "".concat(containerClass, "source")).attr("y", function (d, i) {
/* istanbul ignore next I don't know how to test this. */
if (sourcePosition.y) {
return sourcePosition.y + i * sourceLineHeight;
}
return graphicHeight - (margin.bottom - sourcePlotYOffset) + sourceLineHeight * 1.5 + i * sourceLineHeight;
}).attr("x", sourcePosition.x).call(attributeStyle, sourceStyle);
});
} // copyright
if (copyrightStyle && !p.selectAll("text.chart-copyright").size()) {
p.append("text").attr("class", "chart-copyright").append("tspan").text(copyright).attr("x", sourcePosition.x).attr("y", function () {
/* istanbul ignore next I don't know how to test this. */
if (sourceLineCount > 1) {
return graphicHeight - (margin.bottom - sourcePlotYOffset) + sourceLineHeight * 1.125 + sourceLineCount * sourceLineHeight * 1.2;
}
return graphicHeight - (margin.bottom - sourcePlotYOffset) + sourceLineHeight * 2.5;
}).call(attributeStyle, copyrightStyle);
} // TODO figure out a way to improve this autoPosition stuff, needs ot be configurable so we don't have to reference specific classes
if (autoPosition && (containerClass === "ft-printgraphic" || containerClass === "ft-socialgraphic" || containerClass === "ft-videographic")) {
margin.top = titlePosition.y + titleLineCount * titleLineHeight + subtitleLineCount * subtitleLineHeight + rem / 3;
} else if (autoPosition) {
margin.top = titlePosition.y + titleLineCount * titleLineHeight + subtitleLineCount * subtitleLineHeight + 28 - plotAdjuster;
} // watermark; @TODO remove existence check (#62)
if (watermarkMarkup && !p.selectAll("g.chart-watermark").size() && p.node().ownerDocument.doctype.name !== "svg") {
p.append("g").attr("class", "chart-watermark").html(watermarkMarkup) // This needs to be .text() to work in pure SVG context
.attr("role", "presentation").attr("transform", "translate(".concat(graphicWidth - watermarkWidth - watermarkOffsetX, ",").concat(graphicHeight - watermarkHeight - watermarkOffsetY, ") scale(1) "));
} // plot area (where you put the chart itself)
/* istanbul ignore next I don't know how to test this. */
if (!p.selectAll("g.chart-plot").size()) {
plot = p.append("g").attr("class", "chart-plot");
} else {
plot = p.select("g.chart-plot");
}
plot.attr("transform", "translate(".concat(margin.left, ",").concat(margin.top, ")"));
/* istanbul ignore next This is already well tested. */
if (a11yPlotPresentation) {
plot.attr("role", "presentation"); // include this extra role if a11yPlotPresentation
}
/* istanbul ignore next */
if (showDownloadPngButtons && p.node().ownerDocument.doctype.name !== "svg") {
var parent;
if (p.node().nodeName.toLowerCase() === "svg") {
parent = select(p.node().parentNode);
} else {
parent = select(p.node());
} // Prevent this from being rendered twice; @TODO remove check (#62)
if (!parent.selectAll(".button-holder").size()) {
var holder = parent.append("div").attr("class", "button-holder");
holder.append("button").attr("class", "save-png-button save-png-button__1x").text("Save as .png").style("float", "left").style("opacity", 0.6).on("click", function () {
return savePNG(p, 1);
});
holder.append("button").attr("class", "save-png-button save-png-button__2x").style("float", "left").style("opacity", 0.6).text("Save as double size .png").on("click", function () {
return savePNG(p, 2);
});
}
}
} // Setters and getters
frame.a11y = function () {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
newTitle = _ref.title,
newDesc = _ref.desc;
if (newTitle !== undefined) a11yTitle = newTitle;
if (newDesc !== undefined) a11yDesc = newDesc;
if (newTitle === undefined && newDesc === undefined) {
return {
title: a11yTitle,
desc: a11yDesc
};
}
return frame;
};
frame.a11yDesc = function (x) {
if (x === undefined) return a11yDesc;
a11yDesc = x;
return frame;
};
frame.a11yPlotPresentation = function (x) {
if (x === undefined) return a11yPlotPresentation;
a11yPlotPresentation = x;
return frame;
};
frame.a11yTitle = function (x) {
if (x === undefined) return a11yTitle;
a11yTitle = x;
return frame;
};
frame.autoPosition = function (x) {
if (x === undefined) return autoPosition;
autoPosition = x;
return frame;
};
frame.backgroundColor = function (x) {
if (x === undefined) return backgroundColor;
backgroundColor = x;
return frame;
};
frame.backgroundColour = function () {
console.error("gChartframe.backgroundColour() is deprecated and will be removed next version.");
console.error("Please use gChartframe.backgroundColor() instead.");
return frame.backgroundColor.apply(frame, arguments);
};
frame.blackbar = function (x) {
if (x === undefined) return blackbar;
blackbar = x;
return frame;
};
frame.containerClass = function (x) {
if (x === undefined) return containerClass;
containerClass = x;
return frame;
};
frame.copyright = function (x) {
if (x === undefined) return copyright;
copyright = x;
return frame;
};
frame.copyrightStyle = function (x) {
if (x === undefined) return copyrightStyle;
copyrightStyle = x;
return frame;
};
frame.dimension = function () {
return {
width: graphicWidth - (margin.left + margin.right),
height: graphicHeight - (margin.top + margin.bottom)
};
};
frame.extend = function (key, value) {
custom[key] = value;
frame[key] = function (d) {
if (d === undefined) return custom[key];
custom[key] = d;
return frame;
};
return frame;
};
frame.fullYear = function (x) {
if (x === undefined) return fullYear;
fullYear = x;
return frame;
};
frame.goalposts = function (x) {
if (x === undefined) return goalposts;
goalposts = x;
return frame;
};
frame.height = function (x) {
if (x === undefined) return graphicHeight;
graphicHeight = convertFrom[units](x);
return frame;
};
frame.margin = function (x) {
if (x === undefined) return margin;
Object.keys(x).forEach(function (k) {
margin[k] = x[k];
});
return frame;
};
frame.plot = function () {
return plot;
};
frame.plotAdjuster = function (x) {
if (x === undefined) return plotAdjuster;
plotAdjuster = x;
return frame;
};
frame.rem = function (x) {
if (x === undefined) return rem;
rem = x;
return frame;
};
frame.showDownloadPngButtons = function (d) {
if (typeof d === "undefined") return showDownloadPngButtons;
showDownloadPngButtons = d;
return frame;
};
frame.source = function (x) {
if (x === undefined) return source;
source = x;
return frame;
};
frame.sourceLineHeight = function (x) {
if (x === undefined) return sourceLineHeight;
sourceLineHeight = x;
return frame;
};
frame.sourcePlotYOffset = function (x) {
if (x === undefined) return sourcePlotYOffset;
sourcePlotYOffset = x;
return frame;
};
frame.sourceStyle = function (x) {
if (x === undefined) return sourceStyle;
sourceStyle = x;
return frame;
};
frame.sourceX = function (x) {
if (x === undefined) return sourcePosition.x;
sourcePosition.x = x;
return frame;
};
frame.sourceY = function (x) {
if (x === undefined) return sourcePosition.y;
sourcePosition.y = x;
return frame;
};
frame.subtitle = function (x) {
if (x === undefined) return subtitle;
subtitle = x;
return frame;
};
frame.subtitleLineHeight = function (x) {
if (x === undefined) return subtitleLineHeight;
subtitleLineHeight = x;
return frame;
};
frame.subtitleStyle = function (x) {
if (x === undefined) return subtitleStyle;
subtitleStyle = x;
return frame;
};
frame.subtitleX = function (x) {
if (x === undefined) return subtitlePosition.x;
subtitlePosition.x = x;
return frame;
};
frame.subtitleY = function (x) {
if (x === undefined) return subtitlePosition.y;
subtitlePosition.y = x;
return frame;
};
frame.title = function (x) {
if (x === undefined) return title;
title = x;
return frame;
};
frame.titleStyle = function (x) {
if (x === undefined) return titleStyle;
titleStyle = x;
return frame;
};
frame.titleLineHeight = function (x) {
if (x === undefined) return titleLineHeight;
titleLineHeight = x;
return frame;
};
frame.titleX = function (x) {
if (x === undefined) return titlePosition.x;
titlePosition.x = x;
return frame;
};
frame.titleY = function (x) {
if (x === undefined) return titlePosition.y;
titlePosition.y = x;
return frame;
};
frame.units = function (x) {
if (x === undefined) return units;
units = x;
return frame;
};
frame.watermark = function (x) {
if (x === undefined) return watermarkMarkup;
watermarkLocation = "";
watermarkMarkup = x;
return frame;
};
frame.watermarkOffsetY = function (x) {
if (x === undefined) return watermarkOffsetY;
watermarkOffsetY = x;
return frame;
};
frame.watermarkOffsetX = function (x) {
if (x === undefined) return watermarkOffsetX;
watermarkOffsetX = x;
return frame;
};
frame.watermarkLocation = function (x) {
if (x === undefined) return watermarkLocation;
watermarkMarkup = "";
watermarkLocation = x;
return frame;
};
frame.watermarkWidth = function (x) {
if (x === undefined) return watermarkWidth;
watermarkWidth = x;
return frame;
};
frame.watermarkHeight = function (x) {
if (x === undefined) return watermarkHeight;
watermarkHeight = x;
return frame;
};
frame.whitebar = function (x) {
if (x === undefined) return whitebar;
whitebar = x;
return frame;
};
frame.width = function (x) {
if (!x) return graphicWidth;
graphicWidth = convertFrom[units](x);
return frame;
};
frame.attrs = function (x) {
if (x === undefined) {
return Object.assign({}, {
a11yDesc: a11yDesc,
a11yPlotPresentation: a11yPlotPresentation,
a11yTitle: a11yTitle,
autoPosition: autoPosition,
containerClass: containerClass,
copyright: copyright,
copyrightStyle: copyrightStyle,
blackbar: blackbar,
backgroundColor: backgroundColor,
goalposts: goalposts,
graphicHeight: graphicHeight,
graphicWidth: graphicWidth,
margin: margin,
plot: plot,
plotAdjuster: plotAdjuster,
rem: rem,
subtitle: subtitle,
subtitleLineHeight: subtitleLineHeight,
subtitlePosition: subtitlePosition,
subtitleStyle: subtitleStyle,
source: source,
sourceLineHeight: sourceLineHeight,
sourcePosition: sourcePosition,
sourceStyle: sourceStyle,
title: title,
titleLineHeight: titleLineHeight,
titlePosition: titlePosition,
titleStyle: titleStyle,
watermarkLocation: watermarkLocation,
watermarkMarkup: watermarkMarkup,
watermarkOffsetX: watermarkOffsetX,
watermarkOffsetY: watermarkOffsetY,
watermarkHeight: watermarkHeight,
watermarkWidth: watermarkWidth,
whitebar: whitebar,
units: units
}, custom);
}
Object.keys(x).forEach(function (setterName) {
var value = x[setterName];
/* istanbul ignore next I don't know why this won't cover. */
if (isFunction(frame[setterName])) {
frame[setterName](value);
}
});
return frame;
};
if (configObject !== undefined) {
frame.attrs(configObject);
}
return frame;
}
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === "[object Function]";
}
var classes = [".annotation", ".lines", ".highlights", ".axis path", ".axis text", ".axis line", ".axis", ".baseline", ".baseline line", ".legend", ".legend text", ".chart-goalposts", ".chart-title", ".chart-subtitle", ".chart-source", ".chart-copyright", ".chart-watermark", ".annotations-holder", ".lines highlighlines", ".highlights", ".annotation", ".annotations-holder line", ".annotations-holder text", ".line path", ".highlights rects"];
/* istanbul ignore next */
function savePNG(svg, scaleFactor) {
svg.selectAll(classes.join(", ")).each(function inlineProps() {
var element = this;
var computedStyle = getComputedStyle(element, null); // loop through and compute inline svg styles
for (var i = 0; i < computedStyle.length; i += 1) {
var property = computedStyle.item(i);
var value = computedStyle.getPropertyValue(property);
element.style[property] = value;
}
});
saveSvgAsPng_1(svg.node(), "".concat(svg.select("title").text().replace(/\s/g, "-").toLowerCase(), ".png"), {
scale: scaleFactor
});
}
function webFrameS(configObject) {
var f = chartFrame().autoPosition(true).containerClass('ft-webgraphic-s').backgroundColor('#FFF1E0').blackbar('#000').width(300) // .watermark(watermarkPathDark)
// .watermarkSize(80)
// .watermarkOffset(-28)
.margin({
bottom: 90,
right: 5,
left: 15
}).rem(14).plotAdjuster(0).titleStyle({
'font-size': 20,
'font-family': 'MetricWeb,sans-serif',
'font-weight': 400,
fill: '#000'
}).titleY(32).titleLineHeight(24).subtitleLineHeight(20).subtitleStyle({
'font-size': 18,
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
}).subtitleY(64).sourceLineHeight(12).sourcePlotYOffset(38).sourceStyle({
'font-size': '12px',
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
}).copyrightStyle({
'font-size': '12px',
'font-style': 'italic',
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
});
if (configObject !== undefined) f.attrs(configObject);
return f;
}
function webFrameM(configObject) {
var f = chartFrame().autoPosition(true).containerClass('ft-webgraphic-m').backgroundColor('#FFF1E0').blackbar('#000').width(700).height(500) // .watermark(watermarkPathDark)
// .watermarkSize(80)
// .watermarkOffset(-28)
.margin({
bottom: 104,
right: 5,
left: 20
}).rem(16).plotAdjuster(4).titleY(32).titleStyle({
'font-size': 24,
'font-family': 'MetricWeb,sans-serif',
'font-weight': 400,
fill: '#000'
}).titleLineHeight(28).subtitleLineHeight(20).subtitleStyle({
'font-size': 18,
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
}).subtitleY(64).sourceLineHeight(16).sourcePlotYOffset(44).sourceStyle({
'font-size': '14px',
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
}).copyrightStyle({
'font-size': '14px',
'font-style': 'italic',
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
});
if (configObject !== undefined) f.attrs(configObject);
return f;
}
function webFrameMDefault(configObject) {
var f = chartFrame().autoPosition(true).containerClass('ft-webgraphic-m-default').backgroundColor('#FFF1E0').blackbar('#000').width(700).height(500) // .watermark(watermarkPathDark)
// .watermarkSize(80)
// .watermarkOffset(-28)
.margin({
bottom: 115,
right: 5,
left: 20
}).rem(20).plotAdjuster(8).titleY(32).titleStyle({
'font-size': 28,
'font-family': 'MetricWeb,sans-serif',
'font-weight': 400,
fill: '#000'
}).titleLineHeight(28).subtitleLineHeight(28).subtitleStyle({
'font-size': 24,
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
}).subtitleY(68).sourceLineHeight(18).sourcePlotYOffset(34).sourceStyle({
'font-size': '16px',
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
}).copyrightStyle({
'font-size': '14px',
'font-style': 'italic',
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
});
if (configObject !== undefined) f.attrs(configObject);
return f;
}
function webFrameL(configObject) {
var f = chartFrame().autoPosition(true).containerClass('ft-webgraphic-l').backgroundColor('#FFF1E0').width(1180).height(700).blackbar('#000').fullYear(true) // .watermark(watermarkPathDark)
// .watermarkSize(80)
// .watermarkOffset(-28)
.margin({
bottom: 105,
right: 5,
left: 20
}).rem(18).plotAdjuster(8).titleY(32).titleStyle({
'font-size': 28,
'font-family': 'MetricWeb,sans-serif',
'font-weight': 400,
fill: '#000'
}).titleLineHeight(32).subtitleLineHeight(20).subtitleY(64).subtitleStyle({
'font-size': 18,
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
}).sourceLineHeight(16).sourcePlotYOffset(44).sourceStyle({
'font-size': '16px',
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
}).copyrightStyle({
'font-size': '16px',
'font-style': 'italic',
'font-family': 'MetricWeb,sans-serif',
fill: '#66605C'
});
if (configObject !== undefined) f.attrs(configObject);
return f;
}
function printFrame(configObject) {
var f = chartFrame().containerClass('ft-printgraphic').autoPosition(true).backgroundColor('#FFF').goalposts('#000').units('mm').width(112.25) // these are after the units are set so they are converted from mm to px
.height(68).margin({
top: 40,
left: 15,
bottom: 35,
right: 7
}).rem(9.6).titleStyle({
'font-size': '12px',
fill: '#000000',
'font-weight': '600',
'font-family': 'MetricWeb,sans-serif'
}).titleX(7).titleY(15).titleLineHeight(13).subtitleStyle({
fill: '#000000',
'font-size': '9.6px',
'font-weight': 400,
'font-family': 'MetricWeb,sans-serif'
}).subtitleLineHeight(10).subtitleX(7).subtitleY(27).sourceStyle({
fill: '#000000',
'font-size': '7.2px',
'font-weight': 400,
'font-family': 'MetricWeb,sans-serif'
}).sourceX(7).sourcePlotYOffset(18).sourceLineHeight(8);
if (configObject !== undefined) f.attrs(configObject);
return f;
}
var watermarkPathLight = '<path fill="#8e9095" id="logo" d="M3,8.5c0,0.7,0.2,0.9,1.5,0.9v0.4H0V9.4c0.9,0,1.2-0.1,1.2-0.8V1.4c0-0.8-0.3-0.8-1.2-0.8V0.2h6.1c0.8,0,1.1,0,1.4-0.1l0,2.7H7.2c-0.2-2-0.7-2.2-2.6-2.2H3v4h1.3c1.3,0,1.3-0.2,1.4-1.2h0.4v2.9H5.7c-0.1-1-0.2-1.2-1.4-1.2H3V8.5zM8.4,9.8V9.4c0.9,0,1.2-0.1,1.2-0.8V1.4c0-0.8-0.3-0.8-1.2-0.8V0.2h4.2v0.4c-0.9,0-1.2,0.1-1.2,0.8v7.1c0,0.8,0.3,0.8,1.2,0.8v0.4H8.4z M22,10l-6.6-8.2v6.7c0,0.8,0.5,0.9,1.5,0.9v0.4h-3.5V9.4c0.9,0,1.4-0.1,1.4-0.9V1.1c-0.4-0.4-0.7-0.5-1.4-0.5V0.2h3.1l5.1,6.5V1.5c0-0.8-0.5-0.9-1.5-0.9V0.2h3.5v0.4c-0.9,0-1.4,0.1-1.4,0.9V10H22z M26.7,9.8h-3.5V9.4c0.9,0,1.3-0.1,1.6-0.9L28,0.1h0.9l3.4,8.5c0.3,0.8,0.4,0.8,1.1,0.8v0.4h-4.1V9.4c1.2,0,1.4-0.1,1.1-0.8l-1-2.6h-3l-0.9,2.5c-0.3,0.8,0.2,0.9,1.2,0.9V9.8z M26.6,5.5h2.6L27.9,2L26.6,5.5z M42.6,10l-6.6-8.2v6.7c0,0.8,0.5,0.9,1.5,0.9v0.4H34V9.4c0.8,0,1.3-0.1,1.3-0.9V1.1c-0.5-0.4-0.8-0.5-1.6-0.5V0.2H37l5.1,6.5V1.5c0-0.8-0.5-0.9-1.5-0.9V0.2h3.5v0.4c-0.9,0-1.4,0.1-1.4,0.9V10H42.6z M52.4,0.1h0.2L52.7,3l-0.4,0c-0.2-1.7-1.1-2.5-2.7-2.5c-1.8,0-3.2,1.5-3.2,3.9c0,3,1.9,4.7,3.9,4.7c0.9,0,1.6-0.2,2.4-1.1L53,8.4c-0.6,0.9-1.8,1.6-3.5,1.6c-2.4,0-4.9-1.8-4.9-4.9c0-3,2.3-5.1,5-5.1c1.3,0,2,0.6,2.3,0.6C52.2,0.6,52.3,0.4,52.4,0.1z M53.8,9.8V9.4c0.9,0,1.2-0.1,1.2-0.8V1.4c0-0.8-0.3-0.8-1.2-0.8V0.2H58v0.4c-0.9,0-1.2,0.1-1.2,0.8v7.1c0,0.8,0.3,0.8,1.2,0.8v0.4H53.8z M61.9,9.8h-3.3V9.4c0.7,0,1.1-0.1,1.4-0.9l3.2-8.4h0.9l3.4,8.5c0.3,0.8,0.4,0.8,1.1,0.8v0.4h-4.1V9.4c1.2,0,1.4-0.1,1.1-0.8l-1-2.6h-3l-0.9,2.5c-0.3,0.8,0.2,0.9,1.2,0.9V9.8z M61.8,5.5h2.6L63,2L61.8,5.5z M69.3,9.8V9.4c0.9,0,1.2-0.1,1.2-0.8V1.4c0-0.8-0.3-0.8-1.2-0.8V0.2h4.1v0.4c-0.9,0-1.2,0.1-1.2,0.8v7.2c0,0.6,0.3,0.7,0.8,0.7h0.5c1.9,0,2.5-0.3,3-2.4L77,7l-0.3,2.9H69.3z M89.4,0.1l0,2.9h-0.4c-0.2-2.1-0.7-2.3-2.6-2.3H86v7.9c0,0.8,0.3,0.9,1.5,0.9v0.4h-4.8V9.4c1.2,0,1.5-0.1,1.5-0.9V0.6h-0.6c-1.9,0-2.3,0.3-2.6,2.3h-0.4l0-2.9c0.3,0,0.5,0.1,1.4,0.1h6C88.9,0.2,89.2,0.1,89.4,0.1z M90.4,9.8V9.4c0.9,0,1.2-0.1,1.2-0.8V1.4c0-0.8-0.3-0.8-1.2-0.8V0.2h4.2v0.4c-0.9,0-1.2,0.1-1.2,0.8v7.1c0,0.8,0.3,0.8,1.2,0.8v0.4H90.4z M107,0.2v0.4c-0.9,0-1.3,0-1.2,0.8l0.8,7.2c0.1,0.7,0.4,0.8,1.2,0.8v0.4h-4.1V9.4c0.9,0,1.2-0.1,1.1-0.8L104,1l-3.1,9h-0.1l-3-9l-0.7,7.5c-0.1,0.8,0.4,0.8,1.3,0.8v0.4h-3.2V9.4c0.9,0,1.2-0.1,1.2-0.8l0.7-7.2c0.1-0.8-0.3-0.8-1.2-0.8V0.2h3.4l2,6.6l2.2-6.6H107z M114.8,6.3h-0.4c-0.1-1-0.2-1.2-1.4-1.2h-1.5v3.5c0,0.6,0.3,0.7,0.8,0.7h0.8c1.9,0,2.5-0.3,3-2.4l0.4,0l-0.3,2.9h-7.6V9.4c0.9,0,1.2-0.1,1.2-0.8V1.4c0-0.8-0.3-0.8-1.2-0.8V0.2h7.3l0,2.4h-0.4c-0.2-1.7-0.7-1.9-2.6-1.9h-1.4v3.9h1.5c1.3,0,1.3-0.2,1.4-1.2h0.4V6.3z M122.5,7.8c0-0.9-0.6-1.3-1.5-1.9l-1.5-0.8c-1.2-0.6-1.8-1.3-1.8-2.5c0-1.5,1.3-2.7,3-2.7c1.2,0,1.8,0.6,2.1,0.6c0.2,0,0.3-0.1,0.4-0.4h0.3l0.1,2.7l-0.4,0c-0.2-1.3-1.1-2.3-2.5-2.3c-1,0-1.7,0.6-1.7,1.4c0,0.9,0.7,1.3,1.5,1.7l1.3,0.7c1.2,0.7,2.1,1.4,2.1,2.7c0,1.7-1.5,2.9-3.3,2.9c-1.3,0-1.9-0.6-2.3-0.6c-0.2,0-0.3,0.2-0.4,0.5h-0.3L117.4,7l0.4,0c0.3,1.8,1.5,2.5,2.8,2.5C121.6,9.5,122.5,9,122.5,7.8z"/>';
var watermarkPath = {
light: watermarkPathLight
};
function socialFrame(configObject) {
var f = chartFrame().autoPosition(true).containerClass('ft-socialgraphic').backgroundColor('#262a33').whitebar('#fff').width(612).height(612).watermark(watermarkPath.light).watermarkOffsetX(40).watermarkOffsetY(40).margin({
left: 40,
right: 40,
bottom: 138,
top: 140
}).rem(24).titleX(40).titleY(80).titleLineHeight(32).titleStyle({
'font-size': '30px',
fill: '#ffffff',
'font-weight': 400,
'font-family': 'MetricWeb,sans-serif'
}).subtitleX(40).subtitleY(112).subtitleLineHeight(28).subtitleStyle({
'font-size': '24px',
fill: '#8e9095',
'font-weight': 400,
'font-family': 'MetricWeb,sans-serif'
}).sourceX(40).sourceLineHeight(20).sourcePlotYOffset(66).sourceStyle({
'font-size': '20px',
fill: '#8e9095',
'font-weight': 400,
'font-family': 'MetricWeb,sans-serif'
});
if (configObject !== undefined) f.attrs(configObject);
return f;
}
function videoFrame(configObject) {
var f = chartFrame().autoPosition(true).backgroundColor('#262a33').containerClass('ft-videographic').width(1920).height(1080).margin({
left: 207,
right: 207,
bottom: 210,
top: 233
}).rem(48).titleX(207).titleY(130).titleLineHeight(68).titleStyle({
'font-size': '72px',
fill: '#ffffff',
'font-weight': 400,
'font-family': 'MetricWeb,sans-serif'
}).subtitleX(207).subtitleY(200).subtitleLineHeight(48).subtitleStyle({
'font-size': '48px',
fill: '#8e9095',
'font-weight': 400,
'font-family': 'MetricWeb,sans-serif'
}).sourceX(207).sourcePlotYOffset(60).sourceLineHeight(38).sourceStyle({
'font-size': '36px',
fill: '#8e9095',
'font-weight': 400,
'font-family': 'MetricWeb,sans-serif'
});
if (configObject !== undefined) f.attrs(configObject);
return f;
}
export { chartFrame as frame, printFrame, socialFrame, videoFrame, webFrameL, webFrameM, webFrameMDefault, webFrameS };