chartjs-chart-wordcloud
Advanced tools
Comparing version 3.8.0 to 3.9.0
@@ -5,3 +5,3 @@ /** | ||
* | ||
* Copyright (c) 2021 Samuel Gratzl <sam@sgratzl.com> | ||
* Copyright (c) 2019 Samuel Gratzl <sam@sgratzl.com> | ||
*/ | ||
@@ -8,0 +8,0 @@ |
@@ -5,3 +5,3 @@ /** | ||
* | ||
* Copyright (c) 2021 Samuel Gratzl <sam@sgratzl.com> | ||
* Copyright (c) 2019 Samuel Gratzl <sam@sgratzl.com> | ||
*/ | ||
@@ -8,0 +8,0 @@ |
@@ -5,759 +5,264 @@ /** | ||
* | ||
* Copyright (c) 2021 Samuel Gratzl <sam@sgratzl.com> | ||
* Copyright (c) 2019 Samuel Gratzl <sam@sgratzl.com> | ||
*/ | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('chart.js'), require('chart.js/helpers')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'chart.js', 'chart.js/helpers'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ChartWordCloud = {}, global.Chart, global.Chart.helpers)); | ||
})(this, (function (exports, chart_js, helpers) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('chart.js'), require('chart.js/helpers'), require('d3-cloud')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'chart.js', 'chart.js/helpers', 'd3-cloud'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ChartWordCloud = {}, global.Chart, global.Chart.helpers, global.layout)); | ||
})(this, (function (exports, chart_js, helpers, layout) { 'use strict'; | ||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var d3Dispatch = {exports: {}}; | ||
var layout__default = /*#__PURE__*/_interopDefaultLegacy(layout); | ||
(function (module, exports) { | ||
// https://d3js.org/d3-dispatch/ v1.0.6 Copyright 2019 Mike Bostock | ||
(function (global, factory) { | ||
factory(exports) ; | ||
}(commonjsGlobal, function (exports) { | ||
var noop = {value: function() {}}; | ||
class WordElement extends chart_js.Element { | ||
static computeRotation(o, rnd) { | ||
if (o.rotationSteps <= 1) { | ||
return 0; | ||
} | ||
if (o.minRotation === o.maxRotation) { | ||
return o.minRotation; | ||
} | ||
const base = Math.min(o.rotationSteps, Math.floor(rnd() * o.rotationSteps)) / (o.rotationSteps - 1); | ||
const range = o.maxRotation - o.minRotation; | ||
return o.minRotation + base * range; | ||
} | ||
inRange(mouseX, mouseY) { | ||
const p = this.getProps(['x', 'y', 'width', 'height', 'scale']); | ||
if (p.scale <= 0) { | ||
return false; | ||
} | ||
const x = Number.isNaN(mouseX) ? p.x : mouseX; | ||
const y = Number.isNaN(mouseY) ? p.y : mouseY; | ||
return x >= p.x - p.width / 2 && x <= p.x + p.width / 2 && y >= p.y - p.height / 2 && y <= p.y + p.height / 2; | ||
} | ||
inXRange(mouseX) { | ||
return this.inRange(mouseX, Number.NaN); | ||
} | ||
inYRange(mouseY) { | ||
return this.inRange(Number.NaN, mouseY); | ||
} | ||
getCenterPoint() { | ||
return this.getProps(['x', 'y']); | ||
} | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
draw(ctx) { | ||
const { options } = this; | ||
const props = this.getProps(['x', 'y', 'width', 'height', 'text', 'scale']); | ||
if (props.scale <= 0) { | ||
return; | ||
} | ||
ctx.save(); | ||
const f = helpers.toFont({ ...options, size: options.size * props.scale }); | ||
ctx.font = f.string; | ||
ctx.fillStyle = options.color; | ||
ctx.textAlign = 'center'; | ||
ctx.translate(props.x, props.y); | ||
ctx.rotate((options.rotate / 180) * Math.PI); | ||
if (options.strokeStyle) { | ||
ctx.strokeStyle = options.strokeStyle; | ||
ctx.strokeText(props.text, 0, 0); | ||
} | ||
ctx.fillText(props.text, 0, 0); | ||
ctx.restore(); | ||
} | ||
} | ||
WordElement.id = 'word'; | ||
WordElement.defaults = { | ||
minRotation: -90, | ||
maxRotation: 0, | ||
rotationSteps: 2, | ||
padding: 1, | ||
strokeStyle: undefined, | ||
size: (ctx) => { | ||
const v = ctx.parsed.y; | ||
return v; | ||
}, | ||
hoverColor: '#ababab', | ||
}; | ||
WordElement.defaultRoutes = { | ||
color: 'color', | ||
family: 'font.family', | ||
style: 'font.style', | ||
weight: 'font.weight', | ||
lineHeight: 'font.lineHeight', | ||
}; | ||
function dispatch() { | ||
for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { | ||
if (!(t = arguments[i] + "") || (t in _) || /[\s.]/.test(t)) throw new Error("illegal type: " + t); | ||
_[t] = []; | ||
} | ||
return new Dispatch(_); | ||
} | ||
function patchController(type, config, controller, elements = [], scales = []) { | ||
chart_js.registry.addControllers(controller); | ||
if (Array.isArray(elements)) { | ||
chart_js.registry.addElements(...elements); | ||
} | ||
else { | ||
chart_js.registry.addElements(elements); | ||
} | ||
if (Array.isArray(scales)) { | ||
chart_js.registry.addScales(...scales); | ||
} | ||
else { | ||
chart_js.registry.addScales(scales); | ||
} | ||
const c = config; | ||
c.type = type; | ||
return c; | ||
} | ||
function Dispatch(_) { | ||
this._ = _; | ||
} | ||
function rnd(seed = Date.now()) { | ||
let s = typeof seed === 'number' ? seed : Array.from(seed).reduce((acc, v) => acc + v.charCodeAt(0), 0); | ||
return () => { | ||
s = (s * 9301 + 49297) % 233280; | ||
return s / 233280; | ||
}; | ||
} | ||
class WordCloudController extends chart_js.DatasetController { | ||
constructor() { | ||
super(...arguments); | ||
this.wordLayout = layout__default["default"]() | ||
.text((d) => d.text) | ||
.padding((d) => d.options.padding) | ||
.rotate((d) => d.options.rotate) | ||
.font((d) => d.options.family) | ||
.fontSize((d) => d.options.size) | ||
.fontStyle((d) => d.options.style) | ||
.fontWeight((d) => { var _a; return (_a = d.options.weight) !== null && _a !== void 0 ? _a : 1; }); | ||
this.rand = Math.random; | ||
} | ||
update(mode) { | ||
super.update(mode); | ||
this.rand = rnd(this.chart.id); | ||
const meta = this._cachedMeta; | ||
const elems = (meta.data || []); | ||
this.updateElements(elems, 0, elems.length, mode); | ||
} | ||
updateElements(elems, start, count, mode) { | ||
var _a, _b, _c, _d; | ||
this.wordLayout.stop(); | ||
const xScale = this._cachedMeta.xScale; | ||
const yScale = this._cachedMeta.yScale; | ||
const w = xScale.right - xScale.left; | ||
const h = yScale.bottom - yScale.top; | ||
const labels = this.chart.data.labels; | ||
const words = []; | ||
for (let i = start; i < start + count; i += 1) { | ||
const o = this.resolveDataElementOptions(i, mode); | ||
if (o.rotate == null) { | ||
o.rotate = WordElement.computeRotation(o, this.rand); | ||
} | ||
const properties = { | ||
options: { ...helpers.toFont(o), ...o }, | ||
x: (_b = (_a = this._cachedMeta.xScale) === null || _a === void 0 ? void 0 : _a.getPixelForDecimal(0.5)) !== null && _b !== void 0 ? _b : 0, | ||
y: (_d = (_c = this._cachedMeta.yScale) === null || _c === void 0 ? void 0 : _c.getPixelForDecimal(0.5)) !== null && _d !== void 0 ? _d : 0, | ||
width: 10, | ||
height: 10, | ||
scale: 1, | ||
index: i, | ||
text: labels[i], | ||
}; | ||
words.push(properties); | ||
} | ||
if (mode === 'reset') { | ||
words.forEach((tag) => { | ||
this.updateElement(elems[tag.index], tag.index, tag, mode); | ||
}); | ||
return; | ||
} | ||
this.wordLayout.random(this.rand).words(words); | ||
const run = (factor = 1, tries = 3) => { | ||
this.wordLayout | ||
.size([w * factor, h * factor]) | ||
.on('end', (tags, bounds) => { | ||
if (tags.length < labels.length) { | ||
if (tries > 0) { | ||
run(factor * 1.2, tries - 1); | ||
return; | ||
} | ||
console.warn('cannot fit all text elements in three tries'); | ||
} | ||
const wb = bounds[1].x - bounds[0].x; | ||
const hb = bounds[1].y - bounds[0].y; | ||
const dsOptions = this.options; | ||
const scale = dsOptions.fit ? Math.min(w / wb, h / hb) : 1; | ||
const indices = new Set(labels.map((_, i) => i)); | ||
tags.forEach((tag) => { | ||
indices.delete(tag.index); | ||
this.updateElement(elems[tag.index], tag.index, { | ||
options: tag.options, | ||
scale, | ||
x: xScale.left + scale * tag.x + w / 2, | ||
y: yScale.top + scale * tag.y + h / 2, | ||
width: scale * tag.width, | ||
height: scale * tag.height, | ||
text: tag.text, | ||
}, mode); | ||
}); | ||
indices.forEach((i) => this.updateElement(elems[i], i, { scale: 0 }, mode)); | ||
}) | ||
.start(); | ||
}; | ||
run(); | ||
} | ||
draw() { | ||
const elements = this._cachedMeta.data; | ||
const { ctx } = this.chart; | ||
elements.forEach((elem) => elem.draw(ctx)); | ||
} | ||
getLabelAndValue(index) { | ||
const r = super.getLabelAndValue(index); | ||
const labels = this.chart.data.labels; | ||
r.label = labels[index]; | ||
return r; | ||
} | ||
} | ||
WordCloudController.id = 'wordCloud'; | ||
WordCloudController.defaults = { | ||
datasets: { | ||
fit: true, | ||
animation: { | ||
colors: { | ||
properties: ['color', 'strokeStyle'], | ||
}, | ||
numbers: { | ||
properties: ['x', 'y', 'size', 'rotate'], | ||
}, | ||
}, | ||
}, | ||
maintainAspectRatio: false, | ||
dataElementType: WordElement.id, | ||
}; | ||
WordCloudController.overrides = { | ||
scales: { | ||
x: { | ||
type: 'linear', | ||
min: -1, | ||
max: 1, | ||
display: false, | ||
}, | ||
y: { | ||
type: 'linear', | ||
min: -1, | ||
max: 1, | ||
display: false, | ||
}, | ||
}, | ||
}; | ||
class WordCloudChart extends chart_js.Chart { | ||
constructor(item, config) { | ||
super(item, patchController('wordCloud', config, WordCloudController, WordElement)); | ||
} | ||
} | ||
WordCloudChart.id = WordCloudController.id; | ||
function parseTypenames(typenames, types) { | ||
return typenames.trim().split(/^|\s+/).map(function(t) { | ||
var name = "", i = t.indexOf("."); | ||
if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); | ||
if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); | ||
return {type: t, name: name}; | ||
}); | ||
} | ||
chart_js.registry.addControllers(WordCloudController); | ||
chart_js.registry.addElements(WordElement); | ||
Dispatch.prototype = dispatch.prototype = { | ||
constructor: Dispatch, | ||
on: function(typename, callback) { | ||
var _ = this._, | ||
T = parseTypenames(typename + "", _), | ||
t, | ||
i = -1, | ||
n = T.length; | ||
exports.WordCloudChart = WordCloudChart; | ||
exports.WordCloudController = WordCloudController; | ||
exports.WordElement = WordElement; | ||
// If no callback was specified, return the callback of the given type and name. | ||
if (arguments.length < 2) { | ||
while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t; | ||
return; | ||
} | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
// If a type was specified, set the callback for the given type and name. | ||
// Otherwise, if a null callback was specified, remove callbacks of the given name. | ||
if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); | ||
while (++i < n) { | ||
if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback); | ||
else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null); | ||
} | ||
return this; | ||
}, | ||
copy: function() { | ||
var copy = {}, _ = this._; | ||
for (var t in _) copy[t] = _[t].slice(); | ||
return new Dispatch(copy); | ||
}, | ||
call: function(type, that) { | ||
if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; | ||
if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); | ||
for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); | ||
}, | ||
apply: function(type, that, args) { | ||
if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); | ||
for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); | ||
} | ||
}; | ||
function get(type, name) { | ||
for (var i = 0, n = type.length, c; i < n; ++i) { | ||
if ((c = type[i]).name === name) { | ||
return c.value; | ||
} | ||
} | ||
} | ||
function set(type, name, callback) { | ||
for (var i = 0, n = type.length; i < n; ++i) { | ||
if (type[i].name === name) { | ||
type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1)); | ||
break; | ||
} | ||
} | ||
if (callback != null) type.push({name: name, value: callback}); | ||
return type; | ||
} | ||
exports.dispatch = dispatch; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
})); | ||
} (d3Dispatch, d3Dispatch.exports)); | ||
// Word cloud layout by Jason Davies, https://www.jasondavies.com/wordcloud/ | ||
// Algorithm due to Jonathan Feinberg, http://static.mrfeinberg.com/bv_ch03.pdf | ||
var dispatch = d3Dispatch.exports.dispatch; | ||
var cloudRadians = Math.PI / 180, | ||
cw = 1 << 11 >> 5, | ||
ch = 1 << 11; | ||
var d3Cloud = function() { | ||
var size = [256, 256], | ||
text = cloudText, | ||
font = cloudFont, | ||
fontSize = cloudFontSize, | ||
fontStyle = cloudFontNormal, | ||
fontWeight = cloudFontNormal, | ||
rotate = cloudRotate, | ||
padding = cloudPadding, | ||
spiral = archimedeanSpiral, | ||
words = [], | ||
timeInterval = Infinity, | ||
event = dispatch("word", "end"), | ||
timer = null, | ||
random = Math.random, | ||
cloud = {}, | ||
canvas = cloudCanvas; | ||
cloud.canvas = function(_) { | ||
return arguments.length ? (canvas = functor(_), cloud) : canvas; | ||
}; | ||
cloud.start = function() { | ||
var contextAndRatio = getContext(canvas()), | ||
board = zeroArray((size[0] >> 5) * size[1]), | ||
bounds = null, | ||
n = words.length, | ||
i = -1, | ||
tags = [], | ||
data = words.map(function(d, i) { | ||
d.text = text.call(this, d, i); | ||
d.font = font.call(this, d, i); | ||
d.style = fontStyle.call(this, d, i); | ||
d.weight = fontWeight.call(this, d, i); | ||
d.rotate = rotate.call(this, d, i); | ||
d.size = ~~fontSize.call(this, d, i); | ||
d.padding = padding.call(this, d, i); | ||
return d; | ||
}).sort(function(a, b) { return b.size - a.size; }); | ||
if (timer) clearInterval(timer); | ||
timer = setInterval(step, 0); | ||
step(); | ||
return cloud; | ||
function step() { | ||
var start = Date.now(); | ||
while (Date.now() - start < timeInterval && ++i < n && timer) { | ||
var d = data[i]; | ||
d.x = (size[0] * (random() + .5)) >> 1; | ||
d.y = (size[1] * (random() + .5)) >> 1; | ||
cloudSprite(contextAndRatio, d, data, i); | ||
if (d.hasText && place(board, d, bounds)) { | ||
tags.push(d); | ||
event.call("word", cloud, d); | ||
if (bounds) cloudBounds(bounds, d); | ||
else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}]; | ||
// Temporary hack | ||
d.x -= size[0] >> 1; | ||
d.y -= size[1] >> 1; | ||
} | ||
} | ||
if (i >= n) { | ||
cloud.stop(); | ||
event.call("end", cloud, tags, bounds); | ||
} | ||
} | ||
}; | ||
cloud.stop = function() { | ||
if (timer) { | ||
clearInterval(timer); | ||
timer = null; | ||
} | ||
return cloud; | ||
}; | ||
function getContext(canvas) { | ||
canvas.width = canvas.height = 1; | ||
var ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2); | ||
canvas.width = (cw << 5) / ratio; | ||
canvas.height = ch / ratio; | ||
var context = canvas.getContext("2d"); | ||
context.fillStyle = context.strokeStyle = "red"; | ||
context.textAlign = "center"; | ||
return {context: context, ratio: ratio}; | ||
} | ||
function place(board, tag, bounds) { | ||
[{x: 0, y: 0}, {x: size[0], y: size[1]}]; | ||
var startX = tag.x, | ||
startY = tag.y, | ||
maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]), | ||
s = spiral(size), | ||
dt = random() < .5 ? 1 : -1, | ||
t = -dt, | ||
dxdy, | ||
dx, | ||
dy; | ||
while (dxdy = s(t += dt)) { | ||
dx = ~~dxdy[0]; | ||
dy = ~~dxdy[1]; | ||
if (Math.min(Math.abs(dx), Math.abs(dy)) >= maxDelta) break; | ||
tag.x = startX + dx; | ||
tag.y = startY + dy; | ||
if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 || | ||
tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue; | ||
// TODO only check for collisions within current bounds. | ||
if (!bounds || !cloudCollide(tag, board, size[0])) { | ||
if (!bounds || collideRects(tag, bounds)) { | ||
var sprite = tag.sprite, | ||
w = tag.width >> 5, | ||
sw = size[0] >> 5, | ||
lx = tag.x - (w << 4), | ||
sx = lx & 0x7f, | ||
msx = 32 - sx, | ||
h = tag.y1 - tag.y0, | ||
x = (tag.y + tag.y0) * sw + (lx >> 5), | ||
last; | ||
for (var j = 0; j < h; j++) { | ||
last = 0; | ||
for (var i = 0; i <= w; i++) { | ||
board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0); | ||
} | ||
x += sw; | ||
} | ||
delete tag.sprite; | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
cloud.timeInterval = function(_) { | ||
return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval; | ||
}; | ||
cloud.words = function(_) { | ||
return arguments.length ? (words = _, cloud) : words; | ||
}; | ||
cloud.size = function(_) { | ||
return arguments.length ? (size = [+_[0], +_[1]], cloud) : size; | ||
}; | ||
cloud.font = function(_) { | ||
return arguments.length ? (font = functor(_), cloud) : font; | ||
}; | ||
cloud.fontStyle = function(_) { | ||
return arguments.length ? (fontStyle = functor(_), cloud) : fontStyle; | ||
}; | ||
cloud.fontWeight = function(_) { | ||
return arguments.length ? (fontWeight = functor(_), cloud) : fontWeight; | ||
}; | ||
cloud.rotate = function(_) { | ||
return arguments.length ? (rotate = functor(_), cloud) : rotate; | ||
}; | ||
cloud.text = function(_) { | ||
return arguments.length ? (text = functor(_), cloud) : text; | ||
}; | ||
cloud.spiral = function(_) { | ||
return arguments.length ? (spiral = spirals[_] || _, cloud) : spiral; | ||
}; | ||
cloud.fontSize = function(_) { | ||
return arguments.length ? (fontSize = functor(_), cloud) : fontSize; | ||
}; | ||
cloud.padding = function(_) { | ||
return arguments.length ? (padding = functor(_), cloud) : padding; | ||
}; | ||
cloud.random = function(_) { | ||
return arguments.length ? (random = _, cloud) : random; | ||
}; | ||
cloud.on = function() { | ||
var value = event.on.apply(event, arguments); | ||
return value === event ? cloud : value; | ||
}; | ||
return cloud; | ||
}; | ||
function cloudText(d) { | ||
return d.text; | ||
} | ||
function cloudFont() { | ||
return "serif"; | ||
} | ||
function cloudFontNormal() { | ||
return "normal"; | ||
} | ||
function cloudFontSize(d) { | ||
return Math.sqrt(d.value); | ||
} | ||
function cloudRotate() { | ||
return (~~(Math.random() * 6) - 3) * 30; | ||
} | ||
function cloudPadding() { | ||
return 1; | ||
} | ||
// Fetches a monochrome sprite bitmap for the specified text. | ||
// Load in batches for speed. | ||
function cloudSprite(contextAndRatio, d, data, di) { | ||
if (d.sprite) return; | ||
var c = contextAndRatio.context, | ||
ratio = contextAndRatio.ratio; | ||
c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio); | ||
var x = 0, | ||
y = 0, | ||
maxh = 0, | ||
n = data.length; | ||
--di; | ||
while (++di < n) { | ||
d = data[di]; | ||
c.save(); | ||
c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font; | ||
var w = c.measureText(d.text + "m").width * ratio, | ||
h = d.size << 1; | ||
if (d.rotate) { | ||
var sr = Math.sin(d.rotate * cloudRadians), | ||
cr = Math.cos(d.rotate * cloudRadians), | ||
wcr = w * cr, | ||
wsr = w * sr, | ||
hcr = h * cr, | ||
hsr = h * sr; | ||
w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5; | ||
h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr)); | ||
} else { | ||
w = (w + 0x1f) >> 5 << 5; | ||
} | ||
if (h > maxh) maxh = h; | ||
if (x + w >= (cw << 5)) { | ||
x = 0; | ||
y += maxh; | ||
maxh = 0; | ||
} | ||
if (y + h >= ch) break; | ||
c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio); | ||
if (d.rotate) c.rotate(d.rotate * cloudRadians); | ||
c.fillText(d.text, 0, 0); | ||
if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0); | ||
c.restore(); | ||
d.width = w; | ||
d.height = h; | ||
d.xoff = x; | ||
d.yoff = y; | ||
d.x1 = w >> 1; | ||
d.y1 = h >> 1; | ||
d.x0 = -d.x1; | ||
d.y0 = -d.y1; | ||
d.hasText = true; | ||
x += w; | ||
} | ||
var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data, | ||
sprite = []; | ||
while (--di >= 0) { | ||
d = data[di]; | ||
if (!d.hasText) continue; | ||
var w = d.width, | ||
w32 = w >> 5, | ||
h = d.y1 - d.y0; | ||
// Zero the buffer | ||
for (var i = 0; i < h * w32; i++) sprite[i] = 0; | ||
x = d.xoff; | ||
if (x == null) return; | ||
y = d.yoff; | ||
var seen = 0, | ||
seenRow = -1; | ||
for (var j = 0; j < h; j++) { | ||
for (var i = 0; i < w; i++) { | ||
var k = w32 * j + (i >> 5), | ||
m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0; | ||
sprite[k] |= m; | ||
seen |= m; | ||
} | ||
if (seen) seenRow = j; | ||
else { | ||
d.y0++; | ||
h--; | ||
j--; | ||
y++; | ||
} | ||
} | ||
d.y1 = d.y0 + seenRow; | ||
d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32); | ||
} | ||
} | ||
// Use mask-based collision detection. | ||
function cloudCollide(tag, board, sw) { | ||
sw >>= 5; | ||
var sprite = tag.sprite, | ||
w = tag.width >> 5, | ||
lx = tag.x - (w << 4), | ||
sx = lx & 0x7f, | ||
msx = 32 - sx, | ||
h = tag.y1 - tag.y0, | ||
x = (tag.y + tag.y0) * sw + (lx >> 5), | ||
last; | ||
for (var j = 0; j < h; j++) { | ||
last = 0; | ||
for (var i = 0; i <= w; i++) { | ||
if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0)) | ||
& board[x + i]) return true; | ||
} | ||
x += sw; | ||
} | ||
return false; | ||
} | ||
function cloudBounds(bounds, d) { | ||
var b0 = bounds[0], | ||
b1 = bounds[1]; | ||
if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0; | ||
if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0; | ||
if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1; | ||
if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1; | ||
} | ||
function collideRects(a, b) { | ||
return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y; | ||
} | ||
function archimedeanSpiral(size) { | ||
var e = size[0] / size[1]; | ||
return function(t) { | ||
return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)]; | ||
}; | ||
} | ||
function rectangularSpiral(size) { | ||
var dy = 4, | ||
dx = dy * size[0] / size[1], | ||
x = 0, | ||
y = 0; | ||
return function(t) { | ||
var sign = t < 0 ? -1 : 1; | ||
// See triangular numbers: T_n = n * (n + 1) / 2. | ||
switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) { | ||
case 0: x += dx; break; | ||
case 1: y += dy; break; | ||
case 2: x -= dx; break; | ||
default: y -= dy; break; | ||
} | ||
return [x, y]; | ||
}; | ||
} | ||
// TODO reuse arrays? | ||
function zeroArray(n) { | ||
var a = [], | ||
i = -1; | ||
while (++i < n) a[i] = 0; | ||
return a; | ||
} | ||
function cloudCanvas() { | ||
return document.createElement("canvas"); | ||
} | ||
function functor(d) { | ||
return typeof d === "function" ? d : function() { return d; }; | ||
} | ||
var spirals = { | ||
archimedean: archimedeanSpiral, | ||
rectangular: rectangularSpiral | ||
}; | ||
class WordElement extends chart_js.Element { | ||
static computeRotation(o, rnd) { | ||
if (o.rotationSteps <= 1) { | ||
return 0; | ||
} | ||
if (o.minRotation === o.maxRotation) { | ||
return o.minRotation; | ||
} | ||
const base = Math.min(o.rotationSteps, Math.floor(rnd() * o.rotationSteps)) / (o.rotationSteps - 1); | ||
const range = o.maxRotation - o.minRotation; | ||
return o.minRotation + base * range; | ||
} | ||
inRange(mouseX, mouseY) { | ||
const p = this.getProps(['x', 'y', 'width', 'height', 'scale']); | ||
if (p.scale <= 0) { | ||
return false; | ||
} | ||
const x = Number.isNaN(mouseX) ? p.x : mouseX; | ||
const y = Number.isNaN(mouseY) ? p.y : mouseY; | ||
return x >= p.x - p.width / 2 && x <= p.x + p.width / 2 && y >= p.y - p.height / 2 && y <= p.y + p.height / 2; | ||
} | ||
inXRange(mouseX) { | ||
return this.inRange(mouseX, Number.NaN); | ||
} | ||
inYRange(mouseY) { | ||
return this.inRange(Number.NaN, mouseY); | ||
} | ||
getCenterPoint() { | ||
return this.getProps(['x', 'y']); | ||
} | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
draw(ctx) { | ||
const { options } = this; | ||
const props = this.getProps(['x', 'y', 'width', 'height', 'text', 'scale']); | ||
if (props.scale <= 0) { | ||
return; | ||
} | ||
ctx.save(); | ||
const f = helpers.toFont({ ...options, size: options.size * props.scale }); | ||
ctx.font = f.string; | ||
ctx.fillStyle = options.color; | ||
ctx.textAlign = 'center'; | ||
ctx.translate(props.x, props.y); | ||
ctx.rotate((options.rotate / 180) * Math.PI); | ||
if (options.strokeStyle) { | ||
ctx.strokeStyle = options.strokeStyle; | ||
ctx.strokeText(props.text, 0, 0); | ||
} | ||
ctx.fillText(props.text, 0, 0); | ||
ctx.restore(); | ||
} | ||
} | ||
WordElement.id = 'word'; | ||
WordElement.defaults = { | ||
minRotation: -90, | ||
maxRotation: 0, | ||
rotationSteps: 2, | ||
padding: 1, | ||
strokeStyle: undefined, | ||
size: (ctx) => { | ||
const v = ctx.parsed.y; | ||
return v; | ||
}, | ||
hoverColor: '#ababab', | ||
}; | ||
WordElement.defaultRoutes = { | ||
color: 'color', | ||
family: 'font.family', | ||
style: 'font.style', | ||
weight: 'font.weight', | ||
lineHeight: 'font.lineHeight', | ||
}; | ||
function patchController(type, config, controller, elements = [], scales = []) { | ||
chart_js.registry.addControllers(controller); | ||
if (Array.isArray(elements)) { | ||
chart_js.registry.addElements(...elements); | ||
} | ||
else { | ||
chart_js.registry.addElements(elements); | ||
} | ||
if (Array.isArray(scales)) { | ||
chart_js.registry.addScales(...scales); | ||
} | ||
else { | ||
chart_js.registry.addScales(scales); | ||
} | ||
const c = config; | ||
c.type = type; | ||
return c; | ||
} | ||
function rnd(seed = Date.now()) { | ||
let s = typeof seed === 'number' ? seed : Array.from(seed).reduce((acc, v) => acc + v.charCodeAt(0), 0); | ||
return () => { | ||
s = (s * 9301 + 49297) % 233280; | ||
return s / 233280; | ||
}; | ||
} | ||
class WordCloudController extends chart_js.DatasetController { | ||
constructor() { | ||
super(...arguments); | ||
this.wordLayout = d3Cloud() | ||
.text((d) => d.text) | ||
.padding((d) => d.options.padding) | ||
.rotate((d) => d.options.rotate) | ||
.font((d) => d.options.family) | ||
.fontSize((d) => d.options.size) | ||
.fontStyle((d) => d.options.style) | ||
.fontWeight((d) => { var _a; return (_a = d.options.weight) !== null && _a !== void 0 ? _a : 1; }); | ||
this.rand = Math.random; | ||
} | ||
update(mode) { | ||
super.update(mode); | ||
this.rand = rnd(this.chart.id); | ||
const meta = this._cachedMeta; | ||
const elems = (meta.data || []); | ||
this.updateElements(elems, 0, elems.length, mode); | ||
} | ||
updateElements(elems, start, count, mode) { | ||
var _a, _b, _c, _d; | ||
this.wordLayout.stop(); | ||
const xScale = this._cachedMeta.xScale; | ||
const yScale = this._cachedMeta.yScale; | ||
const w = xScale.right - xScale.left; | ||
const h = yScale.bottom - yScale.top; | ||
const labels = this.chart.data.labels; | ||
const words = []; | ||
for (let i = start; i < start + count; i += 1) { | ||
const o = this.resolveDataElementOptions(i, mode); | ||
if (o.rotate == null) { | ||
o.rotate = WordElement.computeRotation(o, this.rand); | ||
} | ||
const properties = { | ||
options: { ...helpers.toFont(o), ...o }, | ||
x: (_b = (_a = this._cachedMeta.xScale) === null || _a === void 0 ? void 0 : _a.getPixelForDecimal(0.5)) !== null && _b !== void 0 ? _b : 0, | ||
y: (_d = (_c = this._cachedMeta.yScale) === null || _c === void 0 ? void 0 : _c.getPixelForDecimal(0.5)) !== null && _d !== void 0 ? _d : 0, | ||
width: 10, | ||
height: 10, | ||
scale: 1, | ||
index: i, | ||
text: labels[i], | ||
}; | ||
words.push(properties); | ||
} | ||
if (mode === 'reset') { | ||
words.forEach((tag) => { | ||
this.updateElement(elems[tag.index], tag.index, tag, mode); | ||
}); | ||
return; | ||
} | ||
this.wordLayout.random(this.rand).words(words); | ||
const run = (factor = 1, tries = 3) => { | ||
this.wordLayout | ||
.size([w * factor, h * factor]) | ||
.on('end', (tags, bounds) => { | ||
if (tags.length < labels.length) { | ||
if (tries > 0) { | ||
run(factor * 1.2, tries - 1); | ||
return; | ||
} | ||
console.warn('cannot fit all text elements in three tries'); | ||
} | ||
const wb = bounds[1].x - bounds[0].x; | ||
const hb = bounds[1].y - bounds[0].y; | ||
const dsOptions = this.options; | ||
const scale = dsOptions.fit ? Math.min(w / wb, h / hb) : 1; | ||
const indices = new Set(labels.map((_, i) => i)); | ||
tags.forEach((tag) => { | ||
indices.delete(tag.index); | ||
this.updateElement(elems[tag.index], tag.index, { | ||
options: tag.options, | ||
scale, | ||
x: xScale.left + scale * tag.x + w / 2, | ||
y: yScale.top + scale * tag.y + h / 2, | ||
width: scale * tag.width, | ||
height: scale * tag.height, | ||
text: tag.text, | ||
}, mode); | ||
}); | ||
indices.forEach((i) => this.updateElement(elems[i], i, { scale: 0 }, mode)); | ||
}) | ||
.start(); | ||
}; | ||
run(); | ||
} | ||
draw() { | ||
const elements = this._cachedMeta.data; | ||
const { ctx } = this.chart; | ||
elements.forEach((elem) => elem.draw(ctx)); | ||
} | ||
getLabelAndValue(index) { | ||
const r = super.getLabelAndValue(index); | ||
const labels = this.chart.data.labels; | ||
r.label = labels[index]; | ||
return r; | ||
} | ||
} | ||
WordCloudController.id = 'wordCloud'; | ||
WordCloudController.defaults = { | ||
datasets: { | ||
fit: true, | ||
animation: { | ||
colors: { | ||
properties: ['color', 'strokeStyle'], | ||
}, | ||
numbers: { | ||
properties: ['x', 'y', 'size', 'rotate'], | ||
}, | ||
}, | ||
}, | ||
maintainAspectRatio: false, | ||
dataElementType: WordElement.id, | ||
}; | ||
WordCloudController.overrides = { | ||
scales: { | ||
x: { | ||
type: 'linear', | ||
min: -1, | ||
max: 1, | ||
display: false, | ||
}, | ||
y: { | ||
type: 'linear', | ||
min: -1, | ||
max: 1, | ||
display: false, | ||
}, | ||
}, | ||
}; | ||
class WordCloudChart extends chart_js.Chart { | ||
constructor(item, config) { | ||
super(item, patchController('wordCloud', config, WordCloudController, WordElement)); | ||
} | ||
} | ||
WordCloudChart.id = WordCloudController.id; | ||
chart_js.registry.addControllers(WordCloudController); | ||
chart_js.registry.addElements(WordElement); | ||
exports.WordCloudChart = WordCloudChart; | ||
exports.WordCloudController = WordCloudController; | ||
exports.WordElement = WordElement; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
})); | ||
//# sourceMappingURL=index.umd.js.map |
@@ -1,2 +0,2 @@ | ||
!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).ChartWordCloud={},t.Chart,t.Chart.helpers)}(this,(function(t,e,n){"use strict";"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var r={exports:{}};!function(t,e){!function(t){var e={value:function(){}};function n(){for(var t,e=0,n=arguments.length,o={};e<n;++e){if(!(t=arguments[e]+"")||t in o||/[\s.]/.test(t))throw new Error("illegal type: "+t);o[t]=[]}return new r(o)}function r(t){this._=t}function o(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function a(t,e){for(var n,r=0,o=t.length;r<o;++r)if((n=t[r]).name===e)return n.value}function i(t,n,r){for(var o=0,a=t.length;o<a;++o)if(t[o].name===n){t[o]=e,t=t.slice(0,o).concat(t.slice(o+1));break}return null!=r&&t.push({name:n,value:r}),t}r.prototype=n.prototype={constructor:r,on:function(t,e){var n,r=this._,s=o(t+"",r),l=-1,u=s.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++l<u;)if(n=(t=s[l]).type)r[n]=i(r[n],t.name,e);else if(null==e)for(n in r)r[n]=i(r[n],t.name,null);return this}for(;++l<u;)if((n=(t=s[l]).type)&&(n=a(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new r(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,o=new Array(n),a=0;a<n;++a)o[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,o)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],o=0,a=r.length;o<a;++o)r[o].value.apply(e,n)}},t.dispatch=n,Object.defineProperty(t,"__esModule",{value:!0})}(e)}(0,r.exports);var o=r.exports.dispatch,a=Math.PI/180,i=2048,s=function(){var t=[256,256],e=l,n=u,r=c,a=h,s=h,b=f,M=d,S=g,E=[],C=1/0,R=o("word","end"),k=null,_=Math.random,P={},z=m;function T(e,n,r){t[0],t[1];for(var o,a,i,s,l,u=n.x,h=n.y,c=Math.sqrt(t[0]*t[0]+t[1]*t[1]),f=S(t),d=_()<.5?1:-1,y=-d;(o=f(y+=d))&&(a=~~o[0],i=~~o[1],!(Math.min(Math.abs(a),Math.abs(i))>=c));)if(n.x=u+a,n.y=h+i,!(n.x+n.x0<0||n.y+n.y0<0||n.x+n.x1>t[0]||n.y+n.y1>t[1]||r&&p(n,e,t[0])||r&&(l=r,!((s=n).x+s.x1>l[0].x&&s.x+s.x0<l[1].x&&s.y+s.y1>l[0].y&&s.y+s.y0<l[1].y)))){for(var x,g=n.sprite,m=n.width>>5,v=t[0]>>5,w=n.x-(m<<4),b=127&w,M=32-b,E=n.y1-n.y0,C=(n.y+n.y0)*v+(w>>5),R=0;R<E;R++){x=0;for(var k=0;k<=m;k++)e[C+k]|=x<<M|(k<m?(x=g[R*m+k])>>>b:0);C+=v}return delete n.sprite,!0}return!1}return P.canvas=function(t){return arguments.length?(z=v(t),P):z},P.start=function(){var o=function(t){t.width=t.height=1;var e=Math.sqrt(t.getContext("2d").getImageData(0,0,1,1).data.length>>2);t.width=2048/e,t.height=i/e;var n=t.getContext("2d");return n.fillStyle=n.strokeStyle="red",n.textAlign="center",{context:n,ratio:e}}(z()),l=function(t){var e=[],n=-1;for(;++n<t;)e[n]=0;return e}((t[0]>>5)*t[1]),u=null,h=E.length,c=-1,f=[],d=E.map((function(t,o){return t.text=e.call(this,t,o),t.font=n.call(this,t,o),t.style=a.call(this,t,o),t.weight=s.call(this,t,o),t.rotate=b.call(this,t,o),t.size=~~r.call(this,t,o),t.padding=M.call(this,t,o),t})).sort((function(t,e){return e.size-t.size}));return k&&clearInterval(k),k=setInterval(p,0),p(),P;function p(){for(var e=Date.now();Date.now()-e<C&&++c<h&&k;){var n=d[c];n.x=t[0]*(_()+.5)>>1,n.y=t[1]*(_()+.5)>>1,y(o,n,d,c),n.hasText&&T(l,n,u)&&(f.push(n),R.call("word",P,n),u?x(u,n):u=[{x:n.x+n.x0,y:n.y+n.y0},{x:n.x+n.x1,y:n.y+n.y1}],n.x-=t[0]>>1,n.y-=t[1]>>1)}c>=h&&(P.stop(),R.call("end",P,f,u))}},P.stop=function(){return k&&(clearInterval(k),k=null),P},P.timeInterval=function(t){return arguments.length?(C=null==t?1/0:t,P):C},P.words=function(t){return arguments.length?(E=t,P):E},P.size=function(e){return arguments.length?(t=[+e[0],+e[1]],P):t},P.font=function(t){return arguments.length?(n=v(t),P):n},P.fontStyle=function(t){return arguments.length?(a=v(t),P):a},P.fontWeight=function(t){return arguments.length?(s=v(t),P):s},P.rotate=function(t){return arguments.length?(b=v(t),P):b},P.text=function(t){return arguments.length?(e=v(t),P):e},P.spiral=function(t){return arguments.length?(S=w[t]||t,P):S},P.fontSize=function(t){return arguments.length?(r=v(t),P):r},P.padding=function(t){return arguments.length?(M=v(t),P):M},P.random=function(t){return arguments.length?(_=t,P):_},P.on=function(){var t=R.on.apply(R,arguments);return t===R?P:t},P};function l(t){return t.text}function u(){return"serif"}function h(){return"normal"}function c(t){return Math.sqrt(t.value)}function f(){return 30*(~~(6*Math.random())-3)}function d(){return 1}function y(t,e,n,r){if(!e.sprite){var o=t.context,s=t.ratio;o.clearRect(0,0,2048/s,i/s);var l=0,u=0,h=0,c=n.length;for(--r;++r<c;){e=n[r],o.save(),o.font=e.style+" "+e.weight+" "+~~((e.size+1)/s)+"px "+e.font;var f=o.measureText(e.text+"m").width*s,d=e.size<<1;if(e.rotate){var y=Math.sin(e.rotate*a),p=Math.cos(e.rotate*a),x=f*p,g=f*y,m=d*p,v=d*y;f=Math.max(Math.abs(x+v),Math.abs(x-v))+31>>5<<5,d=~~Math.max(Math.abs(g+m),Math.abs(g-m))}else f=f+31>>5<<5;if(d>h&&(h=d),l+f>=2048&&(l=0,u+=h,h=0),u+d>=i)break;o.translate((l+(f>>1))/s,(u+(d>>1))/s),e.rotate&&o.rotate(e.rotate*a),o.fillText(e.text,0,0),e.padding&&(o.lineWidth=2*e.padding,o.strokeText(e.text,0,0)),o.restore(),e.width=f,e.height=d,e.xoff=l,e.yoff=u,e.x1=f>>1,e.y1=d>>1,e.x0=-e.x1,e.y0=-e.y1,e.hasText=!0,l+=f}for(var w=o.getImageData(0,0,2048/s,i/s).data,b=[];--r>=0;)if((e=n[r]).hasText){for(var M=(f=e.width)>>5,S=(d=e.y1-e.y0,0);S<d*M;S++)b[S]=0;if(null==(l=e.xoff))return;u=e.yoff;for(var E=0,C=-1,R=0;R<d;R++){for(S=0;S<f;S++){var k=M*R+(S>>5),_=w[2048*(u+R)+(l+S)<<2]?1<<31-S%32:0;b[k]|=_,E|=_}E?C=R:(e.y0++,d--,R--,u++)}e.y1=e.y0+C,e.sprite=b.slice(0,(e.y1-e.y0)*M)}}}function p(t,e,n){n>>=5;for(var r,o=t.sprite,a=t.width>>5,i=t.x-(a<<4),s=127&i,l=32-s,u=t.y1-t.y0,h=(t.y+t.y0)*n+(i>>5),c=0;c<u;c++){r=0;for(var f=0;f<=a;f++)if((r<<l|(f<a?(r=o[c*a+f])>>>s:0))&e[h+f])return!0;h+=n}return!1}function x(t,e){var n=t[0],r=t[1];e.x+e.x0<n.x&&(n.x=e.x+e.x0),e.y+e.y0<n.y&&(n.y=e.y+e.y0),e.x+e.x1>r.x&&(r.x=e.x+e.x1),e.y+e.y1>r.y&&(r.y=e.y+e.y1)}function g(t){var e=t[0]/t[1];return function(t){return[e*(t*=.1)*Math.cos(t),t*Math.sin(t)]}}function m(){return document.createElement("canvas")}function v(t){return"function"==typeof t?t:function(){return t}}var w={archimedean:g,rectangular:function(t){var e=4*t[0]/t[1],n=0,r=0;return function(t){var o=t<0?-1:1;switch(Math.sqrt(1+4*o*t)-o&3){case 0:n+=e;break;case 1:r+=4;break;case 2:n-=e;break;default:r-=4}return[n,r]}}};class b extends e.Element{static computeRotation(t,e){if(t.rotationSteps<=1)return 0;if(t.minRotation===t.maxRotation)return t.minRotation;const n=Math.min(t.rotationSteps,Math.floor(e()*t.rotationSteps))/(t.rotationSteps-1),r=t.maxRotation-t.minRotation;return t.minRotation+n*r}inRange(t,e){const n=this.getProps(["x","y","width","height","scale"]);if(n.scale<=0)return!1;const r=Number.isNaN(t)?n.x:t,o=Number.isNaN(e)?n.y:e;return r>=n.x-n.width/2&&r<=n.x+n.width/2&&o>=n.y-n.height/2&&o<=n.y+n.height/2}inXRange(t){return this.inRange(t,Number.NaN)}inYRange(t){return this.inRange(Number.NaN,t)}getCenterPoint(){return this.getProps(["x","y"])}tooltipPosition(){return this.getCenterPoint()}draw(t){const{options:e}=this,r=this.getProps(["x","y","width","height","text","scale"]);if(r.scale<=0)return;t.save();const o=n.toFont({...e,size:e.size*r.scale});t.font=o.string,t.fillStyle=e.color,t.textAlign="center",t.translate(r.x,r.y),t.rotate(e.rotate/180*Math.PI),e.strokeStyle&&(t.strokeStyle=e.strokeStyle,t.strokeText(r.text,0,0)),t.fillText(r.text,0,0),t.restore()}}b.id="word",b.defaults={minRotation:-90,maxRotation:0,rotationSteps:2,padding:1,strokeStyle:void 0,size:t=>t.parsed.y,hoverColor:"#ababab"},b.defaultRoutes={color:"color",family:"font.family",style:"font.style",weight:"font.weight",lineHeight:"font.lineHeight"};class M extends e.DatasetController{constructor(){super(...arguments),this.wordLayout=s().text((t=>t.text)).padding((t=>t.options.padding)).rotate((t=>t.options.rotate)).font((t=>t.options.family)).fontSize((t=>t.options.size)).fontStyle((t=>t.options.style)).fontWeight((t=>{var e;return null!==(e=t.options.weight)&&void 0!==e?e:1})),this.rand=Math.random}update(t){super.update(t),this.rand=function(t=Date.now()){let e="number"==typeof t?t:Array.from(t).reduce(((t,e)=>t+e.charCodeAt(0)),0);return()=>(e=(9301*e+49297)%233280,e/233280)}(this.chart.id);const e=this._cachedMeta.data||[];this.updateElements(e,0,e.length,t)}updateElements(t,e,r,o){var a,i,s,l;this.wordLayout.stop();const u=this._cachedMeta.xScale,h=this._cachedMeta.yScale,c=u.right-u.left,f=h.bottom-h.top,d=this.chart.data.labels,y=[];for(let t=e;t<e+r;t+=1){const e=this.resolveDataElementOptions(t,o);null==e.rotate&&(e.rotate=b.computeRotation(e,this.rand));const r={options:{...n.toFont(e),...e},x:null!==(i=null===(a=this._cachedMeta.xScale)||void 0===a?void 0:a.getPixelForDecimal(.5))&&void 0!==i?i:0,y:null!==(l=null===(s=this._cachedMeta.yScale)||void 0===s?void 0:s.getPixelForDecimal(.5))&&void 0!==l?l:0,width:10,height:10,scale:1,index:t,text:d[t]};y.push(r)}if("reset"===o)return void y.forEach((e=>{this.updateElement(t[e.index],e.index,e,o)}));this.wordLayout.random(this.rand).words(y);const p=(e=1,n=3)=>{this.wordLayout.size([c*e,f*e]).on("end",((r,a)=>{if(r.length<d.length){if(n>0)return void p(1.2*e,n-1);console.warn("cannot fit all text elements in three tries")}const i=a[1].x-a[0].x,s=a[1].y-a[0].y,l=this.options.fit?Math.min(c/i,f/s):1,y=new Set(d.map(((t,e)=>e)));r.forEach((e=>{y.delete(e.index),this.updateElement(t[e.index],e.index,{options:e.options,scale:l,x:u.left+l*e.x+c/2,y:h.top+l*e.y+f/2,width:l*e.width,height:l*e.height,text:e.text},o)})),y.forEach((e=>this.updateElement(t[e],e,{scale:0},o)))})).start()};p()}draw(){const t=this._cachedMeta.data,{ctx:e}=this.chart;t.forEach((t=>t.draw(e)))}getLabelAndValue(t){const e=super.getLabelAndValue(t),n=this.chart.data.labels;return e.label=n[t],e}}M.id="wordCloud",M.defaults={datasets:{fit:!0,animation:{colors:{properties:["color","strokeStyle"]},numbers:{properties:["x","y","size","rotate"]}}},maintainAspectRatio:!1,dataElementType:b.id},M.overrides={scales:{x:{type:"linear",min:-1,max:1,display:!1},y:{type:"linear",min:-1,max:1,display:!1}}};class S extends e.Chart{constructor(t,n){super(t,function(t,n,r,o=[],a=[]){e.registry.addControllers(r),Array.isArray(o)?e.registry.addElements(...o):e.registry.addElements(o),Array.isArray(a)?e.registry.addScales(...a):e.registry.addScales(a);const i=n;return i.type=t,i}("wordCloud",n,M,b))}}S.id=M.id,e.registry.addControllers(M),e.registry.addElements(b),t.WordCloudChart=S,t.WordCloudController=M,t.WordElement=b,Object.defineProperty(t,"__esModule",{value:!0})})); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("chart.js"),require("chart.js/helpers"),require("d3-cloud")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers","d3-cloud"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).ChartWordCloud={},t.Chart,t.Chart.helpers,t.layout)}(this,(function(t,e,o,i){"use strict";function r(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var a=r(i);class n extends e.Element{static computeRotation(t,e){if(t.rotationSteps<=1)return 0;if(t.minRotation===t.maxRotation)return t.minRotation;const o=Math.min(t.rotationSteps,Math.floor(e()*t.rotationSteps))/(t.rotationSteps-1),i=t.maxRotation-t.minRotation;return t.minRotation+o*i}inRange(t,e){const o=this.getProps(["x","y","width","height","scale"]);if(o.scale<=0)return!1;const i=Number.isNaN(t)?o.x:t,r=Number.isNaN(e)?o.y:e;return i>=o.x-o.width/2&&i<=o.x+o.width/2&&r>=o.y-o.height/2&&r<=o.y+o.height/2}inXRange(t){return this.inRange(t,Number.NaN)}inYRange(t){return this.inRange(Number.NaN,t)}getCenterPoint(){return this.getProps(["x","y"])}tooltipPosition(){return this.getCenterPoint()}draw(t){const{options:e}=this,i=this.getProps(["x","y","width","height","text","scale"]);if(i.scale<=0)return;t.save();const r=o.toFont({...e,size:e.size*i.scale});t.font=r.string,t.fillStyle=e.color,t.textAlign="center",t.translate(i.x,i.y),t.rotate(e.rotate/180*Math.PI),e.strokeStyle&&(t.strokeStyle=e.strokeStyle,t.strokeText(i.text,0,0)),t.fillText(i.text,0,0),t.restore()}}n.id="word",n.defaults={minRotation:-90,maxRotation:0,rotationSteps:2,padding:1,strokeStyle:void 0,size:t=>t.parsed.y,hoverColor:"#ababab"},n.defaultRoutes={color:"color",family:"font.family",style:"font.style",weight:"font.weight",lineHeight:"font.lineHeight"};class s extends e.DatasetController{constructor(){super(...arguments),this.wordLayout=a.default().text((t=>t.text)).padding((t=>t.options.padding)).rotate((t=>t.options.rotate)).font((t=>t.options.family)).fontSize((t=>t.options.size)).fontStyle((t=>t.options.style)).fontWeight((t=>{var e;return null!==(e=t.options.weight)&&void 0!==e?e:1})),this.rand=Math.random}update(t){super.update(t),this.rand=function(t=Date.now()){let e="number"==typeof t?t:Array.from(t).reduce(((t,e)=>t+e.charCodeAt(0)),0);return()=>(e=(9301*e+49297)%233280,e/233280)}(this.chart.id);const e=this._cachedMeta.data||[];this.updateElements(e,0,e.length,t)}updateElements(t,e,i,r){var a,s,l,d;this.wordLayout.stop();const h=this._cachedMeta.xScale,c=this._cachedMeta.yScale,u=h.right-h.left,p=c.bottom-c.top,y=this.chart.data.labels,f=[];for(let t=e;t<e+i;t+=1){const e=this.resolveDataElementOptions(t,r);null==e.rotate&&(e.rotate=n.computeRotation(e,this.rand));const i={options:{...o.toFont(e),...e},x:null!==(s=null===(a=this._cachedMeta.xScale)||void 0===a?void 0:a.getPixelForDecimal(.5))&&void 0!==s?s:0,y:null!==(d=null===(l=this._cachedMeta.yScale)||void 0===l?void 0:l.getPixelForDecimal(.5))&&void 0!==d?d:0,width:10,height:10,scale:1,index:t,text:y[t]};f.push(i)}if("reset"===r)return void f.forEach((e=>{this.updateElement(t[e.index],e.index,e,r)}));this.wordLayout.random(this.rand).words(f);const m=(e=1,o=3)=>{this.wordLayout.size([u*e,p*e]).on("end",((i,a)=>{if(i.length<y.length){if(o>0)return void m(1.2*e,o-1);console.warn("cannot fit all text elements in three tries")}const n=a[1].x-a[0].x,s=a[1].y-a[0].y,l=this.options.fit?Math.min(u/n,p/s):1,d=new Set(y.map(((t,e)=>e)));i.forEach((e=>{d.delete(e.index),this.updateElement(t[e.index],e.index,{options:e.options,scale:l,x:h.left+l*e.x+u/2,y:c.top+l*e.y+p/2,width:l*e.width,height:l*e.height,text:e.text},r)})),d.forEach((e=>this.updateElement(t[e],e,{scale:0},r)))})).start()};m()}draw(){const t=this._cachedMeta.data,{ctx:e}=this.chart;t.forEach((t=>t.draw(e)))}getLabelAndValue(t){const e=super.getLabelAndValue(t),o=this.chart.data.labels;return e.label=o[t],e}}s.id="wordCloud",s.defaults={datasets:{fit:!0,animation:{colors:{properties:["color","strokeStyle"]},numbers:{properties:["x","y","size","rotate"]}}},maintainAspectRatio:!1,dataElementType:n.id},s.overrides={scales:{x:{type:"linear",min:-1,max:1,display:!1},y:{type:"linear",min:-1,max:1,display:!1}}};class l extends e.Chart{constructor(t,o){super(t,function(t,o,i,r=[],a=[]){e.registry.addControllers(i),Array.isArray(r)?e.registry.addElements(...r):e.registry.addElements(r),Array.isArray(a)?e.registry.addScales(...a):e.registry.addScales(a);const n=o;return n.type=t,n}("wordCloud",o,s,n))}}l.id=s.id,e.registry.addControllers(s),e.registry.addElements(n),t.WordCloudChart=l,t.WordCloudController=s,t.WordElement=n,Object.defineProperty(t,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=index.umd.min.js.map |
{ | ||
"name": "chartjs-chart-wordcloud", | ||
"description": "Chart.js module for word clouds", | ||
"version": "3.8.0", | ||
"version": "3.9.0", | ||
"author": { | ||
@@ -26,4 +26,4 @@ "name": "Samuel Gratzl", | ||
"module": "build/index.js", | ||
"main": "build/index.cjs.js", | ||
"browser": "build/index.umd.js", | ||
"main": "build/index.cjs", | ||
"node": "build/index.cjs", | ||
"unpkg": "build/index.umd.min.js", | ||
@@ -38,3 +38,3 @@ "jsdelivr": "build/index.umd.min.js", | ||
"peerDependencies": { | ||
"chart.js": "^3.8.0" | ||
"chart.js": "^3.9.1" | ||
}, | ||
@@ -48,17 +48,17 @@ "browserslist": [ | ||
"@chiogen/rollup-plugin-terser": "^7.0.2", | ||
"@rollup/plugin-commonjs": "^22.0.0", | ||
"@rollup/plugin-commonjs": "^22.0.2", | ||
"@rollup/plugin-node-resolve": "^13.3.0", | ||
"@rollup/plugin-replace": "^4.0.0", | ||
"@rollup/plugin-typescript": "^8.3.3", | ||
"@types/jest": "^28.1.1", | ||
"@types/jest-image-snapshot": "^4.3.1", | ||
"@types/node": "^17.0.42", | ||
"@rollup/plugin-typescript": "^8.3.4", | ||
"@types/jest": "^28.1.6", | ||
"@types/jest-image-snapshot": "^5.1.0", | ||
"@types/node": "^18.7.3", | ||
"@types/seedrandom": "^3.0.2", | ||
"@typescript-eslint/eslint-plugin": "^5.27.1", | ||
"@typescript-eslint/parser": "^5.27.1", | ||
"@typescript-eslint/eslint-plugin": "^5.33.0", | ||
"@typescript-eslint/parser": "^5.33.0", | ||
"@yarnpkg/sdks": "^2.6.2", | ||
"canvas": "^2.9.1", | ||
"canvas": "^2.9.3", | ||
"canvas-5-polyfill": "^0.1.5", | ||
"chart.js": "^3.8.0", | ||
"eslint": "^8.17.0", | ||
"chart.js": "^3.9.1", | ||
"eslint": "^8.22.0", | ||
"eslint-config-airbnb-typescript": "^17.0.0", | ||
@@ -69,19 +69,19 @@ "eslint-config-prettier": "^8.5.0", | ||
"eslint-plugin-import": "^2.26.0", | ||
"eslint-plugin-jsx-a11y": "^6.5.1", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"eslint-plugin-react": "^7.30.0", | ||
"eslint-plugin-react-hooks": "^4.5.0", | ||
"jest": "^28.1.1", | ||
"jest-environment-jsdom": "^28.1.1", | ||
"eslint-plugin-jsx-a11y": "^6.6.1", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"eslint-plugin-react": "^7.30.1", | ||
"eslint-plugin-react-hooks": "^4.6.0", | ||
"jest": "^28.1.3", | ||
"jest-environment-jsdom": "^28.1.3", | ||
"jest-image-snapshot": "^5.1.0", | ||
"prettier": "^2.6.2", | ||
"prettier": "^2.7.1", | ||
"rimraf": "^3.0.2", | ||
"rollup": "~2.75.6", | ||
"rollup": "^2.78.0", | ||
"rollup-plugin-cleanup": "^3.2.1", | ||
"rollup-plugin-dts": "^4.2.2", | ||
"seedrandom": "^3.0.5", | ||
"ts-jest": "^28.0.4", | ||
"ts-jest": "^28.0.7", | ||
"tslib": "^2.4.0", | ||
"typedoc": "^0.22.17", | ||
"typescript": "^4.7.3", | ||
"typedoc": "^0.23.10", | ||
"typescript": "^4.7.4", | ||
"us-atlas": "^3.0.0", | ||
@@ -114,3 +114,3 @@ "world-atlas": "^2.0.2" | ||
}, | ||
"packageManager": "yarn@3.2.1" | ||
"packageManager": "yarn@3.2.2" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
97466
1379