UNPKG

billboard.js

Version:

Re-usable easy interface JavaScript chart library, based on D3 v4+

1,648 lines (1,631 loc) 766 kB
/*! * 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 3.3.3 */ import { timeParse, utcParse, timeFormat, utcFormat } from 'd3-time-format'; import { pointer, select, namespaces, selectAll } from 'd3-selection'; import { brushSelection, brushY, brushX } from 'd3-brush'; import { csvParseRows, csvParse, tsvParseRows, tsvParse } from 'd3-dsv'; import { drag as drag$1 } from 'd3-drag'; import { scaleOrdinal, scaleLinear, scaleSymlog, scaleLog, scaleTime, scaleUtc } from 'd3-scale'; import { transition } from 'd3-transition'; import { curveBasis, curveBasisClosed, curveBasisOpen, curveBundle, curveCardinal, curveCardinalClosed, curveCardinalOpen, curveCatmullRom, curveCatmullRomClosed, curveCatmullRomOpen, curveMonotoneX, curveMonotoneY, curveNatural, curveLinearClosed, curveLinear, curveStep, curveStepAfter, curveStepBefore, pie as pie$1, arc, area as area$1, line as line$1 } from 'd3-shape'; import { axisLeft, axisBottom, axisTop, axisRight } from 'd3-axis'; import { easeLinear } from 'd3-ease'; import { interpolate } from 'd3-interpolate'; import { zoomIdentity, zoomTransform, zoom as zoom$2 } from 'd3-zoom'; /** * Copyright (c) 2017 ~ present NAVER Corp. * billboard.js project is licensed under the MIT license */ var win = (function () { var root = (typeof globalThis === "object" && globalThis !== null && globalThis.Object === Object && globalThis) || (typeof global === "object" && global !== null && global.Object === Object && global) || (typeof self === "object" && self !== null && self.Object === Object && self); return root || Function("return this")(); })(); /* eslint-enable no-new-func, no-undef */ // fallback for non-supported environments win.requestIdleCallback = win.requestIdleCallback || (function (cb) { return setTimeout(cb, 1); }); win.cancelIdleCallback = win.cancelIdleCallback || (function (id) { return clearTimeout(id); }); var doc = win === null || win === void 0 ? void 0 : win.document; /** * Copyright (c) 2017 ~ present NAVER Corp. * billboard.js project is licensed under the MIT license */ /** * Chart type constant * @private */ var TYPE = { AREA: "area", AREA_LINE_RANGE: "area-line-range", AREA_SPLINE: "area-spline", AREA_SPLINE_RANGE: "area-spline-range", AREA_STEP: "area-step", BAR: "bar", BUBBLE: "bubble", CANDLESTICK: "candlestick", DONUT: "donut", GAUGE: "gauge", LINE: "line", PIE: "pie", RADAR: "radar", SCATTER: "scatter", SPLINE: "spline", STEP: "step" }; /** * Chart type module and its method from ChartInternal class, needed to be initialized. * @private */ var TYPE_METHOD_NEEDED = { AREA: "initArea", AREA_LINE_RANGE: "initArea", AREA_SPLINE: "initArea", AREA_SPLINE_RANGE: "initArea", AREA_STEP: "initArea", BAR: "initBar", BUBBLE: "initCircle", CANDLESTICK: "initCandlestick", DONUT: "initArc", GAUGE: "initArc", LINE: "initLine", PIE: "initArc", RADAR: "initCircle", SCATTER: "initCircle", SPLINE: "initLine", STEP: "initLine" }; /** * chart types by category * @private */ var TYPE_BY_CATEGORY = { Area: [ TYPE.AREA, TYPE.AREA_SPLINE, TYPE.AREA_SPLINE_RANGE, TYPE.AREA_LINE_RANGE, TYPE.AREA_STEP ], AreaRange: [ TYPE.AREA_SPLINE_RANGE, TYPE.AREA_LINE_RANGE ], Arc: [ TYPE.PIE, TYPE.DONUT, TYPE.GAUGE, TYPE.RADAR ], Line: [ TYPE.LINE, TYPE.SPLINE, TYPE.AREA, TYPE.AREA_SPLINE, TYPE.AREA_SPLINE_RANGE, TYPE.AREA_LINE_RANGE, TYPE.STEP, TYPE.AREA_STEP ], Step: [ TYPE.STEP, TYPE.AREA_STEP ], Spline: [ TYPE.SPLINE, TYPE.AREA_SPLINE, TYPE.AREA_SPLINE_RANGE ] }; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ var _assign = function __assign() { _assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } } return t; }; return _assign.apply(this, arguments); }; function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } var isValue = function (v) { return v || v === 0; }; var isFunction = function (v) { return typeof v === "function"; }; var isString = function (v) { return typeof v === "string"; }; var isNumber = function (v) { return typeof v === "number"; }; var isUndefined = function (v) { return typeof v === "undefined"; }; var isDefined = function (v) { return typeof v !== "undefined"; }; var isboolean = function (v) { return typeof v === "boolean"; }; var ceil10 = function (v) { return Math.ceil(v / 10) * 10; }; var asHalfPixel = function (n) { return Math.ceil(n) + 0.5; }; var diffDomain = function (d) { return d[1] - d[0]; }; var isObjectType = function (v) { return typeof v === "object"; }; var isEmpty = function (o) { return (isUndefined(o) || o === null || (isString(o) && o.length === 0) || (isObjectType(o) && !(o instanceof Date) && Object.keys(o).length === 0) || (isNumber(o) && isNaN(o))); }; var notEmpty = function (o) { return !isEmpty(o); }; /** * Check if is array * @param {Array} arr Data to be checked * @returns {boolean} * @private */ var isArray = function (arr) { return Array.isArray(arr); }; /** * Check if is object * @param {object} obj Data to be checked * @returns {boolean} * @private */ var isObject = function (obj) { return obj && !(obj === null || obj === void 0 ? void 0 : obj.nodeType) && isObjectType(obj) && !isArray(obj); }; /** * Get specified key value from object * If default value is given, will return if given key value not found * @param {object} options Source object * @param {string} key Key value * @param {*} defaultValue Default value * @returns {*} * @private */ function getOption(options, key, defaultValue) { return isDefined(options[key]) ? options[key] : defaultValue; } /** * Check if value exist in the given object * @param {object} dict Target object to be checked * @param {*} value Value to be checked * @returns {boolean} * @private */ function hasValue(dict, value) { var found = false; Object.keys(dict).forEach(function (key) { return (dict[key] === value) && (found = true); }); return found; } /** * Call function with arguments * @param {Function} fn Function to be called * @param {*} thisArg "this" value for fn * @param {*} args Arguments for fn * @returns {boolean} true: fn is function, false: fn is not function * @private */ function callFn(fn, thisArg) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } var isFn = isFunction(fn); isFn && fn.call.apply(fn, __spreadArray([thisArg], args, false)); return isFn; } /** * Call function after all transitions ends * @param {d3.transition} transition Transition * @param {Fucntion} cb Callback function * @private */ function endall(transition, cb) { var n = 0; var end = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } !--n && cb.apply.apply(cb, __spreadArray([this], args, false)); }; // if is transition selection if ("duration" in transition) { transition .each(function () { return ++n; }) .on("end", end); } else { ++n; transition.call(end); } } /** * Replace tag sign to html entity * @param {string} str Target string value * @returns {string} * @private */ function sanitise(str) { return isString(str) ? str.replace(/</g, "&lt;").replace(/>/g, "&gt;") : str; } /** * Set text value. If there's multiline add nodes. * @param {d3Selection} node Text node * @param {string} text Text value string * @param {Array} dy dy value for multilined text * @param {boolean} toMiddle To be alingned vertically middle * @private */ function setTextValue(node, text, dy, toMiddle) { if (dy === void 0) { dy = [-1, 1]; } if (toMiddle === void 0) { toMiddle = false; } if (!node || !isString(text)) { return; } if (text.indexOf("\n") === -1) { node.text(text); } else { var diff = [node.text(), text].map(function (v) { return v.replace(/[\s\n]/g, ""); }); if (diff[0] !== diff[1]) { var multiline = text.split("\n"); var len_1 = toMiddle ? multiline.length - 1 : 1; // reset possible text node.html(""); multiline.forEach(function (v, i) { node.append("tspan") .attr("x", 0) .attr("dy", "".concat(i === 0 ? dy[0] * len_1 : dy[1], "em")) .text(v); }); } } } /** * Substitution of SVGPathSeg API polyfill * @param {SVGGraphicsElement} path Target svg element * @returns {Array} * @private */ function getRectSegList(path) { /* * seg1 ---------- seg2 * | | * | | * | | * seg0 ---------- seg3 * */ var _a = path.getBBox(), x = _a.x, y = _a.y, width = _a.width, height = _a.height; return [ { x: x, y: y + height }, { x: x, y: y }, { x: x + width, y: y }, { x: x + width, y: y + height } // seg3 ]; } /** * Get svg bounding path box dimension * @param {SVGGraphicsElement} path Target svg element * @returns {object} * @private */ function getPathBox(path) { var _a = path.getBoundingClientRect(), width = _a.width, height = _a.height; var items = getRectSegList(path); var x = items[0].x; var y = Math.min(items[0].y, items[1].y); return { x: x, y: y, width: width, height: height }; } /** * Get event's current position coordinates * @param {object} event Event object * @param {SVGElement|HTMLElement} element Target element * @returns {Array} [x, y] Coordinates x, y array * @private */ function getPointer(event, element) { var _a; var touches = event && ((_a = (event.touches || (event.sourceEvent && event.sourceEvent.touches))) === null || _a === void 0 ? void 0 : _a[0]); var pointer$1 = pointer(touches || event, element); return pointer$1.map(function (v) { return (isNaN(v) ? 0 : v); }); } /** * Return brush selection array * @param {object} ctx Current instance * @returns {d3.brushSelection} * @private */ function getBrushSelection(ctx) { var event = ctx.event, $el = ctx.$el; var main = $el.subchart.main || $el.main; var selection; // check from event if (event && event.type === "brush") { selection = event.selection; // check from brush area selection } else if (main && (selection = main.select(".bb-brush").node())) { selection = brushSelection(selection); } return selection; } /** * Get boundingClientRect. * Cache the evaluated value once it was called. * @param {HTMLElement} node Target element * @returns {object} * @private */ function getBoundingRect(node) { var needEvaluate = !("rect" in node) || ("rect" in node && node.hasAttribute("width") && node.rect.width !== +node.getAttribute("width")); return needEvaluate ? (node.rect = node.getBoundingClientRect()) : node.rect; } /** * Retrun random number * @param {boolean} asStr Convert returned value as string * @param {number} min Minimum value * @param {number} max Maximum value * @returns {number|string} * @private */ function getRandom(asStr, min, max) { if (asStr === void 0) { asStr = true; } if (min === void 0) { min = 0; } if (max === void 0) { max = 10000; } var rand = Math.floor(Math.random() * (max - min) + min); return asStr ? String(rand) : rand; } /** * Find index based on binary search * @param {Array} arr Data array * @param {number} v Target number to find * @param {number} start Start index of data array * @param {number} end End index of data arr * @param {boolean} isRotated Weather is roted axis * @returns {number} Index number * @private */ function findIndex(arr, v, start, end, isRotated) { if (start > end) { return -1; } var mid = Math.floor((start + end) / 2); var _a = arr[mid], x = _a.x, _b = _a.w, w = _b === void 0 ? 0 : _b; if (isRotated) { x = arr[mid].y; w = arr[mid].h; } if (v >= x && v <= x + w) { return mid; } return v < x ? findIndex(arr, v, start, mid - 1, isRotated) : findIndex(arr, v, mid + 1, end, isRotated); } /** * Check if brush is empty * @param {object} ctx Bursh context * @returns {boolean} * @private */ function brushEmpty(ctx) { var selection = getBrushSelection(ctx); if (selection) { // brush selected area // two-dimensional: [[x0, y0], [x1, y1]] // one-dimensional: [x0, x1] or [y0, y1] return selection[0] === selection[1]; } return true; } /** * Deep copy object * @param {object} objectN Source object * @returns {object} Cloned object * @private */ function deepClone() { var objectN = []; for (var _i = 0; _i < arguments.length; _i++) { objectN[_i] = arguments[_i]; } var clone = function (v) { if (isObject(v) && v.constructor) { var r = new v.constructor(); for (var k in v) { r[k] = clone(v[k]); } return r; } return v; }; return objectN.map(function (v) { return clone(v); }) .reduce(function (a, c) { return (_assign(_assign({}, a), c)); }); } /** * Extend target from source object * @param {object} target Target object * @param {object|Array} source Source object * @returns {object} * @private */ function extend(target, source) { if (target === void 0) { target = {}; } if (isArray(source)) { source.forEach(function (v) { return extend(target, v); }); } // exclude name with only numbers for (var p in source) { if (/^\d+$/.test(p) || p in target) { continue; } target[p] = source[p]; } return target; } /** * Return first letter capitalized * @param {string} str Target string * @returns {string} capitalized string * @private */ var capitalize = function (str) { return str.charAt(0).toUpperCase() + str.slice(1); }; /** * Camelize from kebob style string * @param {string} str Target string * @param {string} separator Separator string * @returns {string} camelized string * @private */ function camelize(str, separator) { if (separator === void 0) { separator = "-"; } return str.split(separator) .map(function (v, i) { return (i ? v.charAt(0).toUpperCase() + v.slice(1).toLowerCase() : v.toLowerCase()); }) .join(""); } /** * Convert to array * @param {object} v Target to be converted * @returns {Array} * @private */ var toArray = function (v) { return [].slice.call(v); }; /** * Get css rules for specified stylesheets * @param {Array} styleSheets The stylesheets to get the rules from * @returns {Array} * @private */ function getCssRules(styleSheets) { var rules = []; styleSheets.forEach(function (sheet) { try { if (sheet.cssRules && sheet.cssRules.length) { rules = rules.concat(toArray(sheet.cssRules)); } } catch (e) { console.error("Error while reading rules from ".concat(sheet.href, ": ").concat(e.toString())); } }); return rules; } /** * Gets the SVGMatrix of an SVGGElement * @param {SVGElement} node Node element * @returns {SVGMatrix} matrix * @private */ function getTranslation(node) { var transform = node ? node.transform : null; var baseVal = transform && transform.baseVal; return baseVal && baseVal.numberOfItems ? baseVal.getItem(0).matrix : { a: 0, b: 0, c: 0, d: 0, e: 0, f: 0 }; } /** * Get unique value from array * @param {Array} data Source data * @returns {Array} Unique array value * @private */ function getUnique(data) { var isDate = data[0] instanceof Date; var d = (isDate ? data.map(Number) : data) .filter(function (v, i, self) { return self.indexOf(v) === i; }); return isDate ? d.map(function (v) { return new Date(v); }) : d; } /** * Merge array * @param {Array} arr Source array * @returns {Array} * @private */ function mergeArray(arr) { return arr && arr.length ? arr.reduce(function (p, c) { return p.concat(c); }) : []; } /** * Merge object returning new object * @param {object} target Target object * @param {object} objectN Source object * @returns {object} merged target object * @private */ function mergeObj(target) { var objectN = []; for (var _i = 1; _i < arguments.length; _i++) { objectN[_i - 1] = arguments[_i]; } if (!objectN.length || (objectN.length === 1 && !objectN[0])) { return target; } var source = objectN.shift(); if (isObject(target) && isObject(source)) { Object.keys(source).forEach(function (key) { var value = source[key]; if (isObject(value)) { !target[key] && (target[key] = {}); target[key] = mergeObj(target[key], value); } else { target[key] = isArray(value) ? value.concat() : value; } }); } return mergeObj.apply(void 0, __spreadArray([target], objectN, false)); } /** * Sort value * @param {Array} data value to be sorted * @param {boolean} isAsc true: asc, false: desc * @returns {number|string|Date} sorted date * @private */ function sortValue(data, isAsc) { if (isAsc === void 0) { isAsc = true; } var fn; if (data[0] instanceof Date) { fn = isAsc ? function (a, b) { return a - b; } : function (a, b) { return b - a; }; } else { if (isAsc && !data.every(isNaN)) { fn = function (a, b) { return a - b; }; } else if (!isAsc) { fn = function (a, b) { return (a > b && -1) || (a < b && 1) || (a === b && 0); }; } } return data.concat().sort(fn); } /** * Get min/max value * @param {string} type 'min' or 'max' * @param {Array} data Array data value * @returns {number|Date|undefined} * @private */ function getMinMax$1(type, data) { var res = data.filter(function (v) { return notEmpty(v); }); if (res.length) { if (isNumber(res[0])) { res = Math[type].apply(Math, res); } else if (res[0] instanceof Date) { res = sortValue(res, type === "min")[0]; } } else { res = undefined; } return res; } /** * Get range * @param {number} start Start number * @param {number} end End number * @param {number} step Step number * @returns {Array} * @private */ var getRange = function (start, end, step) { if (step === void 0) { step = 1; } var res = []; var n = Math.max(0, Math.ceil((end - start) / step)) | 0; for (var i = start; i < n; i++) { res.push(start + i * step); } return res; }; // emulate event var emulateEvent = { mouse: (function () { var getParams = function () { return ({ bubbles: false, cancelable: false, screenX: 0, screenY: 0, clientX: 0, clientY: 0 }); }; try { // eslint-disable-next-line no-new new MouseEvent("t"); return function (el, eventType, params) { if (params === void 0) { params = getParams(); } el.dispatchEvent(new MouseEvent(eventType, params)); }; } catch (e) { // Polyfills DOM4 MouseEvent return function (el, eventType, params) { if (params === void 0) { params = getParams(); } var mouseEvent = doc.createEvent("MouseEvent"); // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent mouseEvent.initMouseEvent(eventType, params.bubbles, params.cancelable, win, 0, // the event's mouse click count params.screenX, params.screenY, params.clientX, params.clientY, false, false, false, false, 0, null); el.dispatchEvent(mouseEvent); }; } })(), touch: function (el, eventType, params) { var touchObj = new Touch(mergeObj({ identifier: Date.now(), target: el, radiusX: 2.5, radiusY: 2.5, rotationAngle: 10, force: 0.5 }, params)); el.dispatchEvent(new TouchEvent(eventType, { cancelable: true, bubbles: true, shiftKey: true, touches: [touchObj], targetTouches: [], changedTouches: [touchObj] })); } }; /** * Process the template & return bound string * @param {string} tpl Template string * @param {object} data Data value to be replaced * @returns {string} * @private */ function tplProcess(tpl, data) { var res = tpl; for (var x in data) { res = res.replace(new RegExp("{=".concat(x, "}"), "g"), data[x]); } return res; } /** * Get parsed date value * (It must be called in 'ChartInternal' context) * @param {Date|string|number} date Value of date to be parsed * @returns {Date} * @private */ function parseDate(date) { var parsedDate; if (date instanceof Date) { parsedDate = date; } else if (isString(date)) { var _a = this, config = _a.config, format = _a.format; parsedDate = format.dataTime(config.data_xFormat)(date); } else if (isNumber(date) && !isNaN(date)) { parsedDate = new Date(+date); } if (!parsedDate || isNaN(+parsedDate)) { console && console.error && console.error("Failed to parse x '".concat(date, "' to Date object")); } return parsedDate; } /** * Return if the current doc is visible or not * @returns {boolean} * @private */ function isTabVisible() { return !doc.hidden; } /** * Get the current input type * @param {boolean} mouse Config value: interaction.inputType.mouse * @param {boolean} touch Config value: interaction.inputType.touch * @returns {string} "mouse" | "touch" | null * @private */ function convertInputType(mouse, touch) { var hasTouch = false; if (touch) { // Some Edge desktop return true: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/20417074/ if (win.navigator && "maxTouchPoints" in win.navigator) { hasTouch = win.navigator.maxTouchPoints > 0; // Ref: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js // On IE11 with IE9 emulation mode, ('ontouchstart' in window) is returning true } else if ("ontouchmove" in win || (win.DocumentTouch && doc instanceof win.DocumentTouch)) { hasTouch = true; } else { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#avoiding_user_agent_detection var mQ = win.matchMedia && matchMedia("(pointer:coarse)"); if (mQ && mQ.media === "(pointer:coarse)") { hasTouch = !!mQ.matches; } } } var hasMouse = mouse && !hasTouch ? ("onmouseover" in win) : false; return (hasMouse && "mouse") || (hasTouch && "touch") || null; } /** * Check chart type module imports. * @param {ChartInternal} ctx Context * @private */ function checkModuleImport(ctx) { var $$ = ctx; var config = $$.config; var type = ""; if (isEmpty(config.data_type || config.data_types) && !$$[TYPE_METHOD_NEEDED.LINE]) { type = "line"; } else { for (var x in TYPE_METHOD_NEEDED) { var t = TYPE[x]; if ($$.hasType(t) && !$$[TYPE_METHOD_NEEDED[x]]) { type = t; break; } } } type && logError("Please, make sure if %c".concat(camelize(type)), "module has been imported and specified correctly."); } /** * Log error and throw error * @param {string} head Message header * @param {string} tail Message tail * @private */ function logError(head, tail) { var _a; var prefix = "[billboard.js]"; var info = "https://github.com/naver/billboard.js/wiki/CHANGELOG-v2#modularization-by-its-functionality"; var hasConsole = (_a = win.console) === null || _a === void 0 ? void 0 : _a.error; if (hasConsole) { console.error("\u274C ".concat(prefix, " ").concat(head), "background:red;color:white;display:block;font-size:15px", tail); console.info("%cℹ️", "font-size:15px", info); } throw Error("".concat(prefix, " ").concat(head.replace(/\%c([a-z-]+)/i, "'$1' "), " ").concat(tail)); } /** * Copyright (c) 2017 ~ present NAVER Corp. * billboard.js project is licensed under the MIT license */ /** * CSS class names definition * @private */ var $COMMON = { button: "bb-button", chart: "bb-chart", empty: "bb-empty", main: "bb-main", target: "bb-target", EXPANDED: "_expanded_" }; var $ARC = { arc: "bb-arc", arcLabelLine: "bb-arc-label-line", arcs: "bb-arcs", chartArc: "bb-chart-arc", chartArcs: "bb-chart-arcs", chartArcsBackground: "bb-chart-arcs-background", chartArcsTitle: "bb-chart-arcs-title" }; var $AREA = { area: "bb-area", areas: "bb-areas" }; var $AXIS = { axis: "bb-axis", axisX: "bb-axis-x", axisXLabel: "bb-axis-x-label", axisY: "bb-axis-y", axisY2: "bb-axis-y2", axisY2Label: "bb-axis-y2-label", axisYLabel: "bb-axis-y-label" }; var $BAR = { bar: "bb-bar", bars: "bb-bars", chartBar: "bb-chart-bar", chartBars: "bb-chart-bars" }; var $CANDLESTICK = { candlestick: "bb-candlestick", candlesticks: "bb-candlesticks", chartCandlestick: "bb-chart-candlestick", chartCandlesticks: "bb-chart-candlesticks", valueDown: "bb-value-down", valueUp: "bb-value-up" }; var $CIRCLE = { chartCircles: "bb-chart-circles", circle: "bb-circle", circles: "bb-circles" }; var $COLOR = { colorPattern: "bb-color-pattern", colorScale: "bb-colorscale" }; var $DRAG = { dragarea: "bb-dragarea", INCLUDED: "_included_" }; var $GAUGE = { chartArcsGaugeMax: "bb-chart-arcs-gauge-max", chartArcsGaugeMin: "bb-chart-arcs-gauge-min", chartArcsGaugeUnit: "bb-chart-arcs-gauge-unit", chartArcsGaugeTitle: "bb-chart-arcs-gauge-title", gaugeValue: "bb-gauge-value" }; var $LEGEND = { legend: "bb-legend", legendBackground: "bb-legend-background", legendItem: "bb-legend-item", legendItemEvent: "bb-legend-item-event", legendItemHidden: "bb-legend-item-hidden", legendItemPoint: "bb-legend-item-point", legendItemTile: "bb-legend-item-tile" }; var $LINE = { chartLine: "bb-chart-line", chartLines: "bb-chart-lines", line: "bb-line", lines: "bb-lines" }; var $EVENT = { eventRect: "bb-event-rect", eventRects: "bb-event-rects", eventRectsMultiple: "bb-event-rects-multiple", eventRectsSingle: "bb-event-rects-single" }; var $FOCUS = { focused: "bb-focused", defocused: "bb-defocused", legendItemFocused: "bb-legend-item-focused", xgridFocus: "bb-xgrid-focus", ygridFocus: "bb-ygrid-focus" }; var $GRID = { grid: "bb-grid", gridLines: "bb-grid-lines", xgrid: "bb-xgrid", xgridLine: "bb-xgrid-line", xgridLines: "bb-xgrid-lines", xgrids: "bb-xgrids", ygrid: "bb-ygrid", ygridLine: "bb-ygrid-line", ygridLines: "bb-ygrid-lines", ygrids: "bb-ygrids" }; var $RADAR = { chartRadar: "bb-chart-radar", chartRadars: "bb-chart-radars", level: "bb-level", levels: "bb-levels" }; var $REGION = { region: "bb-region", regions: "bb-regions" }; var $SELECT = { selectedCircle: "bb-selected-circle", selectedCircles: "bb-selected-circles", SELECTED: "_selected_" }; var $SHAPE = { shape: "bb-shape", shapes: "bb-shapes" }; var $SUBCHART = { brush: "bb-brush", subchart: "bb-subchart" }; var $TEXT = { chartText: "bb-chart-text", chartTexts: "bb-chart-texts", text: "bb-text", texts: "bb-texts", title: "bb-title", TextOverlapping: "text-overlapping" }; var $TOOLTIP = { tooltip: "bb-tooltip", tooltipContainer: "bb-tooltip-container", tooltipName: "bb-tooltip-name" }; var $ZOOM = { buttonZoomReset: "bb-zoom-reset", zoomBrush: "bb-zoom-brush" }; var CLASS = _assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign(_assign({}, $COMMON), $ARC), $AREA), $AXIS), $BAR), $CANDLESTICK), $CIRCLE), $COLOR), $DRAG), $GAUGE), $LEGEND), $LINE), $EVENT), $FOCUS), $GRID), $RADAR), $REGION), $SELECT), $SHAPE), $SUBCHART), $TEXT), $TOOLTIP), $ZOOM); /** * Elements class. * @class Elements * @ignore * @private */ var Element = /** @class */ (function () { function Element() { var element = { chart: null, main: null, svg: null, axis: { x: null, y: null, y2: null, subX: null }, defs: null, tooltip: null, legend: null, title: null, subchart: { main: null, bar: null, line: null, area: null // $$.contextArea }, arcs: null, bar: null, candlestick: null, line: null, area: null, circle: null, radar: null, text: null, grid: { main: null, x: null, y: null }, gridLines: { main: null, x: null, y: null }, region: { main: null, list: null // mainRegion }, eventRect: null, zoomResetBtn: null // drag zoom reset button }; return element; } return Element; }()); /** * Copyright (c) 2017 ~ present NAVER Corp. * billboard.js project is licensed under the MIT license */ /** * State class. * @class State * @ignore * @private */ var State = /** @class */ (function () { function State() { return { // chart drawn area dimension, excluding axes width: 0, width2: 0, height: 0, height2: 0, margin: { top: 0, bottom: 0, left: 0, right: 0 }, margin2: { top: 0, bottom: 0, left: 0, right: 0 }, margin3: { top: 0, bottom: 0, left: 0, right: 0 }, arcWidth: 0, arcHeight: 0, xAxisHeight: 0, hasAxis: false, hasRadar: false, current: { // chart whole dimension width: 0, height: 0, dataMax: 0, maxTickWidths: { x: { size: 0, ticks: [], clipPath: 0, domain: "" }, y: { size: 0, domain: "" }, y2: { size: 0, domain: "" } }, // current used chart type list types: [] }, // legend isLegendRight: false, isLegendInset: false, isLegendTop: false, isLegendLeft: false, legendStep: 0, legendItemWidth: 0, legendItemHeight: 0, legendHasRendered: false, eventReceiver: { currentIdx: -1, rect: {}, data: [], coords: [] // coordination value of previous eventRect }, axis: { x: { padding: { left: 0, right: 0 }, tickCount: 0 } }, rotatedPadding: { left: 30, right: 0, top: 5 }, withoutFadeIn: {}, inputType: "", datetimeId: "", // clip id string clip: { id: "", idXAxis: "", idYAxis: "", idXAxisTickTexts: "", idGrid: "", idSubchart: "", path: "", pathXAxis: "", pathYAxis: "", pathXAxisTickTexts: "", pathGrid: "" }, // status event: null, dragStart: null, dragging: false, flowing: false, cancelClick: false, mouseover: false, rendered: false, transiting: false, redrawing: false, resizing: false, toggling: false, zooming: false, hasNegativeValue: false, hasPositiveValue: true, orgAreaOpacity: "0.2", // ID strings hiddenTargetIds: [], hiddenLegendIds: [], focusedTargetIds: [], defocusedTargetIds: [], // value for Arc radius: 0, innerRadius: 0, outerRadius: undefined, innerRadiusRatio: 0, gaugeArcWidth: 0, radiusExpanded: 0, // xgrid attribute xgridAttr: { x1: null, x2: null, y1: null, y2: null } }; } return State; }()); /** * Copyright (c) 2017 ~ present NAVER Corp. * billboard.js project is licensed under the MIT license */ // mapping var classes = { element: Element, state: State }; /** * Internal store class. * @class Store * @ignore * @private */ var Store = /** @class */ (function () { function Store() { var _this = this; Object.keys(classes).forEach(function (v) { _this[v] = new classes[v](); }); } Store.prototype.getStore = function (name) { return this[name]; }; return Store; }()); /** * Copyright (c) 2017 ~ present NAVER Corp. * billboard.js project is licensed under the MIT license */ /** * main config options */ var main = { /** * Specify the CSS selector or the element which the chart will be set to. D3 selection object can be specified also.<br> * If other chart is set already, it will be replaced with the new one (only one chart can be set in one element). * - **NOTE:** In case of element doesn't exist or not specified, will add a `<div>` element to the body. * @name bindto * @memberof Options * @property {string|HTMLElement|d3.selection|object} [bindto="#chart"] Specify the element where chart will be drawn. * @property {string|HTMLElement|d3.selection} bindto.element="#chart" Specify the element where chart will be drawn. * @property {string} [bindto.classname=bb] Specify the class name of bind element.<br> * **NOTE:** When class name isn't `bb`, then you also need to update the default CSS to be rendered correctly. * @default #chart * @example * bindto: "#myContainer" * * // or HTMLElement * bindto: document.getElementById("myContainer") * * // or D3 selection object * bindto: d3.select("#myContainer") * * // or to change default classname * bindto: { * element: "#chart", * classname: "bill-board" // ex) <div id='chart' class='bill-board'> * } */ bindto: "#chart", /** * Set chart background. * @name background * @memberof Options * @property {object} background background object * @property {string} background.class Specify the class name for background element. * @property {string} background.color Specify the fill color for background element.<br>**NOTE:** Will be ignored if `imgUrl` option is set. * @property {string} background.imgUrl Specify the image url string for background. * @see [Demo](https://naver.github.io/billboard.js/demo/#ChartOptions.Background) * @example * background: { * class: "myClass", * color: "red", * * // Set image url for background. * // If specified, 'color' option will be ignored. * imgUrl: "https://naver.github.io/billboard.js/img/logo/billboard.js.svg", * } */ background: {}, /** * Set 'clip-path' attribute for chart element * - **NOTE:** * > When is false, chart node element is positioned after the axis node in DOM tree hierarchy. * > Is to make chart element positioned over axis element. * @name clipPath * @memberof Options * @type {boolean} * @default true * @see [Demo](https://naver.github.io/billboard.js/demo/#ChartOptions.clipPath) * @example * // don't set 'clip-path' attribute * clipPath: false */ clipPath: true, /** * Set svg element's class name * @name svg * @memberof Options * @type {object} * @property {object} [svg] svg object * @property {string} [svg.classname] class name for svg element * @example * svg: { * classname: "test_class" * } */ svg_classname: undefined, /** * The desired size of the chart element. * If value is not specified, the width of the chart will be calculated by the size of the parent element it's appended to. * @name size * @memberof Options * @type {object} * @property {object} [size] size object * @property {number} [size.width] width of the chart element * @property {number} [size.height] height of the chart element * @see [Demo](https://naver.github.io/billboard.js/demo/#ChartOptions.ChartSize) * @example * size: { * width: 640, * height: 480 * } */ size_width: undefined, size_height: undefined, /** * The padding of the chart element. * @name padding * @memberof Options * @type {object} * @property {object|boolean} [padding=true] Set padding of chart, and accepts object or boolean type. * - `Object`: Specify each side's padding. * - `false`: Remove padding completely and make shape to fully occupy the container element. * - In this case, axes and subchart will be hidden. * - To adjust some padding from this state, use `axis.[x|y].padding` option. * @property {number} [padding.top] padding on the top of chart * @property {number} [padding.right] padding on the right of chart * @property {number} [padding.bottom] padding on the bottom of chart * @property {number} [padding.left] padding on the left of chart * @see [Demo](https://naver.github.io/billboard.js/demo/#ChartOptions.Padding) * @example * // remove padding completely. * padding: false, * * // or specify padding value for each side * padding: { * top: 20, * right: 20, * bottom: 20, * left: 20 * } */ padding: true, padding_left: undefined, padding_right: undefined, padding_top: undefined, padding_bottom: undefined, /** * Set chart resize options * @name resize * @memberof Options * @type {object} * @property {object} [resize] resize object * @property {boolean} [resize.auto=true] Set chart resize automatically on viewport changes. * @example * resize: { * auto: false * } */ resize_auto: true, /** * Set a callback to execute when mouse/touch enters the chart. * @name onover * @memberof Options * @type {Function} * @default undefined * @example * onover: function() { * this; // chart instance itself * ... * } */ onover: undefined, /** * Set a callback to execute when mouse/touch leaves the chart. * @name onout * @memberof Options * @type {Function} * @default undefined * @example * onout: function() { * this; // chart instance itself * ... * } */ onout: undefined, /** * Set a callback to execute when user resizes the screen. * @name onresize * @memberof Options * @type {Function} * @default undefined * @example * onresize: function() { * this; // chart instance itself * ... * } */ onresize: undefined, /** * Set a callback to execute when screen resize finished. * @name onresized * @memberof Options * @type {Function} * @default undefined * @example * onresized: function() { * this; // chart instance itself * ... * } */ onresized: undefined, /** * Set a callback to execute before the chart is initialized * @name onbeforeinit * @memberof Options * @type {Function} * @default undefined * @example * onbeforeinit: function() { * this; // chart instance itself * ... * } */ onbeforeinit: undefined, /** * Set a callback to execute when the chart is initialized. * @name oninit * @memberof Options * @type {Function} * @default undefined * @example * oninit: function() { * this; // chart instance itself * ... * } */ oninit: undefined, /** * Set a callback to execute after the chart is initialized * @name onafterinit * @memberof Options * @type {Function} * @default undefined * @example * onafterinit: function() { * this; // chart instance itself * ... * } */ onafterinit: undefined, /** * Set a callback which is executed when the chart is rendered. Basically, this callback will be called in each time when the chart is redrawed. * @name onrendered * @memberof Options * @type {Function} * @default undefined * @example * onrendered: function() { * this; // chart instance itself * ... * } */ onrendered: undefined, /** * Set duration of transition (in milliseconds) for chart animation.<br><br> * - **NOTE:** If `0 `or `null` set, transition will be skipped. So, this makes initial rendering faster especially in case you have a lot of data. * @name transition * @memberof Options * @type {object} * @property {object} [transition] transition object * @property {number} [transition.duration=350] duration in milliseconds * @example * transition: { * duration: 500 * } */ transition_duration: 250, /** * Set plugins * @name plugins * @memberof Options * @type {Array} * @example * plugins: [ * new bb.plugin.stanford({ ... }), * new PluginA(), * ... * ] */ plugins: [], /** * Control the render timing * @name render * @memberof Options * @type {object} * @property {object} [render] render object * @property {boolean} [render.lazy=true] Make to not render at initialization (enabled by default when bind element's visibility is hidden). * @property {boolean} [render.observe=true] Observe bind element's visibility(`display` or `visiblity` inline css property or class value) & render when is visible automatically (for IEs, only works IE11+). When set to **false**, call [`.flush()`](./Chart.html#flush) to render. * @see [Demo](https://naver.github.io/billboard.js/demo/#ChartOptions.LazyRender) * @example * render: { * lazy: true, * observe: true * } * * @example * // <!-- render.lazy will detect visibility defined --> * // (a) <div id='chart' class='hide'></div> * // (b) <div id='chart' style='display:none'></div> * * // render.lazy enabled by default when element is hidden * var chart = bb.generate({ ... }); * * // chart will be rendered automatically when element's visibility changes * // Note: works only for inlined css property or class attribute changes * document.getElementById('chart').classList.remove('hide') // (a) * document.getElementById('chart').style.display = 'block'; // (b) * * @example * // chart won't be rendered and not observing bind element's visiblity changes * var chart = bb.generate({ * render: { * lazy: true, * observe: false * } * }); * * // call at any point when you want to render * chart.flush(); */ render: {}, /** * Show rectangles inside the chart.<br><br> * This option accepts array including object that has axis, start, end and class. * The keys start, end and class are optional. * axis must be x, y or y2. start and end should be the value where regions start and end. * If not specified, the edge values will be used. * If timeseries x axis, date string, Date object and unixtime integer can be used. * If class is set, the region element will have it as class. * @name regions * @memberof Options * @type {Array} * @default [] * @example * regions: [ * { * axis: "x", * start: 1, * end: 4, * class: "region-1-4" * } * ] */ regions: [] }; /** * data config options */ var data$2 = { /** * Specify the key of x values in the data.<br><br> * We can show the data with non-index x values by this option. This option is required when the type of x axis is timeseries. If this option is set on category axis, the values of the data on the key will be used for category names. * @name data․x * @memberof Options * @type {string} * @default undefined * @example * data: { * x: "date" * } */ data_x: undefined, /** * Converts data id value * @name data․idConverter * @memberof Options * @type {Function} * @default function(id) { return id; } * @example * data: { * idConverter: function(id) { * // when id is 'data1', converts to be 'data2' * // 'data2' should be given as the initial data value * if (id === "data1") { * return "data2"; * } else { * return id; * } * } * } */ data_idConverter: function (id) { return id; }, /** * Set custom data name. * @name data․names * @memberof Options * @type {object} * @default {} * @see [Demo](https://naver.github.io/billboard.js/demo/#Data.DataName) * @example * data: { * names: { * data1: "Data Name 1", * data2: "Data Name 2" * } * } */ data_names: {}, /** * Set custom data class.<br><br> * If this option is specified, the element g for the data has an additional class that has the prefix 'bb-target-' (eg. bb-target-additional-data1-class). * @name data․classes * @memberof Options * @type {object} * @default {} * @example * data: { * classes: { * data1: "additional-data1-class", * data2: "additional-data2-class" * } * } */ data_classes: {}, /** * Set chart type at once.<br><br> * If this option is specified, the type will be applied to every data. This setting can be overwritten by data.types.<br><br> * **Available Values:** * - area * - area-line-range * - area-spline * - area-spline-range * - area-step * - bar * - bubble * - candlestick * - donut * - gauge * - line * - pie * - radar * - scatter * - spline * - step * @name data․type * @memberof Options * @type {string} * @default "line"<br>NOTE: When importing shapes by ESM, `line()` should be specified for t