billboard.js
Version:
Re-usable easy interface JavaScript chart library, based on D3 v4+
142 lines (139 loc) • 4.91 kB
JavaScript
/*!
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*
* billboard.js, JavaScript chart library
* https://naver.github.io/billboard.js/
*
* @version 4.0.1
*/
import { select, namespaces } from 'd3-selection';
import { document as doc } from '../../../module/browser.js';
import { notEmpty, isObjectType, isFunction } from '../../../module/util/type-checks.js';
import { sanitize } from '../../../module/sanitize.js';
import { toArray } from '../../../module/util/object.js';
/**
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
/**
* Check if point draw methods are valid
* @param {string} point point type
* @returns {boolean}
* @private
*/
function _hasValidPointDrawMethods(point) {
return isObjectType(point) &&
isFunction(point.create) && isFunction(point.update);
}
/**
* Insert point info defs element
* @param {string} point Point element
* @param {string} id Point id
* @private
*/
function _insertPointInfoDefs(point, id) {
const $$ = this;
const copyAttr = (from, target) => {
const attribs = from.attributes;
for (let i = 0, name; (name = attribs[i]); i++) {
name = name.name;
target.setAttribute(name, from.getAttribute(name));
}
};
const doc$1 = new DOMParser().parseFromString(sanitize(point), "image/svg+xml");
const node = doc$1.documentElement;
const clone = doc.createElementNS(namespaces.svg, node.nodeName.toLowerCase());
clone.id = id;
clone.style.fill = "inherit";
clone.style.stroke = "inherit";
copyAttr(node, clone);
if (node.childNodes?.length) {
const parent = select(clone);
if ("innerHTML" in clone) {
parent.html(sanitize(node.innerHTML));
}
else {
toArray(node.childNodes).forEach(v => {
copyAttr(v, parent.append(v.tagName).node());
});
}
}
$$.$el.defs.node().appendChild(clone);
}
var shapePointCommon = {
/**
* Check if point type option is valid
* @param {string} type point type
* @returns {boolean}
* @private
*/
hasValidPointType(type) {
// For point.pattern, allow additional SVG shape tags (polygon, ellipse, use)
// These will be sanitized before use
return /^(circle|rect(angle)?|polygon|ellipse|use)$/i.test(type || this.config.point_type);
},
/**
* Check if pattern point is set to be used on legend
* @returns {boolean}
* @private
*/
hasLegendDefsPoint() {
const { config } = this;
return config.legend_show && config.point_pattern?.length && config.legend_usePoint;
},
getDefsPointId(id) {
const { state: { datetimeId } } = this;
return `${datetimeId}-point${id}`;
},
/**
* Get validated point pattern array
* @returns {Array} Array of point types
* @private
*/
getValidPointPattern() {
const { config } = this;
// Ensure point_type is restricted to 'circle' or 'rectangle' only
const validPointType = /^(circle|rect(angle)?)$/i.test(config.point_type) ?
config.point_type :
"circle";
return notEmpty(config.point_pattern) ? config.point_pattern : [validPointType];
},
/**
* Get generate point function
* @returns {function}
* @private
*/
generatePoint() {
const $$ = this;
const { $el, config } = $$;
const ids = [];
const pattern = $$.getValidPointPattern();
return function (method, context, ...args) {
return function (d) {
const id = $$.getTargetSelectorSuffix(d.id || d.data?.id || d);
const element = select(this);
ids.indexOf(id) < 0 && ids.push(id);
let point = pattern[ids.indexOf(id) % pattern.length];
if ($$.hasValidPointType(point)) {
point = $$[point];
}
else if (!_hasValidPointDrawMethods(point || config.point_type)) {
const pointId = $$.getDefsPointId(id);
const defsPoint = $el.defs.select(`#${pointId}`);
if (defsPoint.size() < 1) {
_insertPointInfoDefs.call($$, point, pointId);
}
if (method === "create") {
return $$.custom?.create.bind(context)(element, pointId, ...args);
}
else if (method === "update") {
return $$.custom?.update.bind(context)(element, ...args);
}
}
return point[method]?.bind(context)(element, ...args);
};
};
}
};
export { shapePointCommon as default };