chartjs-chart-treemap
Advanced tools
Comparing version 2.1.2 to 2.1.3
/*! | ||
* chartjs-chart-treemap v2.1.2 | ||
* chartjs-chart-treemap v2.1.3 | ||
* https://chartjs-chart-treemap.pages.dev/ | ||
@@ -8,14 +8,4 @@ * (c) 2022 Jukka Kurkela | ||
import { Element, Chart, registry, DatasetController } from 'chart.js'; | ||
import { isObject, addRoundedRectPath, toFont, isArray, toTRBL, toTRBLCorners, valueOrDefault, defined, clipArea, unclipArea } from 'chart.js/helpers'; | ||
import { isObject, addRoundedRectPath, defined, toFont, isArray, toTRBL, toTRBLCorners, valueOrDefault, clipArea, unclipArea } from 'chart.js/helpers'; | ||
const getValue = (item) => isObject(item) ? item.v : item; | ||
const maxValue = (data) => data.reduce(function(m, v) { | ||
return (m > getValue(v) ? m : getValue(v)); | ||
}, 0); | ||
const minValue = (data, mx) => data.reduce(function(m, v) { | ||
return (m < getValue(v) ? m : getValue(v)); | ||
}, mx); | ||
const getGroupKey = (lvl) => '' + lvl; | ||
@@ -196,14 +186,2 @@ | ||
const rasterize = (v, dpr) => Math.round(v * dpr) / dpr; | ||
function rasterizeRect(rect, dpr) { | ||
return { | ||
...rect, | ||
x: rasterize(rect.x, dpr), | ||
y: rasterize(rect.y, dpr), | ||
w: rasterize(rect.w, dpr), | ||
h: rasterize(rect.h, dpr), | ||
}; | ||
} | ||
const widthCache = new Map(); | ||
@@ -250,3 +228,3 @@ | ||
function boundingRects(rect, dpr) { | ||
function boundingRects(rect) { | ||
const bounds = getBounds(rect); | ||
@@ -257,3 +235,3 @@ const width = bounds.right - bounds.left; | ||
const radius = parseBorderRadius(rect.options.borderRadius, width / 2, height / 2); | ||
const outer = rasterizeRect({ | ||
const outer = { | ||
x: bounds.left, | ||
@@ -265,3 +243,3 @@ y: bounds.top, | ||
radius | ||
}, dpr); | ||
}; | ||
@@ -326,3 +304,3 @@ return { | ||
ctx.clip(); | ||
const isLeaf = (!('l' in item) || item.l === levels); | ||
const isLeaf = item && (!defined(item.l) || item.l === levels); | ||
if (isLeaf && labels.display) { | ||
@@ -481,3 +459,3 @@ drawLabel(ctx, rect, options); | ||
draw(ctx, data, levels = 0, dpr) { | ||
draw(ctx, data, levels = 0) { | ||
if (!data) { | ||
@@ -487,3 +465,3 @@ return; | ||
const options = this.options; | ||
const {inner, outer} = boundingRects(this, dpr); | ||
const {inner, outer} = boundingRects(this); | ||
@@ -602,7 +580,7 @@ const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath; | ||
function getDims(itm, w2, s2, key, dpr, maxd2) { | ||
function getDims(itm, w2, s2, key) { | ||
const a = itm._normalized; | ||
const ar = w2 * a / s2; | ||
const d1 = rasterize(Math.sqrt(a * ar), dpr); | ||
const d2 = Math.min(rasterize(a / d1, dpr), maxd2); | ||
const d1 = Math.sqrt(a * ar); | ||
const d2 = a / d1; | ||
const w = key === '_ix' ? d1 : d2; | ||
@@ -616,4 +594,4 @@ const h = key === '_ix' ? d2 : d1; | ||
function buildRow(rect, itm, dims, sum, dpr) { | ||
const r = rasterizeRect({ | ||
function buildRow(rect, itm, dims, sum) { | ||
const r = { | ||
x: getX(rect, dims.w), | ||
@@ -627,3 +605,3 @@ y: rect.y + rect._iy, | ||
_data: itm._data | ||
}, dpr); | ||
}; | ||
if (itm.group) { | ||
@@ -638,4 +616,3 @@ r.g = itm.group; | ||
class Rect { | ||
constructor(r, dpr) { | ||
this.dpr = dpr; | ||
constructor(r) { | ||
r = r || {w: 1, h: 1}; | ||
@@ -669,12 +646,11 @@ this.rtl = !!r.rtl; | ||
get side() { | ||
return rasterize(this.dir === 'x' ? this.iw : this.ih, this.dpr); | ||
return this.dir === 'x' ? this.iw : this.ih; | ||
} | ||
map(arr) { | ||
const {dir, side, dpr} = this; | ||
const {key, d2prop, id2prop, d2key} = getPropNames(dir); | ||
const {dir, side} = this; | ||
const key = dir === 'x' ? '_ix' : '_iy'; | ||
const sum = arr.nsum; | ||
const row = arr.get(); | ||
const w2 = side * side; | ||
const availd2 = rasterize(this[id2prop], dpr); | ||
const s2 = sum * sum; | ||
@@ -685,20 +661,10 @@ const ret = []; | ||
for (const itm of row) { | ||
const dims = getDims(itm, w2, s2, key, dpr, availd2); | ||
const dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
maxd2 = Math.min(Math.max(maxd2, dims.d2), availd2); | ||
ret.push(buildRow(this, itm, dims, arr.sum, dpr)); | ||
maxd2 = Math.max(maxd2, dims.d2); | ||
ret.push(buildRow(this, itm, dims, arr.sum)); | ||
this[key] += dims.d1; | ||
} | ||
// make sure all rects are same size in d2 direction | ||
for (const r of ret) { | ||
const d2 = r[d2prop]; | ||
if (d2 !== maxd2) { | ||
if (this.rtl && dir === 'y') { | ||
// for RTL, x-coordinate needs to be adjusted too | ||
r.x -= maxd2 - d2; | ||
} | ||
r[d2prop] = maxd2; | ||
} | ||
} | ||
this[d2key] += maxd2; | ||
this[dir === 'x' ? '_iy' : '_ix'] += maxd2; | ||
this[key] -= totd1; | ||
@@ -709,19 +675,2 @@ return ret; | ||
function getPropNames(dir) { | ||
if (dir === 'x') { | ||
return { | ||
key: '_ix', | ||
d2prop: 'h', | ||
id2prop: 'ih', | ||
d2key: '_iy' | ||
}; | ||
} | ||
return { | ||
key: '_iy', | ||
d2prop: 'w', | ||
id2prop: 'iw', | ||
d2key: '_ix' | ||
}; | ||
} | ||
const min = Math.min; | ||
@@ -825,11 +774,10 @@ const max = Math.max; | ||
* @param {string} [key] | ||
* @param {number} [dpr=1] | ||
* @param {*} [grp] | ||
* @param {*} [lvl] | ||
* @param {*} [gsum] | ||
* @param {string} [grp] | ||
* @param {number} [lvl] | ||
* @param {number} [gsum] | ||
*/ | ||
function squarify(values, rectangle, key, dpr = 1, grp, lvl, gsum) { | ||
function squarify(values, rectangle, key, grp, lvl, gsum) { | ||
values = values || []; | ||
const rows = []; | ||
const rect = new Rect(rectangle, dpr); | ||
const rect = new Rect(rectangle); | ||
const row = new StatArray('value', rect.area / sum(values, key)); | ||
@@ -871,4 +819,19 @@ let length = rect.side; | ||
var version = "2.1.2"; | ||
var version = "2.1.3"; | ||
function scaleRect(sq, xScale, yScale, sp) { | ||
const sp2 = sp * 2; | ||
const x = xScale.getPixelForValue(sq.x); | ||
const y = yScale.getPixelForValue(sq.y); | ||
const w = xScale.getPixelForValue(sq.x + sq.w) - x; | ||
const h = yScale.getPixelForValue(sq.y + sq.h) - y; | ||
return { | ||
x: x + sp, | ||
y: y + sp, | ||
width: w - sp2, | ||
height: h - sp2, | ||
hidden: sp2 > w || sp2 > h, | ||
}; | ||
} | ||
function rectNotEqual(r1, r2) { | ||
@@ -879,14 +842,23 @@ return !r1 || !r2 | ||
|| r1.w !== r2.w | ||
|| r1.h !== r2.h; | ||
|| r1.h !== r2.h | ||
|| r1.rtl !== r2.rtl; | ||
} | ||
function arrayNotEqual(a1, a2) { | ||
function arrayNotEqual(a, b) { | ||
let i, n; | ||
if (a1.lenght !== a2.length) { | ||
if (!a || !b) { | ||
return true; | ||
} | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
if (a === b) { | ||
return false; | ||
} | ||
if (a.length !== b.length) { | ||
return true; | ||
} | ||
for (i = 0, n = a.length; i < n; ++i) { | ||
if (a[i] !== b[i]) { | ||
return true; | ||
@@ -898,6 +870,5 @@ } | ||
function buildData(dataset, mainRect, dpr) { | ||
function buildData(tree, dataset, mainRect) { | ||
const key = dataset.key || ''; | ||
const treeLeafKey = dataset.treeLeafKey || '_leaf'; | ||
let tree = dataset.tree || []; | ||
if (isObject(tree)) { | ||
@@ -917,3 +888,3 @@ tree = normalizeTreeToArray(key, treeLeafKey, tree); | ||
const gdata = group(tree, g, key, treeLeafKey, pg, parent, groups.filter((item, index) => index <= gidx)); | ||
const gsq = squarify(gdata, rect, key, dpr, g, gidx, gs); | ||
const gsq = squarify(gdata, rect, key, g, gidx, gs); | ||
const ret = gsq.slice(); | ||
@@ -934,3 +905,3 @@ if (gidx < glen - 1) { | ||
} | ||
ret.push(...recur(gidx + 1, rasterizeRect(subRect, dpr), sq.g, sq.s)); | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
@@ -941,26 +912,7 @@ } | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
return glen | ||
? recur(0, mainRect) | ||
: squarify(tree, mainRect, key, dpr); | ||
: squarify(tree, mainRect, key); | ||
} | ||
function getMinMax(data, useTree) { | ||
const vMax = useTree ? 1 : maxValue(data); | ||
const vMin = useTree ? 0 : minValue(data, vMax); | ||
return {vMin, vMax}; | ||
} | ||
function getArea({xScale, yScale}, data, rtl, useTree) { | ||
const {vMin, vMax} = getMinMax(data, useTree); | ||
const xMin = xScale.getPixelForValue(0); | ||
const xMax = xScale.getPixelForValue(1); | ||
const yMin = yScale.getPixelForValue(vMin); | ||
const yMax = yScale.getPixelForValue(vMax); | ||
return {x: xMin, y: yMax, w: xMax - xMin, h: yMin - yMax, rtl}; | ||
} | ||
class TreemapController extends DatasetController { | ||
@@ -970,6 +922,6 @@ constructor(chart, datasetIndex) { | ||
this._groups = undefined; | ||
this._key = undefined; | ||
this._rect = undefined; | ||
this._key = undefined; | ||
this._groups = undefined; | ||
this._useTree = undefined; | ||
this._rectChanged = true; | ||
} | ||
@@ -982,41 +934,53 @@ | ||
/** | ||
* @todo: Remove with https://github.com/kurkle/chartjs-chart-treemap/issues/137 | ||
*/ | ||
updateRangeFromParsed(range, scale) { | ||
if (range.updated) { | ||
getMinMax(scale) { | ||
return { | ||
min: 0, | ||
max: scale.axis === 'x' ? scale.right - scale.left : scale.bottom - scale.top | ||
}; | ||
} | ||
configure() { | ||
super.configure(); | ||
const {xScale, yScale} = this.getMeta(); | ||
if (!xScale || !yScale) { | ||
// configure is called once before `linkScales`, and at that call we don't have any scales linked yet | ||
return; | ||
} | ||
range.updated = true; | ||
if (scale.axis === 'x') { | ||
range.min = 0; | ||
range.max = 1; | ||
return; | ||
const w = xScale.right - xScale.left; | ||
const h = yScale.bottom - yScale.top; | ||
const rect = {x: 0, y: 0, w, h, rtl: !!this.options.rtl}; | ||
if (rectNotEqual(this._rect, rect)) { | ||
this._rect = rect; | ||
this._rectChanged = true; | ||
} | ||
const dataset = this.getDataset(); | ||
const {vMin, vMax} = getMinMax(dataset.data, this._useTree); | ||
range.min = vMin; | ||
range.max = vMax; | ||
if (this._rectChanged) { | ||
xScale.max = w; | ||
xScale.configure(); | ||
yScale.max = h; | ||
yScale.configure(); | ||
} | ||
} | ||
update(mode) { | ||
const meta = this.getMeta(); | ||
const dataset = this.getDataset(); | ||
const dpr = this.chart.currentDevicePixelRatio; | ||
const {data} = this.getMeta(); | ||
const groups = dataset.groups || (dataset.groups = []); | ||
const key = dataset.key; | ||
const tree = dataset.tree = dataset.tree || dataset.data || []; | ||
if (!defined(this._useTree)) { | ||
this._useTree = !!dataset.tree; | ||
if (mode === 'reset') { | ||
// reset is called before 2nd configure and is only called if animations are enabled. So wen need an extra configure call here. | ||
this.configure(); | ||
} | ||
const groups = dataset.groups || (dataset.groups = []); | ||
const key = dataset.key || ''; | ||
const rtl = !!dataset.rtl; | ||
const mainRect = rasterizeRect(getArea(meta, dataset.data, rtl, this._useTree), dpr); | ||
if (mode === 'reset' || rectNotEqual(this._rect, mainRect) || this._key !== key || arrayNotEqual(this._groups, groups)) { | ||
this._rect = mainRect; | ||
if (this._rectChanged || this._key !== key || arrayNotEqual(this._groups, groups) || this._prevTree !== tree) { | ||
this._groups = groups.slice(); | ||
this._key = key; | ||
this._prevTree = tree; | ||
this._rectChanged = false; | ||
dataset.data = buildData(dataset, mainRect, dpr); | ||
dataset.data = buildData(tree, dataset, this._rect); | ||
// @ts-ignore using private stuff | ||
@@ -1028,25 +992,20 @@ this._dataCheck(); | ||
this.updateElements(meta.data, 0, meta.data.length, mode); | ||
this.updateElements(data, 0, data.length, mode); | ||
} | ||
updateElements(rects, start, count, mode) { | ||
const me = this; | ||
const reset = mode === 'reset'; | ||
const dataset = me.getDataset(); | ||
const firstOpts = me._rect.options = me.resolveDataElementOptions(start, mode); | ||
const sharedOptions = me.getSharedOptions(firstOpts); | ||
const includeOptions = me.includeOptions(mode, sharedOptions); | ||
const dataset = this.getDataset(); | ||
const firstOpts = this._rect.options = this.resolveDataElementOptions(start, mode); | ||
const sharedOptions = this.getSharedOptions(firstOpts); | ||
const includeOptions = this.includeOptions(mode, sharedOptions); | ||
const {xScale, yScale} = this.getMeta(this.index); | ||
for (let i = start; i < start + count; i++) { | ||
const sq = dataset.data[i]; | ||
const options = sharedOptions || me.resolveDataElementOptions(i, mode); | ||
const sp = options.spacing; | ||
const sp2 = sp * 2; | ||
const properties = { | ||
x: sq.x + sp, | ||
y: sq.y + sp, | ||
width: reset ? 0 : sq.w - sp2, | ||
height: reset ? 0 : sq.h - sp2, | ||
hidden: sp2 > sq.w || sp2 > sq.h, | ||
}; | ||
const options = sharedOptions || this.resolveDataElementOptions(i, mode); | ||
const properties = scaleRect(dataset.data[i], xScale, yScale, options.spacing); | ||
if (reset) { | ||
properties.width = 0; | ||
properties.height = 0; | ||
} | ||
@@ -1056,10 +1015,10 @@ if (includeOptions) { | ||
} | ||
me.updateElement(rects[i], i, properties, mode); | ||
this.updateElement(rects[i], i, properties, mode); | ||
} | ||
me.updateSharedOptions(sharedOptions, mode, firstOpts); | ||
this.updateSharedOptions(sharedOptions, mode, firstOpts); | ||
} | ||
draw() { | ||
const {ctx, chartArea, currentDevicePixelRatio: dpr} = this.chart; | ||
const {ctx, chartArea} = this.chart; | ||
const metadata = this.getMeta().data || []; | ||
@@ -1074,3 +1033,3 @@ const dataset = this.getDataset(); | ||
if (!rect.hidden) { | ||
rect.draw(ctx, data[i], levels, dpr); | ||
rect.draw(ctx, data[i], levels); | ||
} | ||
@@ -1136,2 +1095,4 @@ } | ||
type: 'linear', | ||
alignToPixels: true, | ||
bounds: 'data', | ||
display: false | ||
@@ -1141,3 +1102,6 @@ }, | ||
type: 'linear', | ||
display: false | ||
alignToPixels: true, | ||
bounds: 'data', | ||
display: false, | ||
reverse: true | ||
} | ||
@@ -1144,0 +1108,0 @@ }, |
/*! | ||
* chartjs-chart-treemap v2.1.2 | ||
* chartjs-chart-treemap v2.1.3 | ||
* https://chartjs-chart-treemap.pages.dev/ | ||
@@ -13,12 +13,2 @@ * (c) 2022 Jukka Kurkela | ||
const getValue = (item) => helpers.isObject(item) ? item.v : item; | ||
const maxValue = (data) => data.reduce(function(m, v) { | ||
return (m > getValue(v) ? m : getValue(v)); | ||
}, 0); | ||
const minValue = (data, mx) => data.reduce(function(m, v) { | ||
return (m < getValue(v) ? m : getValue(v)); | ||
}, mx); | ||
const getGroupKey = (lvl) => '' + lvl; | ||
@@ -199,14 +189,2 @@ | ||
const rasterize = (v, dpr) => Math.round(v * dpr) / dpr; | ||
function rasterizeRect(rect, dpr) { | ||
return { | ||
...rect, | ||
x: rasterize(rect.x, dpr), | ||
y: rasterize(rect.y, dpr), | ||
w: rasterize(rect.w, dpr), | ||
h: rasterize(rect.h, dpr), | ||
}; | ||
} | ||
const widthCache = new Map(); | ||
@@ -253,3 +231,3 @@ | ||
function boundingRects(rect, dpr) { | ||
function boundingRects(rect) { | ||
const bounds = getBounds(rect); | ||
@@ -260,3 +238,3 @@ const width = bounds.right - bounds.left; | ||
const radius = parseBorderRadius(rect.options.borderRadius, width / 2, height / 2); | ||
const outer = rasterizeRect({ | ||
const outer = { | ||
x: bounds.left, | ||
@@ -268,3 +246,3 @@ y: bounds.top, | ||
radius | ||
}, dpr); | ||
}; | ||
@@ -329,3 +307,3 @@ return { | ||
ctx.clip(); | ||
const isLeaf = (!('l' in item) || item.l === levels); | ||
const isLeaf = item && (!helpers.defined(item.l) || item.l === levels); | ||
if (isLeaf && labels.display) { | ||
@@ -484,3 +462,3 @@ drawLabel(ctx, rect, options); | ||
draw(ctx, data, levels = 0, dpr) { | ||
draw(ctx, data, levels = 0) { | ||
if (!data) { | ||
@@ -490,3 +468,3 @@ return; | ||
const options = this.options; | ||
const {inner, outer} = boundingRects(this, dpr); | ||
const {inner, outer} = boundingRects(this); | ||
@@ -605,7 +583,7 @@ const addRectPath = hasRadius(outer.radius) ? helpers.addRoundedRectPath : addNormalRectPath; | ||
function getDims(itm, w2, s2, key, dpr, maxd2) { | ||
function getDims(itm, w2, s2, key) { | ||
const a = itm._normalized; | ||
const ar = w2 * a / s2; | ||
const d1 = rasterize(Math.sqrt(a * ar), dpr); | ||
const d2 = Math.min(rasterize(a / d1, dpr), maxd2); | ||
const d1 = Math.sqrt(a * ar); | ||
const d2 = a / d1; | ||
const w = key === '_ix' ? d1 : d2; | ||
@@ -619,4 +597,4 @@ const h = key === '_ix' ? d2 : d1; | ||
function buildRow(rect, itm, dims, sum, dpr) { | ||
const r = rasterizeRect({ | ||
function buildRow(rect, itm, dims, sum) { | ||
const r = { | ||
x: getX(rect, dims.w), | ||
@@ -630,3 +608,3 @@ y: rect.y + rect._iy, | ||
_data: itm._data | ||
}, dpr); | ||
}; | ||
if (itm.group) { | ||
@@ -641,4 +619,3 @@ r.g = itm.group; | ||
class Rect { | ||
constructor(r, dpr) { | ||
this.dpr = dpr; | ||
constructor(r) { | ||
r = r || {w: 1, h: 1}; | ||
@@ -672,12 +649,11 @@ this.rtl = !!r.rtl; | ||
get side() { | ||
return rasterize(this.dir === 'x' ? this.iw : this.ih, this.dpr); | ||
return this.dir === 'x' ? this.iw : this.ih; | ||
} | ||
map(arr) { | ||
const {dir, side, dpr} = this; | ||
const {key, d2prop, id2prop, d2key} = getPropNames(dir); | ||
const {dir, side} = this; | ||
const key = dir === 'x' ? '_ix' : '_iy'; | ||
const sum = arr.nsum; | ||
const row = arr.get(); | ||
const w2 = side * side; | ||
const availd2 = rasterize(this[id2prop], dpr); | ||
const s2 = sum * sum; | ||
@@ -688,20 +664,10 @@ const ret = []; | ||
for (const itm of row) { | ||
const dims = getDims(itm, w2, s2, key, dpr, availd2); | ||
const dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
maxd2 = Math.min(Math.max(maxd2, dims.d2), availd2); | ||
ret.push(buildRow(this, itm, dims, arr.sum, dpr)); | ||
maxd2 = Math.max(maxd2, dims.d2); | ||
ret.push(buildRow(this, itm, dims, arr.sum)); | ||
this[key] += dims.d1; | ||
} | ||
// make sure all rects are same size in d2 direction | ||
for (const r of ret) { | ||
const d2 = r[d2prop]; | ||
if (d2 !== maxd2) { | ||
if (this.rtl && dir === 'y') { | ||
// for RTL, x-coordinate needs to be adjusted too | ||
r.x -= maxd2 - d2; | ||
} | ||
r[d2prop] = maxd2; | ||
} | ||
} | ||
this[d2key] += maxd2; | ||
this[dir === 'x' ? '_iy' : '_ix'] += maxd2; | ||
this[key] -= totd1; | ||
@@ -712,19 +678,2 @@ return ret; | ||
function getPropNames(dir) { | ||
if (dir === 'x') { | ||
return { | ||
key: '_ix', | ||
d2prop: 'h', | ||
id2prop: 'ih', | ||
d2key: '_iy' | ||
}; | ||
} | ||
return { | ||
key: '_iy', | ||
d2prop: 'w', | ||
id2prop: 'iw', | ||
d2key: '_ix' | ||
}; | ||
} | ||
const min = Math.min; | ||
@@ -828,11 +777,10 @@ const max = Math.max; | ||
* @param {string} [key] | ||
* @param {number} [dpr=1] | ||
* @param {*} [grp] | ||
* @param {*} [lvl] | ||
* @param {*} [gsum] | ||
* @param {string} [grp] | ||
* @param {number} [lvl] | ||
* @param {number} [gsum] | ||
*/ | ||
function squarify(values, rectangle, key, dpr = 1, grp, lvl, gsum) { | ||
function squarify(values, rectangle, key, grp, lvl, gsum) { | ||
values = values || []; | ||
const rows = []; | ||
const rect = new Rect(rectangle, dpr); | ||
const rect = new Rect(rectangle); | ||
const row = new StatArray('value', rect.area / sum(values, key)); | ||
@@ -874,4 +822,19 @@ let length = rect.side; | ||
var version = "2.1.2"; | ||
var version = "2.1.3"; | ||
function scaleRect(sq, xScale, yScale, sp) { | ||
const sp2 = sp * 2; | ||
const x = xScale.getPixelForValue(sq.x); | ||
const y = yScale.getPixelForValue(sq.y); | ||
const w = xScale.getPixelForValue(sq.x + sq.w) - x; | ||
const h = yScale.getPixelForValue(sq.y + sq.h) - y; | ||
return { | ||
x: x + sp, | ||
y: y + sp, | ||
width: w - sp2, | ||
height: h - sp2, | ||
hidden: sp2 > w || sp2 > h, | ||
}; | ||
} | ||
function rectNotEqual(r1, r2) { | ||
@@ -882,14 +845,23 @@ return !r1 || !r2 | ||
|| r1.w !== r2.w | ||
|| r1.h !== r2.h; | ||
|| r1.h !== r2.h | ||
|| r1.rtl !== r2.rtl; | ||
} | ||
function arrayNotEqual(a1, a2) { | ||
function arrayNotEqual(a, b) { | ||
let i, n; | ||
if (a1.lenght !== a2.length) { | ||
if (!a || !b) { | ||
return true; | ||
} | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
if (a === b) { | ||
return false; | ||
} | ||
if (a.length !== b.length) { | ||
return true; | ||
} | ||
for (i = 0, n = a.length; i < n; ++i) { | ||
if (a[i] !== b[i]) { | ||
return true; | ||
@@ -901,6 +873,5 @@ } | ||
function buildData(dataset, mainRect, dpr) { | ||
function buildData(tree, dataset, mainRect) { | ||
const key = dataset.key || ''; | ||
const treeLeafKey = dataset.treeLeafKey || '_leaf'; | ||
let tree = dataset.tree || []; | ||
if (helpers.isObject(tree)) { | ||
@@ -920,3 +891,3 @@ tree = normalizeTreeToArray(key, treeLeafKey, tree); | ||
const gdata = group(tree, g, key, treeLeafKey, pg, parent, groups.filter((item, index) => index <= gidx)); | ||
const gsq = squarify(gdata, rect, key, dpr, g, gidx, gs); | ||
const gsq = squarify(gdata, rect, key, g, gidx, gs); | ||
const ret = gsq.slice(); | ||
@@ -937,3 +908,3 @@ if (gidx < glen - 1) { | ||
} | ||
ret.push(...recur(gidx + 1, rasterizeRect(subRect, dpr), sq.g, sq.s)); | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
@@ -944,26 +915,7 @@ } | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
return glen | ||
? recur(0, mainRect) | ||
: squarify(tree, mainRect, key, dpr); | ||
: squarify(tree, mainRect, key); | ||
} | ||
function getMinMax(data, useTree) { | ||
const vMax = useTree ? 1 : maxValue(data); | ||
const vMin = useTree ? 0 : minValue(data, vMax); | ||
return {vMin, vMax}; | ||
} | ||
function getArea({xScale, yScale}, data, rtl, useTree) { | ||
const {vMin, vMax} = getMinMax(data, useTree); | ||
const xMin = xScale.getPixelForValue(0); | ||
const xMax = xScale.getPixelForValue(1); | ||
const yMin = yScale.getPixelForValue(vMin); | ||
const yMax = yScale.getPixelForValue(vMax); | ||
return {x: xMin, y: yMax, w: xMax - xMin, h: yMin - yMax, rtl}; | ||
} | ||
class TreemapController extends chart_js.DatasetController { | ||
@@ -973,6 +925,6 @@ constructor(chart, datasetIndex) { | ||
this._groups = undefined; | ||
this._key = undefined; | ||
this._rect = undefined; | ||
this._key = undefined; | ||
this._groups = undefined; | ||
this._useTree = undefined; | ||
this._rectChanged = true; | ||
} | ||
@@ -985,41 +937,53 @@ | ||
/** | ||
* @todo: Remove with https://github.com/kurkle/chartjs-chart-treemap/issues/137 | ||
*/ | ||
updateRangeFromParsed(range, scale) { | ||
if (range.updated) { | ||
getMinMax(scale) { | ||
return { | ||
min: 0, | ||
max: scale.axis === 'x' ? scale.right - scale.left : scale.bottom - scale.top | ||
}; | ||
} | ||
configure() { | ||
super.configure(); | ||
const {xScale, yScale} = this.getMeta(); | ||
if (!xScale || !yScale) { | ||
// configure is called once before `linkScales`, and at that call we don't have any scales linked yet | ||
return; | ||
} | ||
range.updated = true; | ||
if (scale.axis === 'x') { | ||
range.min = 0; | ||
range.max = 1; | ||
return; | ||
const w = xScale.right - xScale.left; | ||
const h = yScale.bottom - yScale.top; | ||
const rect = {x: 0, y: 0, w, h, rtl: !!this.options.rtl}; | ||
if (rectNotEqual(this._rect, rect)) { | ||
this._rect = rect; | ||
this._rectChanged = true; | ||
} | ||
const dataset = this.getDataset(); | ||
const {vMin, vMax} = getMinMax(dataset.data, this._useTree); | ||
range.min = vMin; | ||
range.max = vMax; | ||
if (this._rectChanged) { | ||
xScale.max = w; | ||
xScale.configure(); | ||
yScale.max = h; | ||
yScale.configure(); | ||
} | ||
} | ||
update(mode) { | ||
const meta = this.getMeta(); | ||
const dataset = this.getDataset(); | ||
const dpr = this.chart.currentDevicePixelRatio; | ||
const {data} = this.getMeta(); | ||
const groups = dataset.groups || (dataset.groups = []); | ||
const key = dataset.key; | ||
const tree = dataset.tree = dataset.tree || dataset.data || []; | ||
if (!helpers.defined(this._useTree)) { | ||
this._useTree = !!dataset.tree; | ||
if (mode === 'reset') { | ||
// reset is called before 2nd configure and is only called if animations are enabled. So wen need an extra configure call here. | ||
this.configure(); | ||
} | ||
const groups = dataset.groups || (dataset.groups = []); | ||
const key = dataset.key || ''; | ||
const rtl = !!dataset.rtl; | ||
const mainRect = rasterizeRect(getArea(meta, dataset.data, rtl, this._useTree), dpr); | ||
if (mode === 'reset' || rectNotEqual(this._rect, mainRect) || this._key !== key || arrayNotEqual(this._groups, groups)) { | ||
this._rect = mainRect; | ||
if (this._rectChanged || this._key !== key || arrayNotEqual(this._groups, groups) || this._prevTree !== tree) { | ||
this._groups = groups.slice(); | ||
this._key = key; | ||
this._prevTree = tree; | ||
this._rectChanged = false; | ||
dataset.data = buildData(dataset, mainRect, dpr); | ||
dataset.data = buildData(tree, dataset, this._rect); | ||
// @ts-ignore using private stuff | ||
@@ -1031,25 +995,20 @@ this._dataCheck(); | ||
this.updateElements(meta.data, 0, meta.data.length, mode); | ||
this.updateElements(data, 0, data.length, mode); | ||
} | ||
updateElements(rects, start, count, mode) { | ||
const me = this; | ||
const reset = mode === 'reset'; | ||
const dataset = me.getDataset(); | ||
const firstOpts = me._rect.options = me.resolveDataElementOptions(start, mode); | ||
const sharedOptions = me.getSharedOptions(firstOpts); | ||
const includeOptions = me.includeOptions(mode, sharedOptions); | ||
const dataset = this.getDataset(); | ||
const firstOpts = this._rect.options = this.resolveDataElementOptions(start, mode); | ||
const sharedOptions = this.getSharedOptions(firstOpts); | ||
const includeOptions = this.includeOptions(mode, sharedOptions); | ||
const {xScale, yScale} = this.getMeta(this.index); | ||
for (let i = start; i < start + count; i++) { | ||
const sq = dataset.data[i]; | ||
const options = sharedOptions || me.resolveDataElementOptions(i, mode); | ||
const sp = options.spacing; | ||
const sp2 = sp * 2; | ||
const properties = { | ||
x: sq.x + sp, | ||
y: sq.y + sp, | ||
width: reset ? 0 : sq.w - sp2, | ||
height: reset ? 0 : sq.h - sp2, | ||
hidden: sp2 > sq.w || sp2 > sq.h, | ||
}; | ||
const options = sharedOptions || this.resolveDataElementOptions(i, mode); | ||
const properties = scaleRect(dataset.data[i], xScale, yScale, options.spacing); | ||
if (reset) { | ||
properties.width = 0; | ||
properties.height = 0; | ||
} | ||
@@ -1059,10 +1018,10 @@ if (includeOptions) { | ||
} | ||
me.updateElement(rects[i], i, properties, mode); | ||
this.updateElement(rects[i], i, properties, mode); | ||
} | ||
me.updateSharedOptions(sharedOptions, mode, firstOpts); | ||
this.updateSharedOptions(sharedOptions, mode, firstOpts); | ||
} | ||
draw() { | ||
const {ctx, chartArea, currentDevicePixelRatio: dpr} = this.chart; | ||
const {ctx, chartArea} = this.chart; | ||
const metadata = this.getMeta().data || []; | ||
@@ -1077,3 +1036,3 @@ const dataset = this.getDataset(); | ||
if (!rect.hidden) { | ||
rect.draw(ctx, data[i], levels, dpr); | ||
rect.draw(ctx, data[i], levels); | ||
} | ||
@@ -1139,2 +1098,4 @@ } | ||
type: 'linear', | ||
alignToPixels: true, | ||
bounds: 'data', | ||
display: false | ||
@@ -1144,3 +1105,6 @@ }, | ||
type: 'linear', | ||
display: false | ||
alignToPixels: true, | ||
bounds: 'data', | ||
display: false, | ||
reverse: true | ||
} | ||
@@ -1185,4 +1149,2 @@ }, | ||
exports.index = index; | ||
exports.maxValue = maxValue; | ||
exports.minValue = minValue; | ||
exports.normalizeTreeToArray = normalizeTreeToArray; | ||
@@ -1189,0 +1151,0 @@ exports.requireVersion = requireVersion; |
/*! | ||
* chartjs-chart-treemap v2.1.2 | ||
* chartjs-chart-treemap v2.1.3 | ||
* https://chartjs-chart-treemap.pages.dev/ | ||
@@ -7,2 +7,2 @@ * (c) 2022 Jukka Kurkela | ||
*/ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-treemap"]={},t.Chart,t.Chart.helpers)}(this,(function(t,e,n){"use strict";const i=t=>n.isObject(t)?t.v:t,r=t=>t.reduce((function(t,e){return t>i(e)?t:i(e)}),0),o=(t,e)=>t.reduce((function(t,e){return t<i(e)?t:i(e)}),e),s=t=>""+t;function a(t,e,i,r=[],o=0,l=[]){const h=o-1;if(t in i&&o>0){const n=r.reduce((function(t,e,n){return n!==h&&(t[s(n)]=e),t}),{});n[e]=r[h],n[t]=i[t],l.push(n)}else for(const s of Object.keys(i)){const h=i[s];n.isObject(h)&&(r.push(s),a(t,e,h,r,o+1,l))}return r.splice(h,1),l}function l(t,e,n){const i=a(t,e,n);if(!i.length)return i;const r=i.reduce((function(t,e){const n=Object.keys(e).length-2;return t>n?t:n}));return i.forEach((function(t){for(let e=0;e<r;e++){const n=s(e);t[n]||(t[n]="")}})),i}function h(t){const e=[...t],n=[];for(;e.length;){const t=e.pop();Array.isArray(t)?e.push(...t):n.push(t)}return n.reverse()}function u(t,e,n){if(!t.length)return;const i=[];for(const r of t){const t=e[r];if(""===t){i.push(n);break}i.push(t)}return i.length?i.join("."):n}function c(t,e,n,i,r,o,s=[]){const a=Object.create(null),l=Object.create(null),h=[];let c,d,p;for(d=0,p=t.length;d<p;++d){const h=t[d];r&&h[r]!==o||(c=h[e]||h[i]||"",c in a||(a[c]={value:0},l[c]=[]),a[c].value+=+h[n],a[c].label=h[e]||"",a[c].path=u(s,h,c),l[c].push(h))}return Object.keys(a).forEach((t=>{const i={children:l[t]};i[n]=+a[t].value,i[e]=a[t].label,i.label=t,i.path=a[t].path,r&&(i[r]=o),h.push(i)})),h}function d(t,e){let i,r=t.length;if(!r)return e;const o=n.isObject(t[0]);for(e=o?e:"v",i=0,r=t.length;i<r;++i)o?t[i]._idx=i:t[i]={v:t[i],_idx:i};return e}function p(t,e){e?t.sort(((t,n)=>+n[e]-+t[e])):t.sort(((t,e)=>+e-+t))}function f(t,e){let n,i,r;for(n=0,i=0,r=t.length;i<r;++i)n+=e?+t[i][e]:+t[i];return n}function g(t,e){const n=e.split(".");if(!t.split(".").reduce(((t,e,i)=>t&&e<=n[i]),!0))throw new Error(`Chart.js v${e} is not supported. v${t} or newer is required.`)}const m=(t,e)=>Math.round(t*e)/e;function x(t,e){return{...t,x:m(t.x,e),y:m(t.y,e),w:m(t.w,e),h:m(t.h,e)}}const y=new Map;function b(t,e){const{x:n,y:i,width:r,height:o}=t.getProps(["x","y","width","height"],e);return{left:n,top:i,right:n+r,bottom:i+o}}function v(t,e,n){return Math.max(Math.min(t,n),e)}function w(t,e,i){const r=n.toTRBL(t);return{t:v(r.top,0,i),r:v(r.right,0,e),b:v(r.bottom,0,i),l:v(r.left,0,e)}}function _(t,e){const i=b(t),r=i.right-i.left,o=i.bottom-i.top,s=w(t.options.borderWidth,r/2,o/2),a=function(t,e,i){const r=n.toTRBLCorners(t),o=Math.min(e,i);return{topLeft:v(r.topLeft,0,o),topRight:v(r.topRight,0,o),bottomLeft:v(r.bottomLeft,0,o),bottomRight:v(r.bottomRight,0,o)}}(t.options.borderRadius,r/2,o/2),l=x({x:i.left,y:i.top,w:r,h:o,active:t.active,radius:a},e);return{outer:l,inner:{x:l.x+s.l,y:l.y+s.t,w:l.w-s.l-s.r,h:l.h-s.t-s.b,active:t.active,radius:{topLeft:Math.max(0,a.topLeft-Math.max(s.t,s.l)),topRight:Math.max(0,a.topRight-Math.max(s.t,s.r)),bottomLeft:Math.max(0,a.bottomLeft-Math.max(s.b,s.l)),bottomRight:Math.max(0,a.bottomRight-Math.max(s.b,s.r))}}}}function M(t,e,n,i){const r=null===e,o=null===n,s=!(!t||r&&o)&&b(t,i);return s&&(r||e>=s.left&&e<=s.right)&&(o||n>=s.top&&n<=s.bottom)}function k(t,e){t.rect(e.x,e.y,e.w,e.h)}function R(t,e){if(!e||!1===e.display)return!1;const{w:i,h:r}=t,o=n.toFont(e.font).lineHeight,s=v(2*n.valueOrDefault(e.padding,3),0,Math.min(i,r));return i-s>o&&r-s>o}function O(t,e,i,r,o){const{captions:s,labels:a}=i;t.save(),t.beginPath(),t.rect(e.x,e.y,e.w,e.h),t.clip();const l=!("l"in r)||r.l===o;l&&a.display?function(t,e,i){const r=i.labels,o=r.formatter;if(!o)return;const s=n.isArray(o)?o:[o],{font:a,hoverFont:l}=r,h=(e.active?l:a)||a,u=n.isArray(h)?h.map((t=>n.toFont(t))):[n.toFont(h)],c=function(t,e,n){const i=n.reduce((function(t,e){return t+=e.string}),""),r=e.join()+i+(t._measureText?"-spriting":"");if(!y.has(r)){t.save();const i=e.length;let o=0,s=0;for(let r=0;r<i;r++){const i=n[Math.min(r,n.length-1)];t.font=i.string;const a=e[r];o=Math.max(o,t.measureText(a).width),s+=i.lineHeight}t.restore(),y.set(r,{width:o,height:s})}return y.get(r)}(t,s,u);if(!function(t,e,n,i){const{overflow:r,padding:o}=n,{width:s,height:a}=i;if("hidden"===r)return!(s+2*o>e.w||a+2*o>e.h);return!0}(0,e,r,c))return;const{color:d,hoverColor:p,align:f}=r,g=(e.active?p:d)||d,m=n.isArray(g)?g:[g],x=function(t,e,n){const{align:i,position:r,padding:o}=e;let s,a;s=C(t,i,o),a="top"===r?t.y+o:"bottom"===r?t.y+t.h-o-n.height:t.y+(t.h-n.height)/2+o;return{x:s,y:a}}(e,r,c);t.textAlign=f,t.textBaseline="middle";let b=0;s.forEach((function(e,n){const i=m[Math.min(n,m.length-1)],r=u[Math.min(n,u.length-1)],o=r.lineHeight;t.font=r.string,t.fillStyle=i,t.fillText(e,x.x,x.y+o/2+b),b+=o}))}(t,e,i):!l&&R(e,s)&&function(t,e,i,r){const{captions:o,spacing:s,rtl:a}=i,{color:l,hoverColor:h,font:u,hoverFont:c,padding:d,align:p,formatter:f}=o,g=(e.active?h:l)||l,m=p||(a?"right":"left"),x=(e.active?c:u)||u,y=n.toFont(x),b=y.lineHeight/2,v=C(e,m,d);t.fillStyle=g,t.font=y.string,t.textAlign=m,t.textBaseline="middle",t.fillText(f||r.g,v,e.y+d+s+b)}(t,e,i,r),t.restore()}function C(t,e,n){return"left"===e?t.x+n:"right"===e?t.x+t.w-n:t.x+t.w/2}class j extends e.Element{constructor(t){super(),this.options=void 0,this.width=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t,e,i=0,r){if(!e)return;const o=this.options,{inner:s,outer:a}=_(this,r),l=(h=a.radius).topLeft||h.topRight||h.bottomLeft||h.bottomRight?n.addRoundedRectPath:k;var h;t.save(),a.w===s.w&&a.h===s.h||(t.beginPath(),l(t,a),t.clip(),l(t,s),t.fillStyle=o.borderColor,t.fill("evenodd")),t.beginPath(),l(t,s),t.fillStyle=o.backgroundColor,t.fill(),function(t,e,n,i){const r=n.dividers;if(!r.display||!i._data.children.length)return;const{x:o,y:s,w:a,h:l}=e,{lineColor:h,lineCapStyle:u,lineDash:c,lineDashOffset:d,lineWidth:p}=r;if(t.save(),t.strokeStyle=h,t.lineCap=u,t.setLineDash(c),t.lineDashOffset=d,t.lineWidth=p,t.beginPath(),a>l){const e=a/2;t.moveTo(o+e,s),t.lineTo(o+e,s+l)}else{const e=l/2;t.moveTo(o,s+e),t.lineTo(o+a,s+e)}t.stroke(),t.restore()}(t,s,o,e),O(t,s,o,e,i),t.restore()}inRange(t,e,n){return M(this,t,e,n)}inXRange(t,e){return M(this,t,null,e)}inYRange(t,e){return M(this,null,t,e)}getCenterPoint(t){const{x:e,y:n,width:i,height:r}=this.getProps(["x","y","width","height"],t);return{x:e+i/2,y:n+r/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}function T(t,e,n,i,r,o){const s=t._normalized,a=e*s/n,l=m(Math.sqrt(s*a),r),h=Math.min(m(s/l,r),o);return{d1:l,d2:h,w:"_ix"===i?l:h,h:"_ix"===i?h:l}}j.id="treemap",j.defaults={label:void 0,borderRadius:0,borderWidth:0,captions:{align:void 0,color:"black",display:!0,font:{},formatter:t=>t.raw.g||t.raw._data.label||"",padding:3},dividers:{display:!1,lineCapStyle:"butt",lineColor:"black",lineDash:[],lineDashOffset:0,lineWidth:1},labels:{align:"center",color:"black",display:!1,font:{},formatter:t=>t.raw.g?[t.raw.g,t.raw.v+""]:t.raw._data.label?[t.raw._data.label,t.raw.v+""]:t.raw.v+"",overflow:"cut",position:"middle",padding:3},rtl:!1,spacing:.5},j.descriptors={labels:{_fallback:!0},captions:{_fallback:!0},_scriptable:!0,_indexable:!1},j.defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};const P=(t,e)=>t.rtl?t.x+t.iw-e:t.x+t._ix;function D(t,e,n,i,r){const o=x({x:P(t,n.w),y:t.y+t._iy,w:n.w,h:n.h,a:e._normalized,v:e.value,s:i,_data:e._data},r);return e.group&&(o.g=e.group,o.l=e.level,o.gs=e.groupSum),o}class L{constructor(t,e){this.dpr=e,t=t||{w:1,h:1},this.rtl=!!t.rtl,this.x=t.x||t.left||0,this.y=t.y||t.top||0,this._ix=0,this._iy=0,this.w=t.w||t.width||t.right-t.left,this.h=t.h||t.height||t.bottom-t.top}get area(){return this.w*this.h}get iw(){return this.w-this._ix}get ih(){return this.h-this._iy}get dir(){const t=this.ih;return t<=this.iw&&t>0?"y":"x"}get side(){return m("x"===this.dir?this.iw:this.ih,this.dpr)}map(t){const{dir:e,side:n,dpr:i}=this,{key:r,d2prop:o,id2prop:s,d2key:a}=function(t){if("x"===t)return{key:"_ix",d2prop:"h",id2prop:"ih",d2key:"_iy"};return{key:"_iy",d2prop:"w",id2prop:"iw",d2key:"_ix"}}(e),l=t.nsum,h=t.get(),u=n*n,c=m(this[s],i),d=l*l,p=[];let f=0,g=0;for(const e of h){const n=T(e,u,d,r,i,c);g+=n.d1,f=Math.min(Math.max(f,n.d2),c),p.push(D(this,e,n,t.sum,i)),this[r]+=n.d1}for(const t of p){const n=t[o];n!==f&&(this.rtl&&"y"===e&&(t.x-=f-n),t[o]=f)}return this[a]+=f,this[r]-=g,p}}const S=Math.min,E=Math.max;function F(t,e){const n=+e[t.key],i=n*t.ratio;return e._normalized=i,{min:S(t.min,n),max:E(t.max,n),sum:t.sum+n,nmin:S(t.nmin,i),nmax:E(t.nmax,i),nsum:t.nsum+i}}function A(t,e,n){t._arr.push(e),function(t,e){Object.assign(t,e)}(t,n)}class V{constructor(t,e){const n=this;n.key=t,n.ratio=e,n.reset()}get length(){return this._arr.length}reset(){const t=this;t._arr=[],t._hist=[],t.sum=0,t.nsum=0,t.min=1/0,t.max=-1/0,t.nmin=1/0,t.nmax=-1/0}push(t){A(this,t,F(this,t))}pushIf(t,e,...n){const i=F(this,t);if(!e((r=this,{min:r.min,max:r.max,sum:r.sum,nmin:r.nmin,nmax:r.nmax,nsum:r.nsum}),i,n))return t;var r;A(this,t,i)}get(){return this._arr}}function z(t,e,n){if(0===t.sum)return!0;const[i]=n,r=t.nsum*t.nsum,o=e.nsum*e.nsum,s=i*i,a=Math.max(s*t.nmax/r,r/(s*t.nmin));return Math.max(s*e.nmax/o,o/(s*e.nmin))<=a}function H(t,e,n,i=1,r,o,s){t=t||[];const a=[],l=new L(e,i),u=new V("value",l.area/f(t,n));let c=l.side;const g=t.length;let m,x;if(!g)return a;const y=t.slice();n=d(y,n),p(y,n);const b=t=>r&&y[t][r];for(m=0;m<g;++m)x={value:(v=m,n?+y[v][n]:+y[v]),groupSum:s,_data:t[y[m]._idx],level:void 0,group:void 0},r&&(x.level=o,x.group=b(m)),x=u.pushIf(x,z,c),x&&(a.push(l.map(u)),c=l.side,u.reset(),u.push(x));var v;return u.length&&a.push(l.map(u)),h(a)}function W(t,e){const n=e?1:r(t);return{vMin:e?0:o(t,n),vMax:n}}class q extends e.DatasetController{constructor(t,e){super(t,e),this._rect=void 0,this._key=void 0,this._groups=void 0,this._useTree=void 0}initialize(){this.enableOptionSharing=!0,super.initialize()}updateRangeFromParsed(t,e){if(t.updated)return;if(t.updated=!0,"x"===e.axis)return t.min=0,void(t.max=1);const n=this.getDataset(),{vMin:i,vMax:r}=W(n.data,this._useTree);t.min=i,t.max=r}update(t){const e=this.getMeta(),i=this.getDataset(),r=this.chart.currentDevicePixelRatio;n.defined(this._useTree)||(this._useTree=!!i.tree);const o=i.groups||(i.groups=[]),a=i.key||"",h=!!i.rtl,u=x(function({xScale:t,yScale:e},n,i,r){const{vMin:o,vMax:s}=W(n,r),a=t.getPixelForValue(0),l=t.getPixelForValue(1),h=e.getPixelForValue(o),u=e.getPixelForValue(s);return{x:a,y:u,w:l-a,h:h-u,rtl:i}}(e,i.data,h,this._useTree),r);var d,p;"reset"!==t&&(d=this._rect,p=u,d&&p&&d.x===p.x&&d.y===p.y&&d.w===p.w&&d.h===p.h)&&this._key===a&&!function(t,e){let n,i;if(t.lenght!==e.length)return!0;for(n=0,i=t.length;n<i;++n)if(t[n]!==e[n])return!0;return!1}(this._groups,o)||(this._rect=u,this._groups=o.slice(),this._key=a,i.data=function(t,e,i){const r=t.key||"",o=t.treeLeafKey||"_leaf";let a=t.tree||[];n.isObject(a)&&(a=l(r,o,a));const h=t.groups||[],u=h.length,d=n.valueOrDefault(t.spacing,0),p=t.captions||{},f=n.toFont(p.font),g=n.valueOrDefault(p.padding,3);return!a.length&&t.data.length&&(a=t.tree=t.data),u?function e(n,l,m,y){const b=s(h[n]),v=n>0&&s(h[n-1]),_=c(a,b,r,o,v,m,h.filter(((t,e)=>e<=n))),M=H(_,l,r,i,b,n,y),k=M.slice();return n<u-1&&M.forEach((r=>{const o=w(t.borderWidth,r.w/2,r.h/2),s={...l,x:r.x+d+o.l,y:r.y+d+o.t,w:r.w-2*d-o.l-o.r,h:r.h-2*d-o.t-o.b};R(s,p)&&(s.y+=f.lineHeight+2*g,s.h-=f.lineHeight+2*g),k.push(...e(n+1,x(s,i),r.g,r.s))})),k}(0,e):H(a,e,r,i)}(i,u,r),this._dataCheck(),this._resyncElements()),this.updateElements(e.data,0,e.data.length,t)}updateElements(t,e,n,i){const r=this,o="reset"===i,s=r.getDataset(),a=r._rect.options=r.resolveDataElementOptions(e,i),l=r.getSharedOptions(a),h=r.includeOptions(i,l);for(let a=e;a<e+n;a++){const e=s.data[a],n=l||r.resolveDataElementOptions(a,i),u=n.spacing,c=2*u,d={x:e.x+u,y:e.y+u,width:o?0:e.w-c,height:o?0:e.h-c,hidden:c>e.w||c>e.h};h&&(d.options=n),r.updateElement(t[a],a,d,i)}r.updateSharedOptions(l,i,a)}draw(){const{ctx:t,chartArea:e,currentDevicePixelRatio:i}=this.chart,r=this.getMeta().data||[],o=this.getDataset(),s=(o.groups||[]).length-1,a=o.data;n.clipArea(t,e);for(let e=0,n=r.length;e<n;++e){const n=r[e];n.hidden||n.draw(t,a[e],s,i)}n.unclipArea(t)}}q.id="treemap",q.version="2.1.2",q.defaults={dataElementType:"treemap",animations:{numbers:{type:"number",properties:["x","y","width","height"]}}},q.descriptors={_scriptable:!0,_indexable:!1},q.overrides={interaction:{mode:"point",includeInvisible:!0,intersect:!0},hover:{},plugins:{tooltip:{position:"treemap",intersect:!0,callbacks:{title(t){if(t.length){return t[0].dataset.key||""}return""},label(t){const e=t.dataset,n=e.data[t.dataIndex],i=n.g||n._data.label||e.label;return(i?i+": ":"")+n.v}}}},scales:{x:{type:"linear",display:!1},y:{type:"linear",display:!1}}},q.beforeRegister=function(){g("3.8",e.Chart.version)},q.afterRegister=function(){const t=e.registry.plugins.get("tooltip");t?t.positioners.treemap=function(t){if(!t.length)return!1;return t[t.length-1].element.tooltipPosition()}:console.warn("Unable to register the treemap positioner because tooltip plugin is not registered")},q.afterUnregister=function(){const t=e.registry.plugins.get("tooltip");t&&delete t.positioners.treemap},e.Chart.register(q,j),t.flatten=h,t.getGroupKey=s,t.group=c,t.index=d,t.maxValue=r,t.minValue=o,t.normalizeTreeToArray=l,t.requireVersion=g,t.sort=p,t.sum=f,Object.defineProperty(t,"__esModule",{value:!0})})); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-treemap"]={},t.Chart,t.Chart.helpers)}(this,(function(t,e,n){"use strict";const i=t=>""+t;function r(t,e,o,s=[],a=0,h=[]){const l=a-1;if(t in o&&a>0){const n=s.reduce((function(t,e,n){return n!==l&&(t[i(n)]=e),t}),{});n[e]=s[l],n[t]=o[t],h.push(n)}else for(const i of Object.keys(o)){const l=o[i];n.isObject(l)&&(s.push(i),r(t,e,l,s,a+1,h))}return s.splice(l,1),h}function o(t,e,n){const o=r(t,e,n);if(!o.length)return o;const s=o.reduce((function(t,e){const n=Object.keys(e).length-2;return t>n?t:n}));return o.forEach((function(t){for(let e=0;e<s;e++){const n=i(e);t[n]||(t[n]="")}})),o}function s(t){const e=[...t],n=[];for(;e.length;){const t=e.pop();Array.isArray(t)?e.push(...t):n.push(t)}return n.reverse()}function a(t,e,n){if(!t.length)return;const i=[];for(const r of t){const t=e[r];if(""===t){i.push(n);break}i.push(t)}return i.length?i.join("."):n}function h(t,e,n,i,r,o,s=[]){const h=Object.create(null),l=Object.create(null),u=[];let c,d,g;for(d=0,g=t.length;d<g;++d){const u=t[d];r&&u[r]!==o||(c=u[e]||u[i]||"",c in h||(h[c]={value:0},l[c]=[]),h[c].value+=+u[n],h[c].label=u[e]||"",h[c].path=a(s,u,c),l[c].push(u))}return Object.keys(h).forEach((t=>{const i={children:l[t]};i[n]=+h[t].value,i[e]=h[t].label,i.label=t,i.path=h[t].path,r&&(i[r]=o),u.push(i)})),u}function l(t,e){let i,r=t.length;if(!r)return e;const o=n.isObject(t[0]);for(e=o?e:"v",i=0,r=t.length;i<r;++i)o?t[i]._idx=i:t[i]={v:t[i],_idx:i};return e}function u(t,e){e?t.sort(((t,n)=>+n[e]-+t[e])):t.sort(((t,e)=>+e-+t))}function c(t,e){let n,i,r;for(n=0,i=0,r=t.length;i<r;++i)n+=e?+t[i][e]:+t[i];return n}function d(t,e){const n=e.split(".");if(!t.split(".").reduce(((t,e,i)=>t&&e<=n[i]),!0))throw new Error(`Chart.js v${e} is not supported. v${t} or newer is required.`)}const g=new Map;function f(t,e){const{x:n,y:i,width:r,height:o}=t.getProps(["x","y","width","height"],e);return{left:n,top:i,right:n+r,bottom:i+o}}function p(t,e,n){return Math.max(Math.min(t,n),e)}function m(t,e,i){const r=n.toTRBL(t);return{t:p(r.top,0,i),r:p(r.right,0,e),b:p(r.bottom,0,i),l:p(r.left,0,e)}}function x(t){const e=f(t),i=e.right-e.left,r=e.bottom-e.top,o=m(t.options.borderWidth,i/2,r/2),s=function(t,e,i){const r=n.toTRBLCorners(t),o=Math.min(e,i);return{topLeft:p(r.topLeft,0,o),topRight:p(r.topRight,0,o),bottomLeft:p(r.bottomLeft,0,o),bottomRight:p(r.bottomRight,0,o)}}(t.options.borderRadius,i/2,r/2),a={x:e.left,y:e.top,w:i,h:r,active:t.active,radius:s};return{outer:a,inner:{x:a.x+o.l,y:a.y+o.t,w:a.w-o.l-o.r,h:a.h-o.t-o.b,active:t.active,radius:{topLeft:Math.max(0,s.topLeft-Math.max(o.t,o.l)),topRight:Math.max(0,s.topRight-Math.max(o.t,o.r)),bottomLeft:Math.max(0,s.bottomLeft-Math.max(o.b,o.l)),bottomRight:Math.max(0,s.bottomRight-Math.max(o.b,o.r))}}}}function b(t,e,n,i){const r=null===e,o=null===n,s=!(!t||r&&o)&&f(t,i);return s&&(r||e>=s.left&&e<=s.right)&&(o||n>=s.top&&n<=s.bottom)}function y(t,e){t.rect(e.x,e.y,e.w,e.h)}function v(t,e){if(!e||!1===e.display)return!1;const{w:i,h:r}=t,o=n.toFont(e.font).lineHeight,s=p(2*n.valueOrDefault(e.padding,3),0,Math.min(i,r));return i-s>o&&r-s>o}function w(t,e,i,r,o){const{captions:s,labels:a}=i;t.save(),t.beginPath(),t.rect(e.x,e.y,e.w,e.h),t.clip();const h=r&&(!n.defined(r.l)||r.l===o);h&&a.display?function(t,e,i){const r=i.labels,o=r.formatter;if(!o)return;const s=n.isArray(o)?o:[o],{font:a,hoverFont:h}=r,l=(e.active?h:a)||a,u=n.isArray(l)?l.map((t=>n.toFont(t))):[n.toFont(l)],c=function(t,e,n){const i=n.reduce((function(t,e){return t+=e.string}),""),r=e.join()+i+(t._measureText?"-spriting":"");if(!g.has(r)){t.save();const i=e.length;let o=0,s=0;for(let r=0;r<i;r++){const i=n[Math.min(r,n.length-1)];t.font=i.string;const a=e[r];o=Math.max(o,t.measureText(a).width),s+=i.lineHeight}t.restore(),g.set(r,{width:o,height:s})}return g.get(r)}(t,s,u);if(!function(t,e,n,i){const{overflow:r,padding:o}=n,{width:s,height:a}=i;if("hidden"===r)return!(s+2*o>e.w||a+2*o>e.h);return!0}(0,e,r,c))return;const{color:d,hoverColor:f,align:p}=r,m=(e.active?f:d)||d,x=n.isArray(m)?m:[m],b=function(t,e,n){const{align:i,position:r,padding:o}=e;let s,a;s=_(t,i,o),a="top"===r?t.y+o:"bottom"===r?t.y+t.h-o-n.height:t.y+(t.h-n.height)/2+o;return{x:s,y:a}}(e,r,c);t.textAlign=p,t.textBaseline="middle";let y=0;s.forEach((function(e,n){const i=x[Math.min(n,x.length-1)],r=u[Math.min(n,u.length-1)],o=r.lineHeight;t.font=r.string,t.fillStyle=i,t.fillText(e,b.x,b.y+o/2+y),y+=o}))}(t,e,i):!h&&v(e,s)&&function(t,e,i,r){const{captions:o,spacing:s,rtl:a}=i,{color:h,hoverColor:l,font:u,hoverFont:c,padding:d,align:g,formatter:f}=o,p=(e.active?l:h)||h,m=g||(a?"right":"left"),x=(e.active?c:u)||u,b=n.toFont(x),y=b.lineHeight/2,v=_(e,m,d);t.fillStyle=p,t.font=b.string,t.textAlign=m,t.textBaseline="middle",t.fillText(f||r.g,v,e.y+d+s+y)}(t,e,i,r),t.restore()}function _(t,e,n){return"left"===e?t.x+n:"right"===e?t.x+t.w-n:t.x+t.w/2}class M extends e.Element{constructor(t){super(),this.options=void 0,this.width=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t,e,i=0){if(!e)return;const r=this.options,{inner:o,outer:s}=x(this),a=(h=s.radius).topLeft||h.topRight||h.bottomLeft||h.bottomRight?n.addRoundedRectPath:y;var h;t.save(),s.w===o.w&&s.h===o.h||(t.beginPath(),a(t,s),t.clip(),a(t,o),t.fillStyle=r.borderColor,t.fill("evenodd")),t.beginPath(),a(t,o),t.fillStyle=r.backgroundColor,t.fill(),function(t,e,n,i){const r=n.dividers;if(!r.display||!i._data.children.length)return;const{x:o,y:s,w:a,h:h}=e,{lineColor:l,lineCapStyle:u,lineDash:c,lineDashOffset:d,lineWidth:g}=r;if(t.save(),t.strokeStyle=l,t.lineCap=u,t.setLineDash(c),t.lineDashOffset=d,t.lineWidth=g,t.beginPath(),a>h){const e=a/2;t.moveTo(o+e,s),t.lineTo(o+e,s+h)}else{const e=h/2;t.moveTo(o,s+e),t.lineTo(o+a,s+e)}t.stroke(),t.restore()}(t,o,r,e),w(t,o,r,e,i),t.restore()}inRange(t,e,n){return b(this,t,e,n)}inXRange(t,e){return b(this,t,null,e)}inYRange(t,e){return b(this,null,t,e)}getCenterPoint(t){const{x:e,y:n,width:i,height:r}=this.getProps(["x","y","width","height"],t);return{x:e+i/2,y:n+r/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}function C(t,e,n,i){const r=t._normalized,o=e*r/n,s=Math.sqrt(r*o),a=r/s;return{d1:s,d2:a,w:"_ix"===i?s:a,h:"_ix"===i?a:s}}M.id="treemap",M.defaults={label:void 0,borderRadius:0,borderWidth:0,captions:{align:void 0,color:"black",display:!0,font:{},formatter:t=>t.raw.g||t.raw._data.label||"",padding:3},dividers:{display:!1,lineCapStyle:"butt",lineColor:"black",lineDash:[],lineDashOffset:0,lineWidth:1},labels:{align:"center",color:"black",display:!1,font:{},formatter:t=>t.raw.g?[t.raw.g,t.raw.v+""]:t.raw._data.label?[t.raw._data.label,t.raw.v+""]:t.raw.v+"",overflow:"cut",position:"middle",padding:3},rtl:!1,spacing:.5},M.descriptors={labels:{_fallback:!0},captions:{_fallback:!0},_scriptable:!0,_indexable:!1},M.defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};const k=(t,e)=>t.rtl?t.x+t.iw-e:t.x+t._ix;function O(t,e,n,i){const r={x:k(t,n.w),y:t.y+t._iy,w:n.w,h:n.h,a:e._normalized,v:e.value,s:i,_data:e._data};return e.group&&(r.g=e.group,r.l=e.level,r.gs=e.groupSum),r}class R{constructor(t){t=t||{w:1,h:1},this.rtl=!!t.rtl,this.x=t.x||t.left||0,this.y=t.y||t.top||0,this._ix=0,this._iy=0,this.w=t.w||t.width||t.right-t.left,this.h=t.h||t.height||t.bottom-t.top}get area(){return this.w*this.h}get iw(){return this.w-this._ix}get ih(){return this.h-this._iy}get dir(){const t=this.ih;return t<=this.iw&&t>0?"y":"x"}get side(){return"x"===this.dir?this.iw:this.ih}map(t){const{dir:e,side:n}=this,i="x"===e?"_ix":"_iy",r=t.nsum,o=t.get(),s=n*n,a=r*r,h=[];let l=0,u=0;for(const e of o){const n=C(e,s,a,i);u+=n.d1,l=Math.max(l,n.d2),h.push(O(this,e,n,t.sum)),this[i]+=n.d1}return this["x"===e?"_iy":"_ix"]+=l,this[i]-=u,h}}const j=Math.min,T=Math.max;function P(t,e){const n=+e[t.key],i=n*t.ratio;return e._normalized=i,{min:j(t.min,n),max:T(t.max,n),sum:t.sum+n,nmin:j(t.nmin,i),nmax:T(t.nmax,i),nsum:t.nsum+i}}function S(t,e,n){t._arr.push(e),function(t,e){Object.assign(t,e)}(t,n)}class D{constructor(t,e){const n=this;n.key=t,n.ratio=e,n.reset()}get length(){return this._arr.length}reset(){const t=this;t._arr=[],t._hist=[],t.sum=0,t.nsum=0,t.min=1/0,t.max=-1/0,t.nmin=1/0,t.nmax=-1/0}push(t){S(this,t,P(this,t))}pushIf(t,e,...n){const i=P(this,t);if(!e((r=this,{min:r.min,max:r.max,sum:r.sum,nmin:r.nmin,nmax:r.nmax,nsum:r.nsum}),i,n))return t;var r;S(this,t,i)}get(){return this._arr}}function L(t,e,n){if(0===t.sum)return!0;const[i]=n,r=t.nsum*t.nsum,o=e.nsum*e.nsum,s=i*i,a=Math.max(s*t.nmax/r,r/(s*t.nmin));return Math.max(s*e.nmax/o,o/(s*e.nmin))<=a}function E(t,e,n,i,r,o){t=t||[];const a=[],h=new R(e),d=new D("value",h.area/c(t,n));let g=h.side;const f=t.length;let p,m;if(!f)return a;const x=t.slice();n=l(x,n),u(x,n);const b=t=>i&&x[t][i];for(p=0;p<f;++p)m={value:(y=p,n?+x[y][n]:+x[y]),groupSum:o,_data:t[x[p]._idx],level:void 0,group:void 0},i&&(m.level=r,m.group=b(p)),m=d.pushIf(m,L,g),m&&(a.push(h.map(d)),g=h.side,d.reset(),d.push(m));var y;return d.length&&a.push(h.map(d)),s(a)}function A(t,e,n,i){const r=2*i,o=e.getPixelForValue(t.x),s=n.getPixelForValue(t.y),a=e.getPixelForValue(t.x+t.w)-o,h=n.getPixelForValue(t.y+t.h)-s;return{x:o+i,y:s+i,width:a-r,height:h-r,hidden:r>a||r>h}}class F extends e.DatasetController{constructor(t,e){super(t,e),this._groups=void 0,this._key=void 0,this._rect=void 0,this._rectChanged=!0}initialize(){this.enableOptionSharing=!0,super.initialize()}getMinMax(t){return{min:0,max:"x"===t.axis?t.right-t.left:t.bottom-t.top}}configure(){super.configure();const{xScale:t,yScale:e}=this.getMeta();if(!t||!e)return;const n=t.right-t.left,i=e.bottom-e.top,r={x:0,y:0,w:n,h:i,rtl:!!this.options.rtl};var o,s;o=this._rect,s=r,o&&s&&o.x===s.x&&o.y===s.y&&o.w===s.w&&o.h===s.h&&o.rtl===s.rtl||(this._rect=r,this._rectChanged=!0),this._rectChanged&&(t.max=n,t.configure(),e.max=i,e.configure())}update(t){const e=this.getDataset(),{data:r}=this.getMeta(),s=e.groups||(e.groups=[]),a=e.key,l=e.tree=e.tree||e.data||[];"reset"===t&&this.configure(),(this._rectChanged||this._key!==a||function(t,e){let n,i;if(!t||!e)return!0;if(t===e)return!1;if(t.length!==e.length)return!0;for(n=0,i=t.length;n<i;++n)if(t[n]!==e[n])return!0;return!1}(this._groups,s)||this._prevTree!==l)&&(this._groups=s.slice(),this._key=a,this._prevTree=l,this._rectChanged=!1,e.data=function(t,e,r){const s=e.key||"",a=e.treeLeafKey||"_leaf";n.isObject(t)&&(t=o(s,a,t));const l=e.groups||[],u=l.length,c=n.valueOrDefault(e.spacing,0),d=e.captions||{},g=n.toFont(d.font),f=n.valueOrDefault(d.padding,3);return u?function n(r,o,p,x){const b=i(l[r]),y=r>0&&i(l[r-1]),w=h(t,b,s,a,y,p,l.filter(((t,e)=>e<=r))),_=E(w,o,s,b,r,x),M=_.slice();return r<u-1&&_.forEach((t=>{const i=m(e.borderWidth,t.w/2,t.h/2),s={...o,x:t.x+c+i.l,y:t.y+c+i.t,w:t.w-2*c-i.l-i.r,h:t.h-2*c-i.t-i.b};v(s,d)&&(s.y+=g.lineHeight+2*f,s.h-=g.lineHeight+2*f),M.push(...n(r+1,s,t.g,t.s))})),M}(0,r):E(t,r,s)}(l,e,this._rect),this._dataCheck(),this._resyncElements()),this.updateElements(r,0,r.length,t)}updateElements(t,e,n,i){const r="reset"===i,o=this.getDataset(),s=this._rect.options=this.resolveDataElementOptions(e,i),a=this.getSharedOptions(s),h=this.includeOptions(i,a),{xScale:l,yScale:u}=this.getMeta(this.index);for(let s=e;s<e+n;s++){const e=a||this.resolveDataElementOptions(s,i),n=A(o.data[s],l,u,e.spacing);r&&(n.width=0,n.height=0),h&&(n.options=e),this.updateElement(t[s],s,n,i)}this.updateSharedOptions(a,i,s)}draw(){const{ctx:t,chartArea:e}=this.chart,i=this.getMeta().data||[],r=this.getDataset(),o=(r.groups||[]).length-1,s=r.data;n.clipArea(t,e);for(let e=0,n=i.length;e<n;++e){const n=i[e];n.hidden||n.draw(t,s[e],o)}n.unclipArea(t)}}F.id="treemap",F.version="2.1.3",F.defaults={dataElementType:"treemap",animations:{numbers:{type:"number",properties:["x","y","width","height"]}}},F.descriptors={_scriptable:!0,_indexable:!1},F.overrides={interaction:{mode:"point",includeInvisible:!0,intersect:!0},hover:{},plugins:{tooltip:{position:"treemap",intersect:!0,callbacks:{title(t){if(t.length){return t[0].dataset.key||""}return""},label(t){const e=t.dataset,n=e.data[t.dataIndex],i=n.g||n._data.label||e.label;return(i?i+": ":"")+n.v}}}},scales:{x:{type:"linear",alignToPixels:!0,bounds:"data",display:!1},y:{type:"linear",alignToPixels:!0,bounds:"data",display:!1,reverse:!0}}},F.beforeRegister=function(){d("3.8",e.Chart.version)},F.afterRegister=function(){const t=e.registry.plugins.get("tooltip");t?t.positioners.treemap=function(t){if(!t.length)return!1;return t[t.length-1].element.tooltipPosition()}:console.warn("Unable to register the treemap positioner because tooltip plugin is not registered")},F.afterUnregister=function(){const t=e.registry.plugins.get("tooltip");t&&delete t.positioners.treemap},e.Chart.register(F,M),t.flatten=s,t.getGroupKey=i,t.group=h,t.index=l,t.normalizeTreeToArray=o,t.requireVersion=d,t.sort=u,t.sum=c,Object.defineProperty(t,"__esModule",{value:!0})})); |
{ | ||
"name": "chartjs-chart-treemap", | ||
"homepage": "https://chartjs-chart-treemap.pages.dev/", | ||
"version": "2.1.2", | ||
"version": "2.1.3", | ||
"description": "Chart.js module for creating treemap charts", | ||
@@ -6,0 +6,0 @@ "main": "dist/chartjs-chart-treemap.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
78411
2151