echarts-wordcloud
Advanced tools
Comparing version 2.0.0 to 2.1.0-beta
@@ -30,57 +30,57 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
external_echarts_.extendSeriesModel({ | ||
type: 'series.wordCloud', | ||
type: 'series.wordCloud', | ||
visualStyleAccessPath: 'textStyle', | ||
visualStyleMapper: function (model) { | ||
return { | ||
fill: model.get('color') | ||
}; | ||
}, | ||
visualDrawType: 'fill', | ||
visualStyleAccessPath: 'textStyle', | ||
visualStyleMapper: function (model) { | ||
return { | ||
fill: model.get('color') | ||
}; | ||
}, | ||
visualDrawType: 'fill', | ||
optionUpdated: function () { | ||
var option = this.option; | ||
option.gridSize = Math.max(Math.floor(option.gridSize), 4); | ||
}, | ||
optionUpdated: function () { | ||
var option = this.option; | ||
option.gridSize = Math.max(Math.floor(option.gridSize), 4); | ||
}, | ||
getInitialData: function (option, ecModel) { | ||
var dimensions = external_echarts_.helper.createDimensions(option.data, { | ||
coordDimensions: ['value'] | ||
}); | ||
var list = new external_echarts_.List(dimensions, this); | ||
list.initData(option.data); | ||
return list; | ||
}, | ||
getInitialData: function (option, ecModel) { | ||
var dimensions = external_echarts_.helper.createDimensions(option.data, { | ||
coordDimensions: ['value'] | ||
}); | ||
var list = new external_echarts_.List(dimensions, this); | ||
list.initData(option.data); | ||
return list; | ||
}, | ||
// Most of options are from https://github.com/timdream/wordcloud2.js/blob/gh-pages/API.md | ||
defaultOption: { | ||
maskImage: null, | ||
// Most of options are from https://github.com/timdream/wordcloud2.js/blob/gh-pages/API.md | ||
defaultOption: { | ||
// Shape can be 'circle', 'cardioid', 'diamond', 'triangle-forward', 'triangle', 'pentagon', 'star' | ||
shape: 'circle', | ||
keepAspect: false, | ||
maskImage: null, | ||
left: 'center', | ||
// Shape can be 'circle', 'cardioid', 'diamond', 'triangle-forward', 'triangle', 'pentagon', 'star' | ||
shape: 'circle', | ||
top: 'center', | ||
left: 'center', | ||
width: '70%', | ||
top: 'center', | ||
height: '80%', | ||
width: '70%', | ||
sizeRange: [12, 60], | ||
height: '80%', | ||
rotationRange: [-90, 90], | ||
sizeRange: [12, 60], | ||
rotationStep: 45, | ||
rotationRange: [-90, 90], | ||
gridSize: 8, | ||
rotationStep: 45, | ||
drawOutOfBound: false, | ||
shrinkToFit: false, | ||
gridSize: 8, | ||
drawOutOfBound: false, | ||
textStyle: { | ||
fontWeight: 'normal' | ||
} | ||
textStyle: { | ||
fontWeight: 'normal' | ||
} | ||
} | ||
}); | ||
@@ -92,71 +92,78 @@ | ||
external_echarts_.extendChartView({ | ||
type: 'wordCloud', | ||
type: 'wordCloud', | ||
render: function (seriesModel, ecModel, api) { | ||
var group = this.group; | ||
group.removeAll(); | ||
render: function (seriesModel, ecModel, api) { | ||
var group = this.group; | ||
group.removeAll(); | ||
var data = seriesModel.getData(); | ||
var data = seriesModel.getData(); | ||
var gridSize = seriesModel.get('gridSize'); | ||
var gridSize = seriesModel.get('gridSize'); | ||
seriesModel.layoutInstance.ondraw = function (text, size, dataIdx, drawn) { | ||
var itemModel = data.getItemModel(dataIdx); | ||
var textStyleModel = itemModel.getModel('textStyle'); | ||
seriesModel.layoutInstance.ondraw = function (text, size, dataIdx, drawn) { | ||
var itemModel = data.getItemModel(dataIdx); | ||
var textStyleModel = itemModel.getModel('textStyle'); | ||
var textEl = new external_echarts_.graphic.Text({ | ||
style: external_echarts_.helper.createTextStyle(textStyleModel), | ||
scaleX: 1 / drawn.info.mu, | ||
scaleY: 1 / drawn.info.mu, | ||
x: (drawn.gx + drawn.info.gw / 2) * gridSize, | ||
y: (drawn.gy + drawn.info.gh / 2) * gridSize, | ||
rotation: drawn.rot | ||
}); | ||
textEl.setStyle({ | ||
x: drawn.info.fillTextOffsetX, | ||
y: drawn.info.fillTextOffsetY + size * 0.5, | ||
text: text, | ||
verticalAlign: 'middle', | ||
fill: data.getItemVisual(dataIdx, 'style').fill, | ||
fontSize: size | ||
}); | ||
var textEl = new external_echarts_.graphic.Text({ | ||
style: external_echarts_.helper.createTextStyle(textStyleModel), | ||
scaleX: 1 / drawn.info.mu, | ||
scaleY: 1 / drawn.info.mu, | ||
x: (drawn.gx + drawn.info.gw / 2) * gridSize, | ||
y: (drawn.gy + drawn.info.gh / 2) * gridSize, | ||
rotation: drawn.rot | ||
}); | ||
textEl.setStyle({ | ||
x: drawn.info.fillTextOffsetX, | ||
y: drawn.info.fillTextOffsetY + size * 0.5, | ||
text: text, | ||
verticalAlign: 'middle', | ||
fill: data.getItemVisual(dataIdx, 'style').fill, | ||
fontSize: size | ||
}); | ||
group.add(textEl); | ||
group.add(textEl); | ||
data.setItemGraphicEl(dataIdx, textEl); | ||
data.setItemGraphicEl(dataIdx, textEl); | ||
textEl.ensureState('emphasis').style = external_echarts_.helper.createTextStyle( | ||
itemModel.getModel(['emphasis', 'textStyle']), | ||
{ | ||
state: 'emphasis' | ||
} | ||
); | ||
textEl.ensureState('blur').style = external_echarts_.helper.createTextStyle( | ||
itemModel.getModel(['blur', 'textStyle']), | ||
{ | ||
state: 'blur' | ||
} | ||
); | ||
textEl.ensureState('emphasis').style = external_echarts_.helper.createTextStyle(itemModel.getModel(['emphasis', 'textStyle']), { | ||
state: 'emphasis' | ||
}); | ||
textEl.ensureState('blur').style = external_echarts_.helper.createTextStyle(itemModel.getModel(['blur', 'textStyle']), { | ||
state: 'blur' | ||
}); | ||
external_echarts_.helper.enableHoverEmphasis( | ||
textEl, | ||
itemModel.get(['emphasis', 'focus']), | ||
itemModel.get(['emphasis', 'blurScope']) | ||
); | ||
external_echarts_.helper.enableHoverEmphasis( | ||
textEl, | ||
itemModel.get(['emphasis', 'focus']), | ||
itemModel.get(['emphasis', 'blurScope']) | ||
); | ||
textEl.stateTransition = { | ||
duration: seriesModel.get('animation') | ||
? seriesModel.get(['stateAnimation', 'duration']) | ||
: 0, | ||
easing: seriesModel.get(['stateAnimation', 'easing']) | ||
}; | ||
// TODO | ||
textEl.__highDownDispatcher = true; | ||
}; | ||
textEl.stateTransition = { | ||
duration: seriesModel.get('animation') ? seriesModel.get(['stateAnimation', 'duration']) : 0, | ||
easing: seriesModel.get(['stateAnimation', 'easing']) | ||
}; | ||
// TODO | ||
textEl.__highDownDispatcher = true; | ||
}; | ||
this._model = seriesModel; | ||
}, | ||
this._model = seriesModel; | ||
}, | ||
remove: function () { | ||
this.group.removeAll(); | ||
remove: function () { | ||
this.group.removeAll(); | ||
this._model.layoutInstance.dispose(); | ||
}, | ||
this._model.layoutInstance.dispose(); | ||
}, | ||
dispose: function () { | ||
this._model.layoutInstance.dispose(); | ||
} | ||
dispose: function () { | ||
this._model.layoutInstance.dispose(); | ||
} | ||
}); | ||
@@ -169,3 +176,3 @@ | ||
* | ||
* Copyright 2011 - 2013 Tim Chien | ||
* Copyright 2011 - 2019 Tim Guan-tin Chien and contributors. | ||
* Released under the MIT license | ||
@@ -179,60 +186,68 @@ */ | ||
window.setImmediate = (function setupSetImmediate() { | ||
return window.msSetImmediate || | ||
window.webkitSetImmediate || | ||
window.mozSetImmediate || | ||
window.oSetImmediate || | ||
(function setupSetZeroTimeout() { | ||
if (!window.postMessage || !window.addEventListener) { | ||
return null; | ||
} | ||
return ( | ||
window.msSetImmediate || | ||
window.webkitSetImmediate || | ||
window.mozSetImmediate || | ||
window.oSetImmediate || | ||
(function setupSetZeroTimeout() { | ||
if (!window.postMessage || !window.addEventListener) { | ||
return null; | ||
} | ||
var callbacks = [undefined]; | ||
var message = 'zero-timeout-message'; | ||
var callbacks = [undefined]; | ||
var message = 'zero-timeout-message'; | ||
// Like setTimeout, but only takes a function argument. There's | ||
// no time argument (always zero) and no arguments (you have to | ||
// use a closure). | ||
var setZeroTimeout = function setZeroTimeout(callback) { | ||
var id = callbacks.length; | ||
callbacks.push(callback); | ||
window.postMessage(message + id.toString(36), '*'); | ||
// Like setTimeout, but only takes a function argument. There's | ||
// no time argument (always zero) and no arguments (you have to | ||
// use a closure). | ||
var setZeroTimeout = function setZeroTimeout(callback) { | ||
var id = callbacks.length; | ||
callbacks.push(callback); | ||
window.postMessage(message + id.toString(36), '*'); | ||
return id; | ||
}; | ||
return id; | ||
}; | ||
window.addEventListener('message', function setZeroTimeoutMessage(evt) { | ||
// Skipping checking event source, retarded IE confused this window | ||
// object with another in the presence of iframe | ||
if (typeof evt.data !== 'string' || | ||
evt.data.substr(0, message.length) !== message/* || | ||
evt.source !== window */) { | ||
return; | ||
} | ||
window.addEventListener( | ||
'message', | ||
function setZeroTimeoutMessage(evt) { | ||
// Skipping checking event source, retarded IE confused this window | ||
// object with another in the presence of iframe | ||
if ( | ||
typeof evt.data !== 'string' || | ||
evt.data.substr(0, message.length) !== message /* || | ||
evt.source !== window */ | ||
) { | ||
return; | ||
} | ||
evt.stopImmediatePropagation(); | ||
evt.stopImmediatePropagation(); | ||
var id = parseInt(evt.data.substr(message.length), 36); | ||
if (!callbacks[id]) { | ||
return; | ||
} | ||
var id = parseInt(evt.data.substr(message.length), 36); | ||
if (!callbacks[id]) { | ||
return; | ||
} | ||
callbacks[id](); | ||
callbacks[id] = undefined; | ||
}, true); | ||
callbacks[id](); | ||
callbacks[id] = undefined; | ||
}, | ||
true | ||
); | ||
/* specify clearImmediate() here since we need the scope */ | ||
window.clearImmediate = function clearZeroTimeout(id) { | ||
if (!callbacks[id]) { | ||
return; | ||
} | ||
/* specify clearImmediate() here since we need the scope */ | ||
window.clearImmediate = function clearZeroTimeout(id) { | ||
if (!callbacks[id]) { | ||
return; | ||
} | ||
callbacks[id] = undefined; | ||
}; | ||
callbacks[id] = undefined; | ||
}; | ||
return setZeroTimeout; | ||
})() || | ||
// fallback | ||
function setImmediateFallback(fn) { | ||
window.setTimeout(fn, 0); | ||
}; | ||
return setZeroTimeout; | ||
})() || | ||
// fallback | ||
function setImmediateFallback(fn) { | ||
window.setTimeout(fn, 0); | ||
} | ||
); | ||
})(); | ||
@@ -243,174 +258,201 @@ } | ||
window.clearImmediate = (function setupClearImmediate() { | ||
return window.msClearImmediate || | ||
window.webkitClearImmediate || | ||
window.mozClearImmediate || | ||
window.oClearImmediate || | ||
// "clearZeroTimeout" is implement on the previous block || | ||
// fallback | ||
function clearImmediateFallback(timer) { | ||
window.clearTimeout(timer); | ||
}; | ||
return ( | ||
window.msClearImmediate || | ||
window.webkitClearImmediate || | ||
window.mozClearImmediate || | ||
window.oClearImmediate || | ||
// "clearZeroTimeout" is implement on the previous block || | ||
// fallback | ||
function clearImmediateFallback(timer) { | ||
window.clearTimeout(timer); | ||
} | ||
); | ||
})(); | ||
} | ||
// Check if WordCloud can run on this browser | ||
var isSupported = (function isSupported() { | ||
var canvas = document.createElement('canvas'); | ||
if (!canvas || !canvas.getContext) { | ||
return false; | ||
} | ||
// Check if WordCloud can run on this browser | ||
var isSupported = (function isSupported() { | ||
var canvas = document.createElement('canvas'); | ||
if (!canvas || !canvas.getContext) { | ||
return false; | ||
} | ||
var ctx = canvas.getContext('2d'); | ||
if (!ctx.getImageData) { | ||
return false; | ||
} | ||
if (!ctx.fillText) { | ||
return false; | ||
} | ||
var ctx = canvas.getContext('2d'); | ||
if (!ctx) { | ||
return false; | ||
} | ||
if (!ctx.getImageData) { | ||
return false; | ||
} | ||
if (!ctx.fillText) { | ||
return false; | ||
} | ||
if (!Array.prototype.some) { | ||
return false; | ||
} | ||
if (!Array.prototype.push) { | ||
return false; | ||
} | ||
if (!Array.prototype.some) { | ||
return false; | ||
} | ||
if (!Array.prototype.push) { | ||
return false; | ||
} | ||
return true; | ||
}()); | ||
return true; | ||
})(); | ||
// Find out if the browser impose minium font size by | ||
// drawing small texts on a canvas and measure it's width. | ||
var minFontSize = (function getMinFontSize() { | ||
if (!isSupported) { | ||
return; | ||
} | ||
// Find out if the browser impose minium font size by | ||
// drawing small texts on a canvas and measure it's width. | ||
var minFontSize = (function getMinFontSize() { | ||
if (!isSupported) { | ||
return; | ||
} | ||
var ctx = document.createElement('canvas').getContext('2d'); | ||
var ctx = document.createElement('canvas').getContext('2d'); | ||
// start from 20 | ||
var size = 20; | ||
// start from 20 | ||
var size = 20; | ||
// two sizes to measure | ||
var hanWidth, mWidth; | ||
// two sizes to measure | ||
var hanWidth, mWidth; | ||
while (size) { | ||
ctx.font = size.toString(10) + 'px sans-serif'; | ||
if ((ctx.measureText('\uFF37').width === hanWidth) && | ||
(ctx.measureText('m').width) === mWidth) { | ||
return (size + 1); | ||
} | ||
while (size) { | ||
ctx.font = size.toString(10) + 'px sans-serif'; | ||
if ( | ||
ctx.measureText('\uFF37').width === hanWidth && | ||
ctx.measureText('m').width === mWidth | ||
) { | ||
return size + 1; | ||
} | ||
hanWidth = ctx.measureText('\uFF37').width; | ||
mWidth = ctx.measureText('m').width; | ||
hanWidth = ctx.measureText('\uFF37').width; | ||
mWidth = ctx.measureText('m').width; | ||
size--; | ||
} | ||
size--; | ||
} | ||
return 0; | ||
})(); | ||
return 0; | ||
})(); | ||
// Based on http://jsfromhell.com/array/shuffle | ||
var shuffleArray = function shuffleArray(arr) { | ||
for (var j, x, i = arr.length; i; | ||
j = Math.floor(Math.random() * i), | ||
x = arr[--i], arr[i] = arr[j], | ||
arr[j] = x) {} | ||
return arr; | ||
}; | ||
var getItemExtraData = function (item) { | ||
if (Array.isArray(item)) { | ||
var itemCopy = item.slice(); | ||
// remove data we already have (word and weight) | ||
itemCopy.splice(0, 2); | ||
return itemCopy; | ||
} else { | ||
return []; | ||
} | ||
}; | ||
var WordCloud = function WordCloud(elements, options) { | ||
if (!isSupported) { | ||
return; | ||
} | ||
// Based on http://jsfromhell.com/array/shuffle | ||
var shuffleArray = function shuffleArray(arr) { | ||
for (var j, x, i = arr.length; i; ) { | ||
j = Math.floor(Math.random() * i); | ||
x = arr[--i]; | ||
arr[i] = arr[j]; | ||
arr[j] = x; | ||
} | ||
return arr; | ||
}; | ||
if (!Array.isArray(elements)) { | ||
elements = [elements]; | ||
} | ||
var timer = {}; | ||
var WordCloud = function WordCloud(elements, options) { | ||
if (!isSupported) { | ||
return; | ||
} | ||
elements.forEach(function(el, i) { | ||
if (typeof el === 'string') { | ||
elements[i] = document.getElementById(el); | ||
if (!elements[i]) { | ||
throw 'The element id specified is not found.'; | ||
} | ||
} else if (!el.tagName && !el.appendChild) { | ||
throw 'You must pass valid HTML elements, or ID of the element.'; | ||
var timerId = Math.floor(Math.random() * Date.now()); | ||
if (!Array.isArray(elements)) { | ||
elements = [elements]; | ||
} | ||
elements.forEach(function (el, i) { | ||
if (typeof el === 'string') { | ||
elements[i] = document.getElementById(el); | ||
if (!elements[i]) { | ||
throw new Error('The element id specified is not found.'); | ||
} | ||
}); | ||
} else if (!el.tagName && !el.appendChild) { | ||
throw new Error( | ||
'You must pass valid HTML elements, or ID of the element.' | ||
); | ||
} | ||
}); | ||
/* Default values to be overwritten by options object */ | ||
var settings = { | ||
list: [], | ||
fontFamily: '"Trebuchet MS", "Heiti TC", "微軟正黑體", ' + | ||
'"Arial Unicode MS", "Droid Fallback Sans", sans-serif', | ||
fontWeight: 'normal', | ||
color: 'random-dark', | ||
minSize: 0, // 0 to disable | ||
weightFactor: 1, | ||
clearCanvas: true, | ||
backgroundColor: '#fff', // opaque white = rgba(255, 255, 255, 1) | ||
/* Default values to be overwritten by options object */ | ||
var settings = { | ||
list: [], | ||
fontFamily: | ||
'"Trebuchet MS", "Heiti TC", "微軟正黑體", ' + | ||
'"Arial Unicode MS", "Droid Fallback Sans", sans-serif', | ||
fontWeight: 'normal', | ||
color: 'random-dark', | ||
minSize: 0, // 0 to disable | ||
weightFactor: 1, | ||
clearCanvas: true, | ||
backgroundColor: '#fff', // opaque white = rgba(255, 255, 255, 1) | ||
gridSize: 8, | ||
drawOutOfBound: false, | ||
origin: null, | ||
gridSize: 8, | ||
drawOutOfBound: false, | ||
shrinkToFit: false, | ||
origin: null, | ||
drawMask: false, | ||
maskColor: 'rgba(255,0,0,0.3)', | ||
maskGapWidth: 0.3, | ||
drawMask: false, | ||
maskColor: 'rgba(255,0,0,0.3)', | ||
maskGapWidth: 0.3, | ||
layoutAnimation: true, | ||
layoutAnimation: true, | ||
wait: 0, | ||
abortThreshold: 0, // disabled | ||
abort: function noop() {}, | ||
wait: 0, | ||
abortThreshold: 0, // disabled | ||
abort: function noop() {}, | ||
minRotation: - Math.PI / 2, | ||
maxRotation: Math.PI / 2, | ||
rotationStep: 0.1, | ||
minRotation: -Math.PI / 2, | ||
maxRotation: Math.PI / 2, | ||
rotationStep: 0.1, | ||
shuffle: true, | ||
rotateRatio: 0.1, | ||
shuffle: true, | ||
rotateRatio: 0.1, | ||
shape: 'circle', | ||
ellipticity: 0.65, | ||
shape: 'circle', | ||
ellipticity: 0.65, | ||
classes: null, | ||
classes: null, | ||
hover: null, | ||
click: null | ||
}; | ||
hover: null, | ||
click: null | ||
}; | ||
if (options) { | ||
for (var key in options) { | ||
if (key in settings) { | ||
settings[key] = options[key]; | ||
} | ||
if (options) { | ||
for (var key in options) { | ||
if (key in settings) { | ||
settings[key] = options[key]; | ||
} | ||
} | ||
} | ||
/* Convert weightFactor into a function */ | ||
if (typeof settings.weightFactor !== 'function') { | ||
var factor = settings.weightFactor; | ||
settings.weightFactor = function weightFactor(pt) { | ||
return pt * factor; //in px | ||
}; | ||
} | ||
/* Convert weightFactor into a function */ | ||
if (typeof settings.weightFactor !== 'function') { | ||
var factor = settings.weightFactor; | ||
settings.weightFactor = function weightFactor(pt) { | ||
return pt * factor; // in px | ||
}; | ||
} | ||
/* Convert shape into a function */ | ||
if (typeof settings.shape !== 'function') { | ||
switch (settings.shape) { | ||
case 'circle': | ||
/* falls through */ | ||
default: | ||
// 'circle' is the default and a shortcut in the code loop. | ||
settings.shape = 'circle'; | ||
break; | ||
/* Convert shape into a function */ | ||
if (typeof settings.shape !== 'function') { | ||
switch (settings.shape) { | ||
case 'circle': | ||
/* falls through */ | ||
default: | ||
// 'circle' is the default and a shortcut in the code loop. | ||
settings.shape = 'circle'; | ||
break; | ||
case 'cardioid': | ||
settings.shape = function shapeCardioid(theta) { | ||
return 1 - Math.sin(theta); | ||
}; | ||
break; | ||
case 'cardioid': | ||
settings.shape = function shapeCardioid(theta) { | ||
return 1 - Math.sin(theta); | ||
}; | ||
break; | ||
/* | ||
/* | ||
To work out an X-gon, one has to calculate "m", | ||
@@ -424,888 +466,1002 @@ where 1/(cos(2*PI/X)+m*sin(2*PI/X)) = 1/(cos(0)+m*sin(0)) | ||
case 'diamond': | ||
case 'square': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+ | ||
// %28t%2C+PI%2F2%29%29%2Bsin%28mod+%28t%2C+PI%2F2%29%29%29%2C+t+%3D | ||
// +0+..+2*PI | ||
settings.shape = function shapeSquare(theta) { | ||
var thetaPrime = theta % (2 * Math.PI / 4); | ||
return 1 / (Math.cos(thetaPrime) + Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'diamond': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+ | ||
// %28t%2C+PI%2F2%29%29%2Bsin%28mod+%28t%2C+PI%2F2%29%29%29%2C+t+%3D | ||
// +0+..+2*PI | ||
settings.shape = function shapeSquare(theta) { | ||
var thetaPrime = theta % ((2 * Math.PI) / 4); | ||
return 1 / (Math.cos(thetaPrime) + Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'triangle-forward': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+ | ||
// %28t%2C+2*PI%2F3%29%29%2Bsqrt%283%29sin%28mod+%28t%2C+2*PI%2F3%29 | ||
// %29%29%2C+t+%3D+0+..+2*PI | ||
settings.shape = function shapeTriangle(theta) { | ||
var thetaPrime = theta % (2 * Math.PI / 3); | ||
return 1 / (Math.cos(thetaPrime) + | ||
Math.sqrt(3) * Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'square': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+min(1%2Fabs(cos(t | ||
// )),1%2Fabs(sin(t)))),+t+%3D+0+..+2*PI | ||
settings.shape = function shapeSquare(theta) { | ||
return Math.min( | ||
1 / Math.abs(Math.cos(theta)), | ||
1 / Math.abs(Math.sin(theta)) | ||
); | ||
}; | ||
break; | ||
case 'triangle': | ||
case 'triangle-upright': | ||
settings.shape = function shapeTriangle(theta) { | ||
var thetaPrime = (theta + Math.PI * 3 / 2) % (2 * Math.PI / 3); | ||
return 1 / (Math.cos(thetaPrime) + | ||
Math.sqrt(3) * Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'triangle-forward': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+ | ||
// %28t%2C+2*PI%2F3%29%29%2Bsqrt%283%29sin%28mod+%28t%2C+2*PI%2F3%29 | ||
// %29%29%2C+t+%3D+0+..+2*PI | ||
settings.shape = function shapeTriangle(theta) { | ||
var thetaPrime = theta % ((2 * Math.PI) / 3); | ||
return ( | ||
1 / (Math.cos(thetaPrime) + Math.sqrt(3) * Math.sin(thetaPrime)) | ||
); | ||
}; | ||
break; | ||
case 'pentagon': | ||
settings.shape = function shapePentagon(theta) { | ||
var thetaPrime = (theta + 0.955) % (2 * Math.PI / 5); | ||
return 1 / (Math.cos(thetaPrime) + | ||
0.726543 * Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'triangle': | ||
case 'triangle-upright': | ||
settings.shape = function shapeTriangle(theta) { | ||
var thetaPrime = (theta + (Math.PI * 3) / 2) % ((2 * Math.PI) / 3); | ||
return ( | ||
1 / (Math.cos(thetaPrime) + Math.sqrt(3) * Math.sin(thetaPrime)) | ||
); | ||
}; | ||
break; | ||
case 'star': | ||
settings.shape = function shapeStar(theta) { | ||
var thetaPrime = (theta + 0.955) % (2 * Math.PI / 10); | ||
if ((theta + 0.955) % (2 * Math.PI / 5) - (2 * Math.PI / 10) >= 0) { | ||
return 1 / (Math.cos((2 * Math.PI / 10) - thetaPrime) + | ||
3.07768 * Math.sin((2 * Math.PI / 10) - thetaPrime)); | ||
} else { | ||
return 1 / (Math.cos(thetaPrime) + | ||
3.07768 * Math.sin(thetaPrime)); | ||
} | ||
}; | ||
break; | ||
} | ||
case 'pentagon': | ||
settings.shape = function shapePentagon(theta) { | ||
var thetaPrime = (theta + 0.955) % ((2 * Math.PI) / 5); | ||
return 1 / (Math.cos(thetaPrime) + 0.726543 * Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'star': | ||
settings.shape = function shapeStar(theta) { | ||
var thetaPrime = (theta + 0.955) % ((2 * Math.PI) / 10); | ||
if ( | ||
((theta + 0.955) % ((2 * Math.PI) / 5)) - (2 * Math.PI) / 10 >= | ||
0 | ||
) { | ||
return ( | ||
1 / | ||
(Math.cos((2 * Math.PI) / 10 - thetaPrime) + | ||
3.07768 * Math.sin((2 * Math.PI) / 10 - thetaPrime)) | ||
); | ||
} else { | ||
return 1 / (Math.cos(thetaPrime) + 3.07768 * Math.sin(thetaPrime)); | ||
} | ||
}; | ||
break; | ||
} | ||
} | ||
/* Make sure gridSize is a whole number and is not smaller than 4px */ | ||
settings.gridSize = Math.max(Math.floor(settings.gridSize), 4); | ||
/* Make sure gridSize is a whole number and is not smaller than 4px */ | ||
settings.gridSize = Math.max(Math.floor(settings.gridSize), 4); | ||
/* shorthand */ | ||
var g = settings.gridSize; | ||
var maskRectWidth = g - settings.maskGapWidth; | ||
/* shorthand */ | ||
var g = settings.gridSize; | ||
var maskRectWidth = g - settings.maskGapWidth; | ||
/* normalize rotation settings */ | ||
var rotationRange = Math.abs(settings.maxRotation - settings.minRotation); | ||
var minRotation = Math.min(settings.maxRotation, settings.minRotation); | ||
var rotationStep = settings.rotationStep; | ||
/* normalize rotation settings */ | ||
var rotationRange = Math.abs(settings.maxRotation - settings.minRotation); | ||
var minRotation = Math.min(settings.maxRotation, settings.minRotation); | ||
var rotationStep = settings.rotationStep; | ||
/* information/object available to all functions, set when start() */ | ||
var grid, // 2d array containing filling information | ||
ngx, ngy, // width and height of the grid | ||
center, // position of the center of the cloud | ||
maxRadius; | ||
/* information/object available to all functions, set when start() */ | ||
var grid, // 2d array containing filling information | ||
ngx, | ||
ngy, // width and height of the grid | ||
center, // position of the center of the cloud | ||
maxRadius; | ||
/* timestamp for measuring each putWord() action */ | ||
var escapeTime; | ||
/* timestamp for measuring each putWord() action */ | ||
var escapeTime; | ||
/* function for getting the color of the text */ | ||
var getTextColor; | ||
function random_hsl_color(min, max) { | ||
return 'hsl(' + | ||
(Math.random() * 360).toFixed() + ',' + | ||
(Math.random() * 30 + 70).toFixed() + '%,' + | ||
(Math.random() * (max - min) + min).toFixed() + '%)'; | ||
} | ||
switch (settings.color) { | ||
case 'random-dark': | ||
getTextColor = function getRandomDarkColor() { | ||
return random_hsl_color(10, 50); | ||
}; | ||
break; | ||
/* function for getting the color of the text */ | ||
var getTextColor; | ||
function randomHslColor(min, max) { | ||
return ( | ||
'hsl(' + | ||
(Math.random() * 360).toFixed() + | ||
',' + | ||
(Math.random() * 30 + 70).toFixed() + | ||
'%,' + | ||
(Math.random() * (max - min) + min).toFixed() + | ||
'%)' | ||
); | ||
} | ||
switch (settings.color) { | ||
case 'random-dark': | ||
getTextColor = function getRandomDarkColor() { | ||
return randomHslColor(10, 50); | ||
}; | ||
break; | ||
case 'random-light': | ||
getTextColor = function getRandomLightColor() { | ||
return random_hsl_color(50, 90); | ||
}; | ||
break; | ||
case 'random-light': | ||
getTextColor = function getRandomLightColor() { | ||
return randomHslColor(50, 90); | ||
}; | ||
break; | ||
default: | ||
if (typeof settings.color === 'function') { | ||
getTextColor = settings.color; | ||
} | ||
break; | ||
} | ||
default: | ||
if (typeof settings.color === 'function') { | ||
getTextColor = settings.color; | ||
} | ||
break; | ||
} | ||
/* function for getting the classes of the text */ | ||
var getTextClasses = null; | ||
if (typeof settings.classes === 'function') { | ||
getTextClasses = settings.classes; | ||
} | ||
/* function for getting the font-weight of the text */ | ||
var getTextFontWeight; | ||
if (typeof settings.fontWeight === 'function') { | ||
getTextFontWeight = settings.fontWeight; | ||
} | ||
/* Interactive */ | ||
var interactive = false; | ||
var infoGrid = []; | ||
var hovered; | ||
/* function for getting the classes of the text */ | ||
var getTextClasses = null; | ||
if (typeof settings.classes === 'function') { | ||
getTextClasses = settings.classes; | ||
} | ||
var getInfoGridFromMouseTouchEvent = | ||
function getInfoGridFromMouseTouchEvent(evt) { | ||
var canvas = evt.currentTarget; | ||
var rect = canvas.getBoundingClientRect(); | ||
var clientX; | ||
var clientY; | ||
/** Detect if touches are available */ | ||
if (evt.touches) { | ||
clientX = evt.touches[0].clientX; | ||
clientY = evt.touches[0].clientY; | ||
} else { | ||
clientX = evt.clientX; | ||
clientY = evt.clientY; | ||
} | ||
var eventX = clientX - rect.left; | ||
var eventY = clientY - rect.top; | ||
/* Interactive */ | ||
var interactive = false; | ||
var infoGrid = []; | ||
var hovered; | ||
var x = Math.floor(eventX * ((canvas.width / rect.width) || 1) / g); | ||
var y = Math.floor(eventY * ((canvas.height / rect.height) || 1) / g); | ||
var getInfoGridFromMouseTouchEvent = function getInfoGridFromMouseTouchEvent( | ||
evt | ||
) { | ||
var canvas = evt.currentTarget; | ||
var rect = canvas.getBoundingClientRect(); | ||
var clientX; | ||
var clientY; | ||
/** Detect if touches are available */ | ||
if (evt.touches) { | ||
clientX = evt.touches[0].clientX; | ||
clientY = evt.touches[0].clientY; | ||
} else { | ||
clientX = evt.clientX; | ||
clientY = evt.clientY; | ||
} | ||
var eventX = clientX - rect.left; | ||
var eventY = clientY - rect.top; | ||
return infoGrid[x][y]; | ||
}; | ||
var x = Math.floor((eventX * (canvas.width / rect.width || 1)) / g); | ||
var y = Math.floor((eventY * (canvas.height / rect.height || 1)) / g); | ||
var wordcloudhover = function wordcloudhover(evt) { | ||
var info = getInfoGridFromMouseTouchEvent(evt); | ||
return infoGrid[x][y]; | ||
}; | ||
if (hovered === info) { | ||
return; | ||
} | ||
var wordcloudhover = function wordcloudhover(evt) { | ||
var info = getInfoGridFromMouseTouchEvent(evt); | ||
hovered = info; | ||
if (!info) { | ||
settings.hover(undefined, undefined, evt); | ||
if (hovered === info) { | ||
return; | ||
} | ||
return; | ||
} | ||
hovered = info; | ||
if (!info) { | ||
settings.hover(undefined, undefined, evt); | ||
settings.hover(info.item, info.dimension, evt); | ||
return; | ||
} | ||
}; | ||
settings.hover(info.item, info.dimension, evt); | ||
}; | ||
var wordcloudclick = function wordcloudclick(evt) { | ||
var info = getInfoGridFromMouseTouchEvent(evt); | ||
if (!info) { | ||
return; | ||
} | ||
var wordcloudclick = function wordcloudclick(evt) { | ||
var info = getInfoGridFromMouseTouchEvent(evt); | ||
if (!info) { | ||
return; | ||
} | ||
settings.click(info.item, info.dimension, evt); | ||
evt.preventDefault(); | ||
}; | ||
settings.click(info.item, info.dimension, evt); | ||
evt.preventDefault(); | ||
}; | ||
/* Get points on the grid for a given radius away from the center */ | ||
var pointsAtRadius = []; | ||
var getPointsAtRadius = function getPointsAtRadius(radius) { | ||
if (pointsAtRadius[radius]) { | ||
return pointsAtRadius[radius]; | ||
} | ||
/* Get points on the grid for a given radius away from the center */ | ||
var pointsAtRadius = []; | ||
var getPointsAtRadius = function getPointsAtRadius(radius) { | ||
if (pointsAtRadius[radius]) { | ||
return pointsAtRadius[radius]; | ||
} | ||
// Look for these number of points on each radius | ||
var T = radius * 8; | ||
// Look for these number of points on each radius | ||
var T = radius * 8; | ||
// Getting all the points at this radius | ||
var t = T; | ||
var points = []; | ||
// Getting all the points at this radius | ||
var t = T; | ||
var points = []; | ||
if (radius === 0) { | ||
points.push([center[0], center[1], 0]); | ||
if (radius === 0) { | ||
points.push([center[0], center[1], 0]); | ||
} | ||
while (t--) { | ||
// distort the radius to put the cloud in shape | ||
var rx = 1; | ||
if (settings.shape !== 'circle') { | ||
rx = settings.shape((t / T) * 2 * Math.PI); // 0 to 1 | ||
} | ||
while (t--) { | ||
// distort the radius to put the cloud in shape | ||
var rx = 1; | ||
if (settings.shape !== 'circle') { | ||
rx = settings.shape(t / T * 2 * Math.PI); // 0 to 1 | ||
} | ||
// Push [x, y, t]; t is used solely for getTextColor() | ||
points.push([ | ||
center[0] + radius * rx * Math.cos((-t / T) * 2 * Math.PI), | ||
center[1] + | ||
radius * rx * Math.sin((-t / T) * 2 * Math.PI) * settings.ellipticity, | ||
(t / T) * 2 * Math.PI | ||
]); | ||
} | ||
// Push [x, y, t]; t is used solely for getTextColor() | ||
points.push([ | ||
center[0] + radius * rx * Math.cos(-t / T * 2 * Math.PI), | ||
center[1] + radius * rx * Math.sin(-t / T * 2 * Math.PI) * | ||
settings.ellipticity, | ||
t / T * 2 * Math.PI]); | ||
} | ||
pointsAtRadius[radius] = points; | ||
return points; | ||
}; | ||
pointsAtRadius[radius] = points; | ||
return points; | ||
}; | ||
/* Return true if we had spent too much time */ | ||
var exceedTime = function exceedTime() { | ||
return ( | ||
settings.abortThreshold > 0 && | ||
new Date().getTime() - escapeTime > settings.abortThreshold | ||
); | ||
}; | ||
/* Return true if we had spent too much time */ | ||
var exceedTime = function exceedTime() { | ||
return ((settings.abortThreshold > 0) && | ||
((new Date()).getTime() - escapeTime > settings.abortThreshold)); | ||
}; | ||
/* Get the deg of rotation according to settings, and luck. */ | ||
var getRotateDeg = function getRotateDeg() { | ||
if (settings.rotateRatio === 0) { | ||
return 0; | ||
} | ||
/* Get the deg of rotation according to settings, and luck. */ | ||
var getRotateDeg = function getRotateDeg() { | ||
if (settings.rotateRatio === 0) { | ||
return 0; | ||
} | ||
if (Math.random() > settings.rotateRatio) { | ||
return 0; | ||
} | ||
if (Math.random() > settings.rotateRatio) { | ||
return 0; | ||
} | ||
if (rotationRange === 0) { | ||
return minRotation; | ||
} | ||
if (rotationRange === 0) { | ||
return minRotation; | ||
} | ||
return minRotation + Math.round(Math.random() * rotationRange / rotationStep) * rotationStep; | ||
}; | ||
return minRotation + Math.round(Math.random() * rotationRange / rotationStep) * rotationStep; | ||
}; | ||
var getTextInfo = function getTextInfo( | ||
word, | ||
weight, | ||
rotateDeg, | ||
extraDataArray | ||
) { | ||
// calculate the acutal font size | ||
// fontSize === 0 means weightFactor function wants the text skipped, | ||
// and size < minSize means we cannot draw the text. | ||
var debug = false; | ||
var fontSize = settings.weightFactor(weight); | ||
if (fontSize <= settings.minSize) { | ||
return false; | ||
} | ||
var getTextInfo = function getTextInfo(word, weight, rotateDeg) { | ||
// calculate the acutal font size | ||
// fontSize === 0 means weightFactor function wants the text skipped, | ||
// and size < minSize means we cannot draw the text. | ||
var debug = false; | ||
var fontSize = settings.weightFactor(weight); | ||
if (fontSize <= settings.minSize) { | ||
return false; | ||
} | ||
// Scale factor here is to make sure fillText is not limited by | ||
// the minium font size set by browser. | ||
// It will always be 1 or 2n. | ||
var mu = 1; | ||
if (fontSize < minFontSize) { | ||
mu = (function calculateScaleFactor() { | ||
var mu = 2; | ||
while (mu * fontSize < minFontSize) { | ||
mu += 2; | ||
} | ||
return mu; | ||
})(); | ||
} | ||
// Scale factor here is to make sure fillText is not limited by | ||
// the minium font size set by browser. | ||
// It will always be 1 or 2n. | ||
var mu = 1; | ||
if (fontSize < minFontSize) { | ||
mu = (function calculateScaleFactor() { | ||
var mu = 2; | ||
while (mu * fontSize < minFontSize) { | ||
mu += 2; | ||
} | ||
return mu; | ||
})(); | ||
} | ||
// Get fontWeight that will be used to set fctx.font | ||
var fontWeight; | ||
if (getTextFontWeight) { | ||
fontWeight = getTextFontWeight(word, weight, fontSize, extraDataArray); | ||
} else { | ||
fontWeight = settings.fontWeight; | ||
} | ||
var fcanvas = document.createElement('canvas'); | ||
var fctx = fcanvas.getContext('2d', { willReadFrequently: true }); | ||
var fcanvas = document.createElement('canvas'); | ||
var fctx = fcanvas.getContext('2d', { willReadFrequently: true }); | ||
fctx.font = settings.fontWeight + ' ' + | ||
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily; | ||
fctx.font = | ||
fontWeight + | ||
' ' + | ||
(fontSize * mu).toString(10) + | ||
'px ' + | ||
settings.fontFamily; | ||
// Estimate the dimension of the text with measureText(). | ||
var fw = fctx.measureText(word).width / mu; | ||
var fh = Math.max(fontSize * mu, | ||
fctx.measureText('m').width, | ||
fctx.measureText('\uFF37').width) / mu; | ||
// Estimate the dimension of the text with measureText(). | ||
var fw = fctx.measureText(word).width / mu; | ||
var fh = | ||
Math.max( | ||
fontSize * mu, | ||
fctx.measureText('m').width, | ||
fctx.measureText('\uFF37').width | ||
) / mu; | ||
// Create a boundary box that is larger than our estimates, | ||
// so text don't get cut of (it sill might) | ||
var boxWidth = fw + fh * 2; | ||
var boxHeight = fh * 3; | ||
var fgw = Math.ceil(boxWidth / g); | ||
var fgh = Math.ceil(boxHeight / g); | ||
boxWidth = fgw * g; | ||
boxHeight = fgh * g; | ||
// Create a boundary box that is larger than our estimates, | ||
// so text don't get cut of (it sill might) | ||
var boxWidth = fw + fh * 2; | ||
var boxHeight = fh * 3; | ||
var fgw = Math.ceil(boxWidth / g); | ||
var fgh = Math.ceil(boxHeight / g); | ||
boxWidth = fgw * g; | ||
boxHeight = fgh * g; | ||
// Calculate the proper offsets to make the text centered at | ||
// the preferred position. | ||
// Calculate the proper offsets to make the text centered at | ||
// the preferred position. | ||
// This is simply half of the width. | ||
var fillTextOffsetX = - fw / 2; | ||
// Instead of moving the box to the exact middle of the preferred | ||
// position, for Y-offset we move 0.4 instead, so Latin alphabets look | ||
// vertical centered. | ||
var fillTextOffsetY = - fh * 0.4; | ||
// This is simply half of the width. | ||
var fillTextOffsetX = -fw / 2; | ||
// Instead of moving the box to the exact middle of the preferred | ||
// position, for Y-offset we move 0.4 instead, so Latin alphabets look | ||
// vertical centered. | ||
var fillTextOffsetY = -fh * 0.4; | ||
// Calculate the actual dimension of the canvas, considering the rotation. | ||
var cgh = Math.ceil((boxWidth * Math.abs(Math.sin(rotateDeg)) + | ||
boxHeight * Math.abs(Math.cos(rotateDeg))) / g); | ||
var cgw = Math.ceil((boxWidth * Math.abs(Math.cos(rotateDeg)) + | ||
boxHeight * Math.abs(Math.sin(rotateDeg))) / g); | ||
var width = cgw * g; | ||
var height = cgh * g; | ||
// Calculate the actual dimension of the canvas, considering the rotation. | ||
var cgh = Math.ceil( | ||
(boxWidth * Math.abs(Math.sin(rotateDeg)) + | ||
boxHeight * Math.abs(Math.cos(rotateDeg))) / | ||
g | ||
); | ||
var cgw = Math.ceil( | ||
(boxWidth * Math.abs(Math.cos(rotateDeg)) + | ||
boxHeight * Math.abs(Math.sin(rotateDeg))) / | ||
g | ||
); | ||
var width = cgw * g; | ||
var height = cgh * g; | ||
fcanvas.setAttribute('width', width); | ||
fcanvas.setAttribute('height', height); | ||
fcanvas.setAttribute('width', width); | ||
fcanvas.setAttribute('height', height); | ||
if (debug) { | ||
// Attach fcanvas to the DOM | ||
document.body.appendChild(fcanvas); | ||
// Save it's state so that we could restore and draw the grid correctly. | ||
fctx.save(); | ||
} | ||
if (debug) { | ||
// Attach fcanvas to the DOM | ||
document.body.appendChild(fcanvas); | ||
// Save it's state so that we could restore and draw the grid correctly. | ||
fctx.save(); | ||
} | ||
// Scale the canvas with |mu|. | ||
fctx.scale(1 / mu, 1 / mu); | ||
fctx.translate(width * mu / 2, height * mu / 2); | ||
fctx.rotate(- rotateDeg); | ||
// Scale the canvas with |mu|. | ||
fctx.scale(1 / mu, 1 / mu); | ||
fctx.translate((width * mu) / 2, (height * mu) / 2); | ||
fctx.rotate(-rotateDeg); | ||
// Once the width/height is set, ctx info will be reset. | ||
// Set it again here. | ||
fctx.font = settings.fontWeight + ' ' + | ||
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily; | ||
// Once the width/height is set, ctx info will be reset. | ||
// Set it again here. | ||
fctx.font = | ||
fontWeight + | ||
' ' + | ||
(fontSize * mu).toString(10) + | ||
'px ' + | ||
settings.fontFamily; | ||
// Fill the text into the fcanvas. | ||
// XXX: We cannot because textBaseline = 'top' here because | ||
// Firefox and Chrome uses different default line-height for canvas. | ||
// Please read https://bugzil.la/737852#c6. | ||
// Here, we use textBaseline = 'middle' and draw the text at exactly | ||
// 0.5 * fontSize lower. | ||
fctx.fillStyle = '#000'; | ||
fctx.textBaseline = 'middle'; | ||
fctx.fillText(word, fillTextOffsetX * mu, | ||
(fillTextOffsetY + fontSize * 0.5) * mu); | ||
// Fill the text into the fcanvas. | ||
// XXX: We cannot because textBaseline = 'top' here because | ||
// Firefox and Chrome uses different default line-height for canvas. | ||
// Please read https://bugzil.la/737852#c6. | ||
// Here, we use textBaseline = 'middle' and draw the text at exactly | ||
// 0.5 * fontSize lower. | ||
fctx.fillStyle = '#000'; | ||
fctx.textBaseline = 'middle'; | ||
fctx.fillText( | ||
word, | ||
fillTextOffsetX * mu, | ||
(fillTextOffsetY + fontSize * 0.5) * mu | ||
); | ||
// Get the pixels of the text | ||
var imageData = fctx.getImageData(0, 0, width, height).data; | ||
// Get the pixels of the text | ||
var imageData = fctx.getImageData(0, 0, width, height).data; | ||
if (exceedTime()) { | ||
return false; | ||
} | ||
if (exceedTime()) { | ||
return false; | ||
} | ||
if (debug) { | ||
// Draw the box of the original estimation | ||
fctx.strokeRect(fillTextOffsetX * mu, | ||
fillTextOffsetY, fw * mu, fh * mu); | ||
fctx.restore(); | ||
} | ||
if (debug) { | ||
// Draw the box of the original estimation | ||
fctx.strokeRect(fillTextOffsetX * mu, fillTextOffsetY, fw * mu, fh * mu); | ||
fctx.restore(); | ||
} | ||
// Read the pixels and save the information to the occupied array | ||
var occupied = []; | ||
var gx = cgw, gy, x, y; | ||
var bounds = [cgh / 2, cgw / 2, cgh / 2, cgw / 2]; | ||
while (gx--) { | ||
gy = cgh; | ||
while (gy--) { | ||
y = g; | ||
singleGridLoop: { | ||
while (y--) { | ||
x = g; | ||
while (x--) { | ||
if (imageData[((gy * g + y) * width + | ||
(gx * g + x)) * 4 + 3]) { | ||
occupied.push([gx, gy]); | ||
// Read the pixels and save the information to the occupied array | ||
var occupied = []; | ||
var gx = cgw; | ||
var gy, x, y; | ||
var bounds = [cgh / 2, cgw / 2, cgh / 2, cgw / 2]; | ||
while (gx--) { | ||
gy = cgh; | ||
while (gy--) { | ||
y = g; | ||
/* eslint no-labels: ['error', { 'allowLoop': true }] */ | ||
singleGridLoop: while (y--) { | ||
x = g; | ||
while (x--) { | ||
if (imageData[((gy * g + y) * width + (gx * g + x)) * 4 + 3]) { | ||
occupied.push([gx, gy]); | ||
if (gx < bounds[3]) { | ||
bounds[3] = gx; | ||
} | ||
if (gx > bounds[1]) { | ||
bounds[1] = gx; | ||
} | ||
if (gy < bounds[0]) { | ||
bounds[0] = gy; | ||
} | ||
if (gy > bounds[2]) { | ||
bounds[2] = gy; | ||
} | ||
if (gx < bounds[3]) { | ||
bounds[3] = gx; | ||
} | ||
if (gx > bounds[1]) { | ||
bounds[1] = gx; | ||
} | ||
if (gy < bounds[0]) { | ||
bounds[0] = gy; | ||
} | ||
if (gy > bounds[2]) { | ||
bounds[2] = gy; | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; | ||
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5); | ||
} | ||
break singleGridLoop; | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; | ||
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5); | ||
} | ||
break singleGridLoop; | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(0, 0, 255, 0.5)'; | ||
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5); | ||
} | ||
} | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(0, 0, 255, 0.5)'; | ||
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5); | ||
} | ||
} | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; | ||
fctx.fillRect(bounds[3] * g, | ||
bounds[0] * g, | ||
(bounds[1] - bounds[3] + 1) * g, | ||
(bounds[2] - bounds[0] + 1) * g); | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; | ||
fctx.fillRect( | ||
bounds[3] * g, | ||
bounds[0] * g, | ||
(bounds[1] - bounds[3] + 1) * g, | ||
(bounds[2] - bounds[0] + 1) * g | ||
); | ||
} | ||
// Return information needed to create the text on the real canvas | ||
return { | ||
mu: mu, | ||
occupied: occupied, | ||
bounds: bounds, | ||
gw: cgw, | ||
gh: cgh, | ||
fillTextOffsetX: fillTextOffsetX, | ||
fillTextOffsetY: fillTextOffsetY, | ||
fillTextWidth: fw, | ||
fillTextHeight: fh, | ||
fontSize: fontSize | ||
}; | ||
// Return information needed to create the text on the real canvas | ||
return { | ||
mu: mu, | ||
occupied: occupied, | ||
bounds: bounds, | ||
gw: cgw, | ||
gh: cgh, | ||
fillTextOffsetX: fillTextOffsetX, | ||
fillTextOffsetY: fillTextOffsetY, | ||
fillTextWidth: fw, | ||
fillTextHeight: fh, | ||
fontSize: fontSize | ||
}; | ||
}; | ||
/* Determine if there is room available in the given dimension */ | ||
var canFitText = function canFitText(gx, gy, gw, gh, occupied) { | ||
// Go through the occupied points, | ||
// return false if the space is not available. | ||
var i = occupied.length; | ||
while (i--) { | ||
var px = gx + occupied[i][0]; | ||
var py = gy + occupied[i][1]; | ||
/* Determine if there is room available in the given dimension */ | ||
var canFitText = function canFitText(gx, gy, gw, gh, occupied) { | ||
// Go through the occupied points, | ||
// return false if the space is not available. | ||
var i = occupied.length; | ||
while (i--) { | ||
var px = gx + occupied[i][0]; | ||
var py = gy + occupied[i][1]; | ||
if (px >= ngx || py >= ngy || px < 0 || py < 0) { | ||
if (!settings.drawOutOfBound) { | ||
return false; | ||
} | ||
continue; | ||
} | ||
if (!grid[px][py]) { | ||
if (px >= ngx || py >= ngy || px < 0 || py < 0) { | ||
if (!settings.drawOutOfBound) { | ||
return false; | ||
} | ||
continue; | ||
} | ||
return true; | ||
}; | ||
/* Actually draw the text on the grid */ | ||
var drawText = function drawText(gx, gy, info, word, weight, | ||
distance, theta, rotateDeg, attributes) { | ||
var fontSize = info.fontSize; | ||
var color; | ||
if (getTextColor) { | ||
color = getTextColor(word, weight, fontSize, distance, theta); | ||
} else { | ||
color = settings.color; | ||
if (!grid[px][py]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
var classes; | ||
if (getTextClasses) { | ||
classes = getTextClasses(word, weight, fontSize, distance, theta); | ||
} else { | ||
classes = settings.classes; | ||
} | ||
/* Actually draw the text on the grid */ | ||
var drawText = function drawText( | ||
gx, | ||
gy, | ||
info, | ||
word, | ||
weight, | ||
distance, | ||
theta, | ||
rotateDeg, | ||
attributes, | ||
extraDataArray | ||
) { | ||
var fontSize = info.fontSize; | ||
var color; | ||
if (getTextColor) { | ||
color = getTextColor( | ||
word, | ||
weight, | ||
fontSize, | ||
distance, | ||
theta, | ||
extraDataArray | ||
); | ||
} else { | ||
color = settings.color; | ||
} | ||
var dimension; | ||
var bounds = info.bounds; | ||
dimension = { | ||
x: (gx + bounds[3]) * g, | ||
y: (gy + bounds[0]) * g, | ||
w: (bounds[1] - bounds[3] + 1) * g, | ||
h: (bounds[2] - bounds[0] + 1) * g | ||
}; | ||
// get fontWeight that will be used to set ctx.font and font style rule | ||
var fontWeight; | ||
if (getTextFontWeight) { | ||
fontWeight = getTextFontWeight(word, weight, fontSize, extraDataArray); | ||
} else { | ||
fontWeight = settings.fontWeight; | ||
} | ||
elements.forEach(function(el) { | ||
if (el.getContext) { | ||
var ctx = el.getContext('2d'); | ||
var mu = info.mu; | ||
var classes; | ||
if (getTextClasses) { | ||
classes = getTextClasses(word, weight, fontSize, extraDataArray); | ||
} else { | ||
classes = settings.classes; | ||
} | ||
// Save the current state before messing it | ||
ctx.save(); | ||
ctx.scale(1 / mu, 1 / mu); | ||
elements.forEach(function (el) { | ||
if (el.getContext) { | ||
var ctx = el.getContext('2d'); | ||
var mu = info.mu; | ||
ctx.font = settings.fontWeight + ' ' + | ||
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily; | ||
ctx.fillStyle = color; | ||
// Save the current state before messing it | ||
ctx.save(); | ||
ctx.scale(1 / mu, 1 / mu); | ||
// Translate the canvas position to the origin coordinate of where | ||
// the text should be put. | ||
ctx.translate((gx + info.gw / 2) * g * mu, | ||
(gy + info.gh / 2) * g * mu); | ||
ctx.font = | ||
fontWeight + | ||
' ' + | ||
(fontSize * mu).toString(10) + | ||
'px ' + | ||
settings.fontFamily; | ||
ctx.fillStyle = color; | ||
if (rotateDeg !== 0) { | ||
ctx.rotate(- rotateDeg); | ||
} | ||
// Translate the canvas position to the origin coordinate of where | ||
// the text should be put. | ||
ctx.translate((gx + info.gw / 2) * g * mu, (gy + info.gh / 2) * g * mu); | ||
// Finally, fill the text. | ||
if (rotateDeg !== 0) { | ||
ctx.rotate(-rotateDeg); | ||
} | ||
// XXX: We cannot because textBaseline = 'top' here because | ||
// Firefox and Chrome uses different default line-height for canvas. | ||
// Please read https://bugzil.la/737852#c6. | ||
// Here, we use textBaseline = 'middle' and draw the text at exactly | ||
// 0.5 * fontSize lower. | ||
ctx.textBaseline = 'middle'; | ||
ctx.fillText(word, info.fillTextOffsetX * mu, | ||
(info.fillTextOffsetY + fontSize * 0.5) * mu); | ||
// Finally, fill the text. | ||
// The below box is always matches how <span>s are positioned | ||
/* ctx.strokeRect(info.fillTextOffsetX, info.fillTextOffsetY, | ||
// XXX: We cannot because textBaseline = 'top' here because | ||
// Firefox and Chrome uses different default line-height for canvas. | ||
// Please read https://bugzil.la/737852#c6. | ||
// Here, we use textBaseline = 'middle' and draw the text at exactly | ||
// 0.5 * fontSize lower. | ||
ctx.textBaseline = 'middle'; | ||
ctx.fillText( | ||
word, | ||
info.fillTextOffsetX * mu, | ||
(info.fillTextOffsetY + fontSize * 0.5) * mu | ||
); | ||
// The below box is always matches how <span>s are positioned | ||
/* ctx.strokeRect(info.fillTextOffsetX, info.fillTextOffsetY, | ||
info.fillTextWidth, info.fillTextHeight); */ | ||
// Restore the state. | ||
ctx.restore(); | ||
} else { | ||
// drawText on DIV element | ||
var span = document.createElement('span'); | ||
var transformRule = ''; | ||
transformRule = 'rotate(' + (- rotateDeg / Math.PI * 180) + 'deg) '; | ||
if (info.mu !== 1) { | ||
transformRule += | ||
'translateX(-' + (info.fillTextWidth / 4) + 'px) ' + | ||
'scale(' + (1 / info.mu) + ')'; | ||
// Restore the state. | ||
ctx.restore(); | ||
} else { | ||
// drawText on DIV element | ||
var span = document.createElement('span'); | ||
var transformRule = ''; | ||
transformRule = 'rotate(' + (-rotateDeg / Math.PI) * 180 + 'deg) '; | ||
if (info.mu !== 1) { | ||
transformRule += | ||
'translateX(-' + | ||
info.fillTextWidth / 4 + | ||
'px) ' + | ||
'scale(' + | ||
1 / info.mu + | ||
')'; | ||
} | ||
var styleRules = { | ||
position: 'absolute', | ||
display: 'block', | ||
font: | ||
fontWeight + ' ' + fontSize * info.mu + 'px ' + settings.fontFamily, | ||
left: (gx + info.gw / 2) * g + info.fillTextOffsetX + 'px', | ||
top: (gy + info.gh / 2) * g + info.fillTextOffsetY + 'px', | ||
width: info.fillTextWidth + 'px', | ||
height: info.fillTextHeight + 'px', | ||
lineHeight: fontSize + 'px', | ||
whiteSpace: 'nowrap', | ||
transform: transformRule, | ||
webkitTransform: transformRule, | ||
msTransform: transformRule, | ||
transformOrigin: '50% 40%', | ||
webkitTransformOrigin: '50% 40%', | ||
msTransformOrigin: '50% 40%' | ||
}; | ||
if (color) { | ||
styleRules.color = color; | ||
} | ||
span.textContent = word; | ||
for (var cssProp in styleRules) { | ||
span.style[cssProp] = styleRules[cssProp]; | ||
} | ||
if (attributes) { | ||
for (var attribute in attributes) { | ||
span.setAttribute(attribute, attributes[attribute]); | ||
} | ||
var styleRules = { | ||
'position': 'absolute', | ||
'display': 'block', | ||
'font': settings.fontWeight + ' ' + | ||
(fontSize * info.mu) + 'px ' + settings.fontFamily, | ||
'left': ((gx + info.gw / 2) * g + info.fillTextOffsetX) + 'px', | ||
'top': ((gy + info.gh / 2) * g + info.fillTextOffsetY) + 'px', | ||
'width': info.fillTextWidth + 'px', | ||
'height': info.fillTextHeight + 'px', | ||
'lineHeight': fontSize + 'px', | ||
'whiteSpace': 'nowrap', | ||
'transform': transformRule, | ||
'webkitTransform': transformRule, | ||
'msTransform': transformRule, | ||
'transformOrigin': '50% 40%', | ||
'webkitTransformOrigin': '50% 40%', | ||
'msTransformOrigin': '50% 40%' | ||
}; | ||
if (color) { | ||
styleRules.color = color; | ||
} | ||
span.textContent = word; | ||
for (var cssProp in styleRules) { | ||
span.style[cssProp] = styleRules[cssProp]; | ||
} | ||
if (attributes) { | ||
for (var attribute in attributes) { | ||
span.setAttribute(attribute, attributes[attribute]); | ||
} | ||
} | ||
if (classes) { | ||
span.className += classes; | ||
} | ||
el.appendChild(span); | ||
} | ||
}); | ||
}; | ||
/* Help function to updateGrid */ | ||
var fillGridAt = function fillGridAt(x, y, drawMask, dimension, item) { | ||
if (x >= ngx || y >= ngy || x < 0 || y < 0) { | ||
return; | ||
if (classes) { | ||
span.className += classes; | ||
} | ||
el.appendChild(span); | ||
} | ||
}); | ||
}; | ||
grid[x][y] = false; | ||
/* Help function to updateGrid */ | ||
var fillGridAt = function fillGridAt(x, y, drawMask, dimension, item) { | ||
if (x >= ngx || y >= ngy || x < 0 || y < 0) { | ||
return; | ||
} | ||
if (drawMask) { | ||
var ctx = elements[0].getContext('2d'); | ||
ctx.fillRect(x * g, y * g, maskRectWidth, maskRectWidth); | ||
} | ||
grid[x][y] = false; | ||
if (interactive) { | ||
infoGrid[x][y] = { item: item, dimension: dimension }; | ||
} | ||
}; | ||
if (drawMask) { | ||
var ctx = elements[0].getContext('2d'); | ||
ctx.fillRect(x * g, y * g, maskRectWidth, maskRectWidth); | ||
} | ||
/* Update the filling information of the given space with occupied points. | ||
if (interactive) { | ||
infoGrid[x][y] = { item: item, dimension: dimension }; | ||
} | ||
}; | ||
/* Update the filling information of the given space with occupied points. | ||
Draw the mask on the canvas if necessary. */ | ||
var updateGrid = function updateGrid(gx, gy, gw, gh, info, item) { | ||
var occupied = info.occupied; | ||
var drawMask = settings.drawMask; | ||
var ctx; | ||
if (drawMask) { | ||
ctx = elements[0].getContext('2d'); | ||
ctx.save(); | ||
ctx.fillStyle = settings.maskColor; | ||
} | ||
var updateGrid = function updateGrid(gx, gy, gw, gh, info, item) { | ||
var occupied = info.occupied; | ||
var drawMask = settings.drawMask; | ||
var ctx; | ||
if (drawMask) { | ||
ctx = elements[0].getContext('2d'); | ||
ctx.save(); | ||
ctx.fillStyle = settings.maskColor; | ||
} | ||
var dimension; | ||
if (interactive) { | ||
var bounds = info.bounds; | ||
dimension = { | ||
x: (gx + bounds[3]) * g, | ||
y: (gy + bounds[0]) * g, | ||
w: (bounds[1] - bounds[3] + 1) * g, | ||
h: (bounds[2] - bounds[0] + 1) * g | ||
}; | ||
} | ||
var dimension; | ||
if (interactive) { | ||
var bounds = info.bounds; | ||
dimension = { | ||
x: (gx + bounds[3]) * g, | ||
y: (gy + bounds[0]) * g, | ||
w: (bounds[1] - bounds[3] + 1) * g, | ||
h: (bounds[2] - bounds[0] + 1) * g | ||
}; | ||
} | ||
var i = occupied.length; | ||
while (i--) { | ||
var px = gx + occupied[i][0]; | ||
var py = gy + occupied[i][1]; | ||
var i = occupied.length; | ||
while (i--) { | ||
var px = gx + occupied[i][0]; | ||
var py = gy + occupied[i][1]; | ||
if (px >= ngx || py >= ngy || px < 0 || py < 0) { | ||
continue; | ||
} | ||
fillGridAt(px, py, drawMask, dimension, item); | ||
if (px >= ngx || py >= ngy || px < 0 || py < 0) { | ||
continue; | ||
} | ||
if (drawMask) { | ||
ctx.restore(); | ||
} | ||
}; | ||
fillGridAt(px, py, drawMask, dimension, item); | ||
} | ||
/* putWord() processes each item on the list, | ||
if (drawMask) { | ||
ctx.restore(); | ||
} | ||
}; | ||
/* putWord() processes each item on the list, | ||
calculate it's size and determine it's position, and actually | ||
put it on the canvas. */ | ||
var putWord = function putWord(item) { | ||
var word, weight, attributes; | ||
if (Array.isArray(item)) { | ||
word = item[0]; | ||
weight = item[1]; | ||
} else { | ||
word = item.word; | ||
weight = item.weight; | ||
attributes = item.attributes; | ||
} | ||
var rotateDeg = getRotateDeg(); | ||
var putWord = function putWord(item) { | ||
var word, weight, attributes; | ||
if (Array.isArray(item)) { | ||
word = item[0]; | ||
weight = item[1]; | ||
} else { | ||
word = item.word; | ||
weight = item.weight; | ||
attributes = item.attributes; | ||
} | ||
var rotateDeg = getRotateDeg(); | ||
// get info needed to put the text onto the canvas | ||
var info = getTextInfo(word, weight, rotateDeg); | ||
var extraDataArray = getItemExtraData(item); | ||
// not getting the info means we shouldn't be drawing this one. | ||
if (!info) { | ||
return false; | ||
} | ||
// get info needed to put the text onto the canvas | ||
var info = getTextInfo(word, weight, rotateDeg, extraDataArray); | ||
if (exceedTime()) { | ||
// not getting the info means we shouldn't be drawing this one. | ||
if (!info) { | ||
return false; | ||
} | ||
if (exceedTime()) { | ||
return false; | ||
} | ||
// If drawOutOfBound is set to false, | ||
// skip the loop if we have already know the bounding box of | ||
// word is larger than the canvas. | ||
if (!settings.drawOutOfBound && !settings.shrinkToFit) { | ||
var bounds = info.bounds; | ||
if (bounds[1] - bounds[3] + 1 > ngx || bounds[2] - bounds[0] + 1 > ngy) { | ||
return false; | ||
} | ||
} | ||
// If drawOutOfBound is set to false, | ||
// skip the loop if we have already know the bounding box of | ||
// word is larger than the canvas. | ||
if (!settings.drawOutOfBound) { | ||
var bounds = info.bounds; | ||
if ((bounds[1] - bounds[3] + 1) > ngx || | ||
(bounds[2] - bounds[0] + 1) > ngy) { | ||
return false; | ||
} | ||
} | ||
// Determine the position to put the text by | ||
// start looking for the nearest points | ||
var r = maxRadius + 1; | ||
// Determine the position to put the text by | ||
// start looking for the nearest points | ||
var r = maxRadius + 1; | ||
var tryToPutWordAtPoint = function (gxy) { | ||
var gx = Math.floor(gxy[0] - info.gw / 2); | ||
var gy = Math.floor(gxy[1] - info.gh / 2); | ||
var gw = info.gw; | ||
var gh = info.gh; | ||
var tryToPutWordAtPoint = function(gxy) { | ||
var gx = Math.floor(gxy[0] - info.gw / 2); | ||
var gy = Math.floor(gxy[1] - info.gh / 2); | ||
var gw = info.gw; | ||
var gh = info.gh; | ||
// If we cannot fit the text at this position, return false | ||
// and go to the next position. | ||
if (!canFitText(gx, gy, gw, gh, info.occupied)) { | ||
return false; | ||
} | ||
// If we cannot fit the text at this position, return false | ||
// and go to the next position. | ||
if (!canFitText(gx, gy, gw, gh, info.occupied)) { | ||
return false; | ||
} | ||
// Actually put the text on the canvas | ||
drawText( | ||
gx, | ||
gy, | ||
info, | ||
word, | ||
weight, | ||
maxRadius - r, | ||
gxy[2], | ||
rotateDeg, | ||
attributes, | ||
extraDataArray | ||
); | ||
// Actually put the text on the canvas | ||
drawText(gx, gy, info, word, weight, | ||
(maxRadius - r), gxy[2], rotateDeg, attributes); | ||
// Mark the spaces on the grid as filled | ||
updateGrid(gx, gy, gw, gh, info, item); | ||
// Mark the spaces on the grid as filled | ||
updateGrid(gx, gy, gw, gh, info, item); | ||
return { | ||
gx: gx, | ||
gy: gy, | ||
rot: rotateDeg, | ||
info: info | ||
}; | ||
return { | ||
gx: gx, | ||
gy: gy, | ||
rot: rotateDeg, | ||
info: info | ||
}; | ||
}; | ||
while (r--) { | ||
var points = getPointsAtRadius(maxRadius - r); | ||
while (r--) { | ||
var points = getPointsAtRadius(maxRadius - r); | ||
if (settings.shuffle) { | ||
points = [].concat(points); | ||
shuffleArray(points); | ||
if (settings.shuffle) { | ||
points = [].concat(points); | ||
shuffleArray(points); | ||
} | ||
// Try to fit the words by looking at each point. | ||
// array.some() will stop and return true | ||
// when putWordAtPoint() returns true. | ||
for (var i = 0; i < points.length; i++) { | ||
var res = tryToPutWordAtPoint(points[i]); | ||
if (res) { | ||
return res; | ||
} | ||
} | ||
// Try to fit the words by looking at each point. | ||
// array.some() will stop and return true | ||
// when putWordAtPoint() returns true. | ||
for (var i = 0; i < points.length; i++) { | ||
var res = tryToPutWordAtPoint(points[i]); | ||
if (res) { | ||
return res; | ||
} | ||
// var drawn = points.some(tryToPutWordAtPoint); | ||
// if (drawn) { | ||
// // leave putWord() and return true | ||
// return true; | ||
// } | ||
if (settings.shrinkToFit) { | ||
if (Array.isArray(item)) { | ||
item[1] = (item[1] * 3) / 4; | ||
} else { | ||
item.weight = (item.weight * 3) / 4; | ||
} | ||
// var drawn = points.some(tryToPutWordAtPoint); | ||
// if (drawn) { | ||
// // leave putWord() and return true | ||
// return true; | ||
// } | ||
return putWord(item); | ||
} | ||
// we tried all distances but text won't fit, return null | ||
return null; | ||
}; | ||
} | ||
// we tried all distances but text won't fit, return null | ||
return null; | ||
}; | ||
/* Send DOM event to all elements. Will stop sending event and return | ||
/* Send DOM event to all elements. Will stop sending event and return | ||
if the previous one is canceled (for cancelable events). */ | ||
var sendEvent = function sendEvent(type, cancelable, detail) { | ||
if (cancelable) { | ||
return !elements.some(function(el) { | ||
var evt = document.createEvent('CustomEvent'); | ||
evt.initCustomEvent(type, true, cancelable, detail || {}); | ||
return !el.dispatchEvent(evt); | ||
}, this); | ||
} else { | ||
elements.forEach(function(el) { | ||
var evt = document.createEvent('CustomEvent'); | ||
evt.initCustomEvent(type, true, cancelable, detail || {}); | ||
el.dispatchEvent(evt); | ||
}, this); | ||
} | ||
}; | ||
var sendEvent = function sendEvent(type, cancelable, details) { | ||
if (cancelable) { | ||
return !elements.some(function (el) { | ||
var event = new CustomEvent(type, { | ||
detail: details || {} | ||
}); | ||
return !el.dispatchEvent(event); | ||
}, this); | ||
} else { | ||
elements.forEach(function (el) { | ||
var event = new CustomEvent(type, { | ||
detail: details || {} | ||
}); | ||
el.dispatchEvent(event); | ||
}, this); | ||
} | ||
}; | ||
/* Start drawing on a canvas */ | ||
var start = function start() { | ||
// For dimensions, clearCanvas etc., | ||
// we only care about the first element. | ||
var canvas = elements[0]; | ||
/* Start drawing on a canvas */ | ||
var start = function start() { | ||
// For dimensions, clearCanvas etc., | ||
// we only care about the first element. | ||
var canvas = elements[0]; | ||
if (canvas.getContext) { | ||
ngx = Math.ceil(canvas.width / g); | ||
ngy = Math.ceil(canvas.height / g); | ||
} else { | ||
var rect = canvas.getBoundingClientRect(); | ||
ngx = Math.ceil(rect.width / g); | ||
ngy = Math.ceil(rect.height / g); | ||
} | ||
if (canvas.getContext) { | ||
ngx = Math.ceil(canvas.width / g); | ||
ngy = Math.ceil(canvas.height / g); | ||
} else { | ||
var rect = canvas.getBoundingClientRect(); | ||
ngx = Math.ceil(rect.width / g); | ||
ngy = Math.ceil(rect.height / g); | ||
} | ||
// Sending a wordcloudstart event which cause the previous loop to stop. | ||
// Do nothing if the event is canceled. | ||
if (!sendEvent('wordcloudstart', true)) { | ||
return; | ||
} | ||
// Sending a wordcloudstart event which cause the previous loop to stop. | ||
// Do nothing if the event is canceled. | ||
if (!sendEvent('wordcloudstart', true)) { | ||
return; | ||
} | ||
// Determine the center of the word cloud | ||
center = (settings.origin) ? | ||
[settings.origin[0]/g, settings.origin[1]/g] : | ||
[ngx / 2, ngy / 2]; | ||
// Determine the center of the word cloud | ||
center = settings.origin | ||
? [settings.origin[0] / g, settings.origin[1] / g] | ||
: [ngx / 2, ngy / 2]; | ||
// Maxium radius to look for space | ||
maxRadius = Math.floor(Math.sqrt(ngx * ngx + ngy * ngy)); | ||
// Maxium radius to look for space | ||
maxRadius = Math.floor(Math.sqrt(ngx * ngx + ngy * ngy)); | ||
/* Clear the canvas only if the clearCanvas is set, | ||
/* Clear the canvas only if the clearCanvas is set, | ||
if not, update the grid to the current canvas state */ | ||
grid = []; | ||
grid = []; | ||
var gx, gy, i; | ||
if (!canvas.getContext || settings.clearCanvas) { | ||
elements.forEach(function(el) { | ||
if (el.getContext) { | ||
var ctx = el.getContext('2d'); | ||
ctx.fillStyle = settings.backgroundColor; | ||
ctx.clearRect(0, 0, ngx * (g + 1), ngy * (g + 1)); | ||
ctx.fillRect(0, 0, ngx * (g + 1), ngy * (g + 1)); | ||
} else { | ||
el.textContent = ''; | ||
el.style.backgroundColor = settings.backgroundColor; | ||
el.style.position = 'relative'; | ||
} | ||
}); | ||
var gx, gy, i; | ||
if (!canvas.getContext || settings.clearCanvas) { | ||
elements.forEach(function (el) { | ||
if (el.getContext) { | ||
var ctx = el.getContext('2d'); | ||
ctx.fillStyle = settings.backgroundColor; | ||
ctx.clearRect(0, 0, ngx * (g + 1), ngy * (g + 1)); | ||
ctx.fillRect(0, 0, ngx * (g + 1), ngy * (g + 1)); | ||
} else { | ||
el.textContent = ''; | ||
el.style.backgroundColor = settings.backgroundColor; | ||
el.style.position = 'relative'; | ||
} | ||
}); | ||
/* fill the grid with empty state */ | ||
gx = ngx; | ||
while (gx--) { | ||
grid[gx] = []; | ||
gy = ngy; | ||
while (gy--) { | ||
grid[gx][gy] = true; | ||
} | ||
/* fill the grid with empty state */ | ||
gx = ngx; | ||
while (gx--) { | ||
grid[gx] = []; | ||
gy = ngy; | ||
while (gy--) { | ||
grid[gx][gy] = true; | ||
} | ||
} else { | ||
/* Determine bgPixel by creating | ||
} | ||
} else { | ||
/* Determine bgPixel by creating | ||
another canvas and fill the specified background color. */ | ||
var bctx = document.createElement('canvas').getContext('2d'); | ||
var bctx = document.createElement('canvas').getContext('2d'); | ||
bctx.fillStyle = settings.backgroundColor; | ||
bctx.fillRect(0, 0, 1, 1); | ||
var bgPixel = bctx.getImageData(0, 0, 1, 1).data; | ||
bctx.fillStyle = settings.backgroundColor; | ||
bctx.fillRect(0, 0, 1, 1); | ||
var bgPixel = bctx.getImageData(0, 0, 1, 1).data; | ||
/* Read back the pixels of the canvas we got to tell which part of the | ||
/* Read back the pixels of the canvas we got to tell which part of the | ||
canvas is empty. | ||
(no clearCanvas only works with a canvas, not divs) */ | ||
var imageData = | ||
canvas.getContext('2d').getImageData(0, 0, ngx * g, ngy * g).data; | ||
var imageData = canvas | ||
.getContext('2d') | ||
.getImageData(0, 0, ngx * g, ngy * g).data; | ||
gx = ngx; | ||
var x, y; | ||
while (gx--) { | ||
grid[gx] = []; | ||
gy = ngy; | ||
while (gy--) { | ||
y = g; | ||
singleGridLoop: while (y--) { | ||
x = g; | ||
while (x--) { | ||
i = 4; | ||
while (i--) { | ||
if (imageData[((gy * g + y) * ngx * g + | ||
(gx * g + x)) * 4 + i] !== bgPixel[i]) { | ||
grid[gx][gy] = false; | ||
break singleGridLoop; | ||
} | ||
gx = ngx; | ||
var x, y; | ||
while (gx--) { | ||
grid[gx] = []; | ||
gy = ngy; | ||
while (gy--) { | ||
y = g; | ||
/* eslint no-labels: ['error', { 'allowLoop': true }] */ | ||
singleGridLoop: while (y--) { | ||
x = g; | ||
while (x--) { | ||
i = 4; | ||
while (i--) { | ||
if ( | ||
imageData[((gy * g + y) * ngx * g + (gx * g + x)) * 4 + i] !== | ||
bgPixel[i] | ||
) { | ||
grid[gx][gy] = false; | ||
break singleGridLoop; | ||
} | ||
} | ||
} | ||
if (grid[gx][gy] !== false) { | ||
grid[gx][gy] = true; | ||
} | ||
} | ||
if (grid[gx][gy] !== false) { | ||
grid[gx][gy] = true; | ||
} | ||
} | ||
imageData = bctx = bgPixel = undefined; | ||
} | ||
// fill the infoGrid with empty state if we need it | ||
if (settings.hover || settings.click) { | ||
imageData = bctx = bgPixel = undefined; | ||
} | ||
interactive = true; | ||
// fill the infoGrid with empty state if we need it | ||
if (settings.hover || settings.click) { | ||
interactive = true; | ||
/* fill the grid with empty state */ | ||
gx = ngx + 1; | ||
while (gx--) { | ||
infoGrid[gx] = []; | ||
} | ||
/* fill the grid with empty state */ | ||
gx = ngx + 1; | ||
while (gx--) { | ||
infoGrid[gx] = []; | ||
} | ||
if (settings.hover) { | ||
canvas.addEventListener('mousemove', wordcloudhover); | ||
} | ||
if (settings.hover) { | ||
canvas.addEventListener('mousemove', wordcloudhover); | ||
} | ||
if (settings.click) { | ||
canvas.addEventListener('click', wordcloudclick); | ||
canvas.addEventListener('touchstart', wordcloudclick); | ||
canvas.addEventListener('touchend', function (e) { | ||
e.preventDefault(); | ||
}); | ||
canvas.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)'; | ||
} | ||
canvas.addEventListener('wordcloudstart', function stopInteraction() { | ||
canvas.removeEventListener('wordcloudstart', stopInteraction); | ||
canvas.removeEventListener('mousemove', wordcloudhover); | ||
canvas.removeEventListener('click', wordcloudclick); | ||
hovered = undefined; | ||
if (settings.click) { | ||
canvas.addEventListener('click', wordcloudclick); | ||
canvas.addEventListener('touchstart', wordcloudclick); | ||
canvas.addEventListener('touchend', function (e) { | ||
e.preventDefault(); | ||
}); | ||
canvas.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)'; | ||
} | ||
i = 0; | ||
var loopingFunction, stoppingFunction; | ||
var layouting = true; | ||
if (!settings.layoutAnimation) { | ||
loopingFunction = function (cb) { | ||
cb(); | ||
} | ||
stoppingFunction = function () { | ||
layouting = false; | ||
} | ||
} | ||
else if (settings.wait !== 0) { | ||
loopingFunction = window.setTimeout; | ||
stoppingFunction = window.clearTimeout; | ||
} else { | ||
loopingFunction = window.setImmediate; | ||
stoppingFunction = window.clearImmediate; | ||
} | ||
canvas.addEventListener('wordcloudstart', function stopInteraction() { | ||
canvas.removeEventListener('wordcloudstart', stopInteraction); | ||
var addEventListener = function addEventListener(type, listener) { | ||
elements.forEach(function(el) { | ||
el.addEventListener(type, listener); | ||
}, this); | ||
}; | ||
canvas.removeEventListener('mousemove', wordcloudhover); | ||
canvas.removeEventListener('click', wordcloudclick); | ||
hovered = undefined; | ||
}); | ||
} | ||
var removeEventListener = function removeEventListener(type, listener) { | ||
elements.forEach(function(el) { | ||
el.removeEventListener(type, listener); | ||
}, this); | ||
i = 0; | ||
var loopingFunction, stoppingFunction; | ||
var layouting = true; | ||
if (!settings.layoutAnimation) { | ||
loopingFunction = function (cb) { | ||
cb(); | ||
}; | ||
var anotherWordCloudStart = function anotherWordCloudStart() { | ||
removeEventListener('wordcloudstart', anotherWordCloudStart); | ||
stoppingFunction(timer); | ||
stoppingFunction = function () { | ||
layouting = false; | ||
}; | ||
} else if (settings.wait !== 0) { | ||
loopingFunction = window.setTimeout; | ||
stoppingFunction = window.clearTimeout; | ||
} else { | ||
loopingFunction = window.setImmediate; | ||
stoppingFunction = window.clearImmediate; | ||
} | ||
addEventListener('wordcloudstart', anotherWordCloudStart); | ||
var addEventListener = function addEventListener(type, listener) { | ||
elements.forEach(function (el) { | ||
el.addEventListener(type, listener); | ||
}, this); | ||
}; | ||
// At least wait the following code before call the first iteration. | ||
var timer = (settings.layoutAnimation ? loopingFunction : setTimeout)(function loop() { | ||
var removeEventListener = function removeEventListener(type, listener) { | ||
elements.forEach(function (el) { | ||
el.removeEventListener(type, listener); | ||
}, this); | ||
}; | ||
var anotherWordCloudStart = function anotherWordCloudStart() { | ||
removeEventListener('wordcloudstart', anotherWordCloudStart); | ||
stoppingFunction(timer[timerId]); | ||
}; | ||
addEventListener('wordcloudstart', anotherWordCloudStart); | ||
// At least wait the following code before call the first iteration. | ||
timer[timerId] = (settings.layoutAnimation ? loopingFunction : setTimeout)( | ||
function loop() { | ||
if (!layouting) { | ||
@@ -1315,14 +1471,16 @@ return; | ||
if (i >= settings.list.length) { | ||
stoppingFunction(timer); | ||
stoppingFunction(timer[timerId]); | ||
sendEvent('wordcloudstop', false); | ||
removeEventListener('wordcloudstart', anotherWordCloudStart); | ||
delete timer[timerId]; | ||
return; | ||
} | ||
escapeTime = (new Date()).getTime(); | ||
escapeTime = new Date().getTime(); | ||
var drawn = putWord(settings.list[i]); | ||
var canceled = !sendEvent('wordclouddrawn', true, { | ||
item: settings.list[i], drawn: drawn }); | ||
item: settings.list[i], | ||
drawn: drawn | ||
}); | ||
if (exceedTime() || canceled) { | ||
stoppingFunction(timer); | ||
stoppingFunction(timer[timerId]); | ||
settings.abort(); | ||
@@ -1335,14 +1493,17 @@ sendEvent('wordcloudabort', false); | ||
i++; | ||
timer = loopingFunction(loop, settings.wait); | ||
}, settings.wait); | ||
}; | ||
// All set, start the drawing | ||
start(); | ||
timer[timerId] = loopingFunction(loop, settings.wait); | ||
}, | ||
settings.wait | ||
); | ||
}; | ||
WordCloud.isSupported = isSupported; | ||
WordCloud.minFontSize = minFontSize; | ||
// All set, start the drawing | ||
start(); | ||
}; | ||
/* harmony default export */ const layout = (WordCloud); | ||
WordCloud.isSupported = isSupported; | ||
WordCloud.minFontSize = minFontSize; | ||
/* harmony default export */ const layout = (WordCloud); | ||
;// CONCATENATED MODULE: ./src/wordCloud.js | ||
@@ -1357,3 +1518,3 @@ | ||
if (!layout.isSupported) { | ||
throw new Error('Sorry your browser not support wordCloud'); | ||
throw new Error('Sorry your browser not support wordCloud'); | ||
} | ||
@@ -1363,179 +1524,203 @@ | ||
function updateCanvasMask(maskCanvas) { | ||
var ctx = maskCanvas.getContext('2d'); | ||
var imageData = ctx.getImageData( | ||
0, 0, maskCanvas.width, maskCanvas.height); | ||
var newImageData = ctx.createImageData(imageData); | ||
var ctx = maskCanvas.getContext('2d'); | ||
var imageData = ctx.getImageData(0, 0, maskCanvas.width, maskCanvas.height); | ||
var newImageData = ctx.createImageData(imageData); | ||
var toneSum = 0; | ||
var toneCnt = 0; | ||
for (var i = 0; i < imageData.data.length; i += 4) { | ||
var alpha = imageData.data[i + 3]; | ||
if (alpha > 128) { | ||
var tone = imageData.data[i] | ||
+ imageData.data[i + 1] | ||
+ imageData.data[i + 2]; | ||
toneSum += tone; | ||
++toneCnt; | ||
} | ||
var toneSum = 0; | ||
var toneCnt = 0; | ||
for (var i = 0; i < imageData.data.length; i += 4) { | ||
var alpha = imageData.data[i + 3]; | ||
if (alpha > 128) { | ||
var tone = | ||
imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]; | ||
toneSum += tone; | ||
++toneCnt; | ||
} | ||
var threshold = toneSum / toneCnt; | ||
} | ||
var threshold = toneSum / toneCnt; | ||
for (var i = 0; i < imageData.data.length; i += 4) { | ||
var tone = imageData.data[i] | ||
+ imageData.data[i + 1] | ||
+ imageData.data[i + 2]; | ||
var alpha = imageData.data[i + 3]; | ||
for (var i = 0; i < imageData.data.length; i += 4) { | ||
var tone = | ||
imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]; | ||
var alpha = imageData.data[i + 3]; | ||
if (alpha < 128 || tone > threshold) { | ||
// Area not to draw | ||
newImageData.data[i] = 0; | ||
newImageData.data[i + 1] = 0; | ||
newImageData.data[i + 2] = 0; | ||
newImageData.data[i + 3] = 0; | ||
} | ||
else { | ||
// Area to draw | ||
// The color must be same with backgroundColor | ||
newImageData.data[i] = 255; | ||
newImageData.data[i + 1] = 255; | ||
newImageData.data[i + 2] = 255; | ||
newImageData.data[i + 3] = 255; | ||
} | ||
if (alpha < 128 || tone > threshold) { | ||
// Area not to draw | ||
newImageData.data[i] = 0; | ||
newImageData.data[i + 1] = 0; | ||
newImageData.data[i + 2] = 0; | ||
newImageData.data[i + 3] = 0; | ||
} else { | ||
// Area to draw | ||
// The color must be same with backgroundColor | ||
newImageData.data[i] = 255; | ||
newImageData.data[i + 1] = 255; | ||
newImageData.data[i + 2] = 255; | ||
newImageData.data[i + 3] = 255; | ||
} | ||
} | ||
ctx.putImageData(newImageData, 0, 0); | ||
ctx.putImageData(newImageData, 0, 0); | ||
} | ||
external_echarts_.registerLayout(function (ecModel, api) { | ||
ecModel.eachSeriesByType('wordCloud', function (seriesModel) { | ||
var gridRect = external_echarts_.helper.getLayoutRect( | ||
seriesModel.getBoxLayoutParams(), { | ||
width: api.getWidth(), | ||
height: api.getHeight() | ||
} | ||
); | ||
var data = seriesModel.getData(); | ||
ecModel.eachSeriesByType('wordCloud', function (seriesModel) { | ||
var gridRect = external_echarts_.helper.getLayoutRect( | ||
seriesModel.getBoxLayoutParams(), | ||
{ | ||
width: api.getWidth(), | ||
height: api.getHeight() | ||
} | ||
); | ||
var canvas = document.createElement('canvas'); | ||
canvas.width = gridRect.width; | ||
canvas.height = gridRect.height; | ||
var keepAspect = seriesModel.get('keepAspect'); | ||
var maskImage = seriesModel.get('maskImage'); | ||
var ratio = maskImage ? maskImage.width / maskImage.height : 1; | ||
keepAspect && adjustRectAspect(gridRect, ratio); | ||
var ctx = canvas.getContext('2d'); | ||
var maskImage = seriesModel.get('maskImage'); | ||
if (maskImage) { | ||
try { | ||
ctx.drawImage(maskImage, 0, 0, canvas.width, canvas.height); | ||
updateCanvasMask(canvas); | ||
} | ||
catch (e) { | ||
console.error('Invalid mask image'); | ||
console.error(e.toString()); | ||
} | ||
} | ||
var data = seriesModel.getData(); | ||
var sizeRange = seriesModel.get('sizeRange'); | ||
var rotationRange = seriesModel.get('rotationRange'); | ||
var valueExtent = data.getDataExtent('value'); | ||
var canvas = document.createElement('canvas'); | ||
canvas.width = gridRect.width; | ||
canvas.height = gridRect.height; | ||
var DEGREE_TO_RAD = Math.PI / 180; | ||
var gridSize = seriesModel.get('gridSize'); | ||
layout(canvas, { | ||
list: data.mapArray('value', function (value, idx) { | ||
var itemModel = data.getItemModel(idx); | ||
return [ | ||
data.getName(idx), | ||
itemModel.get('textStyle.fontSize', true) | ||
|| external_echarts_.number.linearMap(value, valueExtent, sizeRange), | ||
idx | ||
]; | ||
}).sort(function (a, b) { | ||
// Sort from large to small in case there is no more room for more words | ||
return b[1] - a[1]; | ||
}), | ||
fontFamily: seriesModel.get('textStyle.fontFamily') | ||
|| seriesModel.get('emphasis.textStyle.fontFamily') | ||
|| ecModel.get('textStyle.fontFamily'), | ||
fontWeight: seriesModel.get('textStyle.fontWeight') | ||
|| seriesModel.get('emphasis.textStyle.fontWeight') | ||
|| ecModel.get('textStyle.fontWeight'), | ||
var ctx = canvas.getContext('2d'); | ||
if (maskImage) { | ||
try { | ||
ctx.drawImage(maskImage, 0, 0, canvas.width, canvas.height); | ||
updateCanvasMask(canvas); | ||
} catch (e) { | ||
console.error('Invalid mask image'); | ||
console.error(e.toString()); | ||
} | ||
} | ||
gridSize: gridSize, | ||
var sizeRange = seriesModel.get('sizeRange'); | ||
var rotationRange = seriesModel.get('rotationRange'); | ||
var valueExtent = data.getDataExtent('value'); | ||
ellipticity: gridRect.height / gridRect.width, | ||
var DEGREE_TO_RAD = Math.PI / 180; | ||
var gridSize = seriesModel.get('gridSize'); | ||
layout(canvas, { | ||
list: data | ||
.mapArray('value', function (value, idx) { | ||
var itemModel = data.getItemModel(idx); | ||
return [ | ||
data.getName(idx), | ||
itemModel.get('textStyle.fontSize', true) || | ||
external_echarts_.number.linearMap(value, valueExtent, sizeRange), | ||
idx | ||
]; | ||
}) | ||
.sort(function (a, b) { | ||
// Sort from large to small in case there is no more room for more words | ||
return b[1] - a[1]; | ||
}), | ||
fontFamily: | ||
seriesModel.get('textStyle.fontFamily') || | ||
seriesModel.get('emphasis.textStyle.fontFamily') || | ||
ecModel.get('textStyle.fontFamily'), | ||
fontWeight: | ||
seriesModel.get('textStyle.fontWeight') || | ||
seriesModel.get('emphasis.textStyle.fontWeight') || | ||
ecModel.get('textStyle.fontWeight'), | ||
minRotation: rotationRange[0] * DEGREE_TO_RAD, | ||
maxRotation: rotationRange[1] * DEGREE_TO_RAD, | ||
gridSize: gridSize, | ||
clearCanvas: !maskImage, | ||
ellipticity: gridRect.height / gridRect.width, | ||
rotateRatio: 1, | ||
minRotation: rotationRange[0] * DEGREE_TO_RAD, | ||
maxRotation: rotationRange[1] * DEGREE_TO_RAD, | ||
rotationStep: seriesModel.get('rotationStep') * DEGREE_TO_RAD, | ||
clearCanvas: !maskImage, | ||
drawOutOfBound: seriesModel.get('drawOutOfBound'), | ||
rotateRatio: 1, | ||
layoutAnimation: seriesModel.get('layoutAnimation'), | ||
rotationStep: seriesModel.get('rotationStep') * DEGREE_TO_RAD, | ||
shuffle: false, | ||
drawOutOfBound: seriesModel.get('drawOutOfBound'), | ||
shrinkToFit: seriesModel.get('shrinkToFit'), | ||
shape: seriesModel.get('shape') | ||
}); | ||
layoutAnimation: seriesModel.get('layoutAnimation'), | ||
function onWordCloudDrawn(e) { | ||
var item = e.detail.item; | ||
if (e.detail.drawn && seriesModel.layoutInstance.ondraw) { | ||
e.detail.drawn.gx += gridRect.x / gridSize; | ||
e.detail.drawn.gy += gridRect.y / gridSize; | ||
seriesModel.layoutInstance.ondraw( | ||
item[0], item[1], item[2], e.detail.drawn | ||
); | ||
} | ||
} | ||
shuffle: false, | ||
canvas.addEventListener('wordclouddrawn', onWordCloudDrawn); | ||
shape: seriesModel.get('shape') | ||
}); | ||
if (seriesModel.layoutInstance) { | ||
// Dispose previous | ||
seriesModel.layoutInstance.dispose(); | ||
} | ||
function onWordCloudDrawn(e) { | ||
var item = e.detail.item; | ||
if (e.detail.drawn && seriesModel.layoutInstance.ondraw) { | ||
e.detail.drawn.gx += gridRect.x / gridSize; | ||
e.detail.drawn.gy += gridRect.y / gridSize; | ||
seriesModel.layoutInstance.ondraw( | ||
item[0], | ||
item[1], | ||
item[2], | ||
e.detail.drawn | ||
); | ||
} | ||
} | ||
seriesModel.layoutInstance = { | ||
ondraw: null, | ||
canvas.addEventListener('wordclouddrawn', onWordCloudDrawn); | ||
dispose: function () { | ||
canvas.removeEventListener('wordclouddrawn', onWordCloudDrawn); | ||
// Abort | ||
canvas.addEventListener('wordclouddrawn', function (e) { | ||
// Prevent default to cancle the event and stop the loop | ||
e.preventDefault(); | ||
}); | ||
} | ||
}; | ||
}); | ||
if (seriesModel.layoutInstance) { | ||
// Dispose previous | ||
seriesModel.layoutInstance.dispose(); | ||
} | ||
seriesModel.layoutInstance = { | ||
ondraw: null, | ||
dispose: function () { | ||
canvas.removeEventListener('wordclouddrawn', onWordCloudDrawn); | ||
// Abort | ||
canvas.addEventListener('wordclouddrawn', function (e) { | ||
// Prevent default to cancle the event and stop the loop | ||
e.preventDefault(); | ||
}); | ||
} | ||
}; | ||
}); | ||
}); | ||
external_echarts_.registerPreprocessor(function (option) { | ||
var series = (option || {}).series; | ||
!external_echarts_.util.isArray(series) && (series = series ? [series] : []); | ||
var series = (option || {}).series; | ||
!external_echarts_.util.isArray(series) && (series = series ? [series] : []); | ||
var compats = ['shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY']; | ||
var compats = ['shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY']; | ||
external_echarts_.util.each(series, function (seriesItem) { | ||
if (seriesItem && seriesItem.type === 'wordCloud') { | ||
var textStyle = seriesItem.textStyle || {}; | ||
external_echarts_.util.each(series, function (seriesItem) { | ||
if (seriesItem && seriesItem.type === 'wordCloud') { | ||
var textStyle = seriesItem.textStyle || {}; | ||
compatTextStyle(textStyle.normal); | ||
compatTextStyle(textStyle.emphasis); | ||
compatTextStyle(textStyle.normal); | ||
compatTextStyle(textStyle.emphasis); | ||
} | ||
}); | ||
function compatTextStyle(textStyle) { | ||
textStyle && | ||
external_echarts_.util.each(compats, function (key) { | ||
if (textStyle.hasOwnProperty(key)) { | ||
textStyle['text' + external_echarts_.format.capitalFirst(key)] = textStyle[key]; | ||
} | ||
}); | ||
function compatTextStyle(textStyle) { | ||
textStyle && external_echarts_.util.each(compats, function (key) { | ||
if (textStyle.hasOwnProperty(key)) { | ||
textStyle['text' + external_echarts_.format.capitalFirst(key)] = textStyle[key]; | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
}); | ||
function adjustRectAspect(gridRect, aspect) { | ||
// var outerWidth = gridRect.width + gridRect.x * 2; | ||
// var outerHeight = gridRect.height + gridRect.y * 2; | ||
var width = gridRect.width; | ||
var height = gridRect.height; | ||
if (width > height * aspect) { | ||
gridRect.x += (width - height * aspect) / 2; | ||
gridRect.width = height * aspect; | ||
} else { | ||
gridRect.y += (height - width / aspect) / 2; | ||
gridRect.height = width / aspect; | ||
} | ||
} | ||
;// CONCATENATED MODULE: ./index.js | ||
@@ -1542,0 +1727,0 @@ |
/*! For license information please see echarts-wordcloud.min.js.LICENSE.txt */ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("echarts")):"function"==typeof define&&define.amd?define(["echarts"],e):"object"==typeof exports?exports["echarts-wordcloud"]=e(require("echarts")):t["echarts-wordcloud"]=e(t.echarts)}(self,(function(t){return(()=>{"use strict";var e={638:(t,e,a)=>{a.r(e);var o=a(83);o.extendSeriesModel({type:"series.wordCloud",visualStyleAccessPath:"textStyle",visualStyleMapper:function(t){return{fill:t.get("color")}},visualDrawType:"fill",optionUpdated:function(){var t=this.option;t.gridSize=Math.max(Math.floor(t.gridSize),4)},getInitialData:function(t,e){var a=o.helper.createDimensions(t.data,{coordDimensions:["value"]}),i=new o.List(a,this);return i.initData(t.data),i},defaultOption:{maskImage:null,shape:"circle",left:"center",top:"center",width:"70%",height:"80%",sizeRange:[12,60],rotationRange:[-90,90],rotationStep:45,gridSize:8,drawOutOfBound:!1,textStyle:{fontWeight:"normal"}}}),o.extendChartView({type:"wordCloud",render:function(t,e,a){var i=this.group;i.removeAll();var r=t.getData(),n=t.get("gridSize");t.layoutInstance.ondraw=function(e,a,s,l){var d=r.getItemModel(s),u=d.getModel("textStyle"),f=new o.graphic.Text({style:o.helper.createTextStyle(u),scaleX:1/l.info.mu,scaleY:1/l.info.mu,x:(l.gx+l.info.gw/2)*n,y:(l.gy+l.info.gh/2)*n,rotation:l.rot});f.setStyle({x:l.info.fillTextOffsetX,y:l.info.fillTextOffsetY+.5*a,text:e,verticalAlign:"middle",fill:r.getItemVisual(s,"style").fill,fontSize:a}),i.add(f),r.setItemGraphicEl(s,f),f.ensureState("emphasis").style=o.helper.createTextStyle(d.getModel(["emphasis","textStyle"]),{state:"emphasis"}),f.ensureState("blur").style=o.helper.createTextStyle(d.getModel(["blur","textStyle"]),{state:"blur"}),o.helper.enableHoverEmphasis(f,d.get(["emphasis","focus"]),d.get(["emphasis","blurScope"])),f.stateTransition={duration:t.get("animation")?t.get(["stateAnimation","duration"]):0,easing:t.get(["stateAnimation","easing"])},f.__highDownDispatcher=!0},this._model=t},remove:function(){this.group.removeAll(),this._model.layoutInstance.dispose()},dispose:function(){this._model.layoutInstance.dispose()}}),window.setImmediate||(window.setImmediate=window.msSetImmediate||window.webkitSetImmediate||window.mozSetImmediate||window.oSetImmediate||function(){if(!window.postMessage||!window.addEventListener)return null;var t=[void 0],e="zero-timeout-message";return window.addEventListener("message",(function(a){if("string"==typeof a.data&&a.data.substr(0,e.length)===e){a.stopImmediatePropagation();var o=parseInt(a.data.substr(e.length),36);t[o]&&(t[o](),t[o]=void 0)}}),!0),window.clearImmediate=function(e){t[e]&&(t[e]=void 0)},function(a){var o=t.length;return t.push(a),window.postMessage(e+o.toString(36),"*"),o}}()||function(t){window.setTimeout(t,0)}),window.clearImmediate||(window.clearImmediate=window.msClearImmediate||window.webkitClearImmediate||window.mozClearImmediate||window.oClearImmediate||function(t){window.clearTimeout(t)});var i=function(){var t=document.createElement("canvas");if(!t||!t.getContext)return!1;var e=t.getContext("2d");return!!(e.getImageData&&e.fillText&&Array.prototype.some&&Array.prototype.push)}(),r=function(){if(i){for(var t,e,a=document.createElement("canvas").getContext("2d"),o=20;o;){if(a.font=o.toString(10)+"px sans-serif",a.measureText("W").width===t&&a.measureText("m").width===e)return o+1;t=a.measureText("W").width,e=a.measureText("m").width,o--}return 0}}(),n=function(t){for(var e,a,o=t.length;o;e=Math.floor(Math.random()*o),a=t[--o],t[o]=t[e],t[e]=a);return t},s=function(t,e){if(i){Array.isArray(t)||(t=[t]),t.forEach((function(e,a){if("string"==typeof e){if(t[a]=document.getElementById(e),!t[a])throw"The element id specified is not found."}else if(!e.tagName&&!e.appendChild)throw"You must pass valid HTML elements, or ID of the element."}));var a={list:[],fontFamily:'"Trebuchet MS", "Heiti TC", "微軟正黑體", "Arial Unicode MS", "Droid Fallback Sans", sans-serif',fontWeight:"normal",color:"random-dark",minSize:0,weightFactor:1,clearCanvas:!0,backgroundColor:"#fff",gridSize:8,drawOutOfBound:!1,origin:null,drawMask:!1,maskColor:"rgba(255,0,0,0.3)",maskGapWidth:.3,layoutAnimation:!0,wait:0,abortThreshold:0,abort:function(){},minRotation:-Math.PI/2,maxRotation:Math.PI/2,rotationStep:.1,shuffle:!0,rotateRatio:.1,shape:"circle",ellipticity:.65,classes:null,hover:null,click:null};if(e)for(var o in e)o in a&&(a[o]=e[o]);if("function"!=typeof a.weightFactor){var s=a.weightFactor;a.weightFactor=function(t){return t*s}}if("function"!=typeof a.shape)switch(a.shape){case"circle":default:a.shape="circle";break;case"cardioid":a.shape=function(t){return 1-Math.sin(t)};break;case"diamond":case"square":a.shape=function(t){var e=t%(2*Math.PI/4);return 1/(Math.cos(e)+Math.sin(e))};break;case"triangle-forward":a.shape=function(t){var e=t%(2*Math.PI/3);return 1/(Math.cos(e)+Math.sqrt(3)*Math.sin(e))};break;case"triangle":case"triangle-upright":a.shape=function(t){var e=(t+3*Math.PI/2)%(2*Math.PI/3);return 1/(Math.cos(e)+Math.sqrt(3)*Math.sin(e))};break;case"pentagon":a.shape=function(t){var e=(t+.955)%(2*Math.PI/5);return 1/(Math.cos(e)+.726543*Math.sin(e))};break;case"star":a.shape=function(t){var e=(t+.955)%(2*Math.PI/10);return(t+.955)%(2*Math.PI/5)-2*Math.PI/10>=0?1/(Math.cos(2*Math.PI/10-e)+3.07768*Math.sin(2*Math.PI/10-e)):1/(Math.cos(e)+3.07768*Math.sin(e))}}a.gridSize=Math.max(Math.floor(a.gridSize),4);var l,d,u,f,c,h,m,g=a.gridSize,v=g-a.maskGapWidth,w=Math.abs(a.maxRotation-a.minRotation),p=Math.min(a.maxRotation,a.minRotation),y=a.rotationStep;switch(a.color){case"random-dark":m=function(){return R(10,50)};break;case"random-light":m=function(){return R(50,90)};break;default:"function"==typeof a.color&&(m=a.color)}var x=null;"function"==typeof a.classes&&(x=a.classes);var M,S=!1,b=[],I=function(t){var e,a,o=t.currentTarget,i=o.getBoundingClientRect();t.touches?(e=t.touches[0].clientX,a=t.touches[0].clientY):(e=t.clientX,a=t.clientY);var r=e-i.left,n=a-i.top,s=Math.floor(r*(o.width/i.width||1)/g),l=Math.floor(n*(o.height/i.height||1)/g);return b[s][l]},T=function(t){var e=I(t);M!==e&&(M=e,e?a.hover(e.item,e.dimension,t):a.hover(void 0,void 0,t))},C=function(t){var e=I(t);e&&(a.click(e.item,e.dimension,t),t.preventDefault())},E=[],k=function(t){if(E[t])return E[t];var e=8*t,o=e,i=[];for(0===t&&i.push([f[0],f[1],0]);o--;){var r=1;"circle"!==a.shape&&(r=a.shape(o/e*2*Math.PI)),i.push([f[0]+t*r*Math.cos(-o/e*2*Math.PI),f[1]+t*r*Math.sin(-o/e*2*Math.PI)*a.ellipticity,o/e*2*Math.PI])}return E[t]=i,i},O=function(){return a.abortThreshold>0&&(new Date).getTime()-h>a.abortThreshold},P=function(e,a,o,i,r){e>=d||a>=u||e<0||a<0||(l[e][a]=!1,o&&t[0].getContext("2d").fillRect(e*g,a*g,v,v),S&&(b[e][a]={item:r,dimension:i}))},D=function(e,a,o){if(a)return!t.some((function(t){var i=document.createEvent("CustomEvent");return i.initCustomEvent(e,!0,a,o||{}),!t.dispatchEvent(i)}),this);t.forEach((function(t){var i=document.createEvent("CustomEvent");i.initCustomEvent(e,!0,a,o||{}),t.dispatchEvent(i)}),this)};!function(){var e=t[0];if(e.getContext)d=Math.ceil(e.width/g),u=Math.ceil(e.height/g);else{var o=e.getBoundingClientRect();d=Math.ceil(o.width/g),u=Math.ceil(o.height/g)}if(D("wordcloudstart",!0)){var i,s,v,I,E;if(f=a.origin?[a.origin[0]/g,a.origin[1]/g]:[d/2,u/2],c=Math.floor(Math.sqrt(d*d+u*u)),l=[],!e.getContext||a.clearCanvas)for(t.forEach((function(t){if(t.getContext){var e=t.getContext("2d");e.fillStyle=a.backgroundColor,e.clearRect(0,0,d*(g+1),u*(g+1)),e.fillRect(0,0,d*(g+1),u*(g+1))}else t.textContent="",t.style.backgroundColor=a.backgroundColor,t.style.position="relative"})),i=d;i--;)for(l[i]=[],s=u;s--;)l[i][s]=!0;else{var R=document.createElement("canvas").getContext("2d");R.fillStyle=a.backgroundColor,R.fillRect(0,0,1,1);var A,z,F=R.getImageData(0,0,1,1).data,L=e.getContext("2d").getImageData(0,0,d*g,u*g).data;for(i=d;i--;)for(l[i]=[],s=u;s--;){z=g;t:for(;z--;)for(A=g;A--;)for(v=4;v--;)if(L[4*((s*g+z)*d*g+(i*g+A))+v]!==F[v]){l[i][s]=!1;break t}!1!==l[i][s]&&(l[i][s]=!0)}L=R=F=void 0}if(a.hover||a.click){for(S=!0,i=d+1;i--;)b[i]=[];a.hover&&e.addEventListener("mousemove",T),a.click&&(e.addEventListener("click",C),e.addEventListener("touchstart",C),e.addEventListener("touchend",(function(t){t.preventDefault()})),e.style.webkitTapHighlightColor="rgba(0, 0, 0, 0)"),e.addEventListener("wordcloudstart",(function t(){e.removeEventListener("wordcloudstart",t),e.removeEventListener("mousemove",T),e.removeEventListener("click",C),M=void 0}))}v=0;var W=!0;a.layoutAnimation?0!==a.wait?(I=window.setTimeout,E=window.clearTimeout):(I=window.setImmediate,E=window.clearImmediate):(I=function(t){t()},E=function(){W=!1});var B=function(e,a){t.forEach((function(t){t.removeEventListener(e,a)}),this)},X=function t(){B("wordcloudstart",t),E(Y)};!function(e,a){t.forEach((function(t){t.addEventListener("wordcloudstart",a)}),this)}(0,X);var Y=(a.layoutAnimation?I:setTimeout)((function e(){if(W){if(v>=a.list.length)return E(Y),D("wordcloudstop",!1),void B("wordcloudstart",X);h=(new Date).getTime();var o=function(e){var o,i,s;Array.isArray(e)?(o=e[0],i=e[1]):(o=e.word,i=e.weight,s=e.attributes);var f,h,v,M=0===a.rotateRatio||Math.random()>a.rotateRatio?0:0===w?p:p+Math.round(Math.random()*w/y)*y,b=function(t,e,o){var i=a.weightFactor(e);if(i<=a.minSize)return!1;var n=1;i<r&&(n=function(){for(var t=2;t*i<r;)t+=2;return t}());var s=document.createElement("canvas"),l=s.getContext("2d",{willReadFrequently:!0});l.font=a.fontWeight+" "+(i*n).toString(10)+"px "+a.fontFamily;var d=l.measureText(t).width/n,u=Math.max(i*n,l.measureText("m").width,l.measureText("W").width)/n,f=d+2*u,c=3*u,h=Math.ceil(f/g),m=Math.ceil(c/g);f=h*g,c=m*g;var v=-d/2,w=.4*-u,p=Math.ceil((f*Math.abs(Math.sin(o))+c*Math.abs(Math.cos(o)))/g),y=Math.ceil((f*Math.abs(Math.cos(o))+c*Math.abs(Math.sin(o)))/g),x=y*g,M=p*g;s.setAttribute("width",x),s.setAttribute("height",M),l.scale(1/n,1/n),l.translate(x*n/2,M*n/2),l.rotate(-o),l.font=a.fontWeight+" "+(i*n).toString(10)+"px "+a.fontFamily,l.fillStyle="#000",l.textBaseline="middle",l.fillText(t,v*n,(w+.5*i)*n);var S=l.getImageData(0,0,x,M).data;if(O())return!1;for(var b,I,T,C=[],E=y,k=[p/2,y/2,p/2,y/2];E--;)for(b=p;b--;){T=g;t:for(;T--;)for(I=g;I--;)if(S[4*((b*g+T)*x+(E*g+I))+3]){C.push([E,b]),E<k[3]&&(k[3]=E),E>k[1]&&(k[1]=E),b<k[0]&&(k[0]=b),b>k[2]&&(k[2]=b);break t}}return{mu:n,occupied:C,bounds:k,gw:y,gh:p,fillTextOffsetX:v,fillTextOffsetY:w,fillTextWidth:d,fillTextHeight:u,fontSize:i}}(o,i,M);if(!b)return!1;if(O())return!1;if(!a.drawOutOfBound){var I=b.bounds;if(I[1]-I[3]+1>d||I[2]-I[0]+1>u)return!1}for(var T=c+1;T--;){var C=k(c-T);a.shuffle&&(C=[].concat(C),n(C));for(var E=0;E<C.length;E++){var D=(f=C[E],h=void 0,v=void 0,h=Math.floor(f[0]-b.gw/2),v=Math.floor(f[1]-b.gh/2),b.gw,b.gh,!!function(t,e,o,i,r){for(var n=r.length;n--;){var s=t+r[n][0],f=e+r[n][1];if(s>=d||f>=u||s<0||f<0){if(!a.drawOutOfBound)return!1}else if(!l[s][f])return!1}return!0}(h,v,0,0,b.occupied)&&(function(e,o,i,r,n,s,l,d,u){var f,c,h=i.fontSize;f=m?m(r,n,h,s,l):a.color,c=x?x(r,n,h,s,l):a.classes;var v=i.bounds;v[3],v[0],v[1],v[3],v[2],v[0],t.forEach((function(t){if(t.getContext){var n=t.getContext("2d"),s=i.mu;n.save(),n.scale(1/s,1/s),n.font=a.fontWeight+" "+(h*s).toString(10)+"px "+a.fontFamily,n.fillStyle=f,n.translate((e+i.gw/2)*g*s,(o+i.gh/2)*g*s),0!==d&&n.rotate(-d),n.textBaseline="middle",n.fillText(r,i.fillTextOffsetX*s,(i.fillTextOffsetY+.5*h)*s),n.restore()}else{var l=document.createElement("span"),m="";m="rotate("+-d/Math.PI*180+"deg) ",1!==i.mu&&(m+="translateX(-"+i.fillTextWidth/4+"px) scale("+1/i.mu+")");var v={position:"absolute",display:"block",font:a.fontWeight+" "+h*i.mu+"px "+a.fontFamily,left:(e+i.gw/2)*g+i.fillTextOffsetX+"px",top:(o+i.gh/2)*g+i.fillTextOffsetY+"px",width:i.fillTextWidth+"px",height:i.fillTextHeight+"px",lineHeight:h+"px",whiteSpace:"nowrap",transform:m,webkitTransform:m,msTransform:m,transformOrigin:"50% 40%",webkitTransformOrigin:"50% 40%",msTransformOrigin:"50% 40%"};for(var w in f&&(v.color=f),l.textContent=r,v)l.style[w]=v[w];if(u)for(var p in u)l.setAttribute(p,u[p]);c&&(l.className+=c),t.appendChild(l)}}))}(h,v,b,o,i,c-T,f[2],M,s),function(e,o,i,r,n,s){var l,f,c=n.occupied,h=a.drawMask;if(h&&((l=t[0].getContext("2d")).save(),l.fillStyle=a.maskColor),S){var m=n.bounds;f={x:(e+m[3])*g,y:(o+m[0])*g,w:(m[1]-m[3]+1)*g,h:(m[2]-m[0]+1)*g}}for(var v=c.length;v--;){var w=e+c[v][0],p=o+c[v][1];w>=d||p>=u||w<0||p<0||P(w,p,h,f,s)}h&&l.restore()}(h,v,0,0,b,e),{gx:h,gy:v,rot:M,info:b}));if(D)return D}}return null}(a.list[v]),i=!D("wordclouddrawn",!0,{item:a.list[v],drawn:o});if(O()||i)return E(Y),a.abort(),D("wordcloudabort",!1),D("wordcloudstop",!1),void B("wordcloudstart",X);v++,Y=I(e,a.wait)}}),a.wait)}}()}function R(t,e){return"hsl("+(360*Math.random()).toFixed()+","+(30*Math.random()+70).toFixed()+"%,"+(Math.random()*(e-t)+t).toFixed()+"%)"}};s.isSupported=i,s.minFontSize=r;const l=s;if(!l.isSupported)throw new Error("Sorry your browser not support wordCloud");o.registerLayout((function(t,e){t.eachSeriesByType("wordCloud",(function(a){var i=o.helper.getLayoutRect(a.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()}),r=a.getData(),n=document.createElement("canvas");n.width=i.width,n.height=i.height;var s=n.getContext("2d"),d=a.get("maskImage");if(d)try{s.drawImage(d,0,0,n.width,n.height),function(t){for(var e=t.getContext("2d"),a=e.getImageData(0,0,t.width,t.height),o=e.createImageData(a),i=0,r=0,n=0;n<a.data.length;n+=4)a.data[n+3]>128&&(i+=l=a.data[n]+a.data[n+1]+a.data[n+2],++r);var s=i/r;for(n=0;n<a.data.length;n+=4){var l=a.data[n]+a.data[n+1]+a.data[n+2];a.data[n+3]<128||l>s?(o.data[n]=0,o.data[n+1]=0,o.data[n+2]=0,o.data[n+3]=0):(o.data[n]=255,o.data[n+1]=255,o.data[n+2]=255,o.data[n+3]=255)}e.putImageData(o,0,0)}(n)}catch(t){console.error("Invalid mask image"),console.error(t.toString())}var u=a.get("sizeRange"),f=a.get("rotationRange"),c=r.getDataExtent("value"),h=Math.PI/180,m=a.get("gridSize");function g(t){var e=t.detail.item;t.detail.drawn&&a.layoutInstance.ondraw&&(t.detail.drawn.gx+=i.x/m,t.detail.drawn.gy+=i.y/m,a.layoutInstance.ondraw(e[0],e[1],e[2],t.detail.drawn))}l(n,{list:r.mapArray("value",(function(t,e){var a=r.getItemModel(e);return[r.getName(e),a.get("textStyle.fontSize",!0)||o.number.linearMap(t,c,u),e]})).sort((function(t,e){return e[1]-t[1]})),fontFamily:a.get("textStyle.fontFamily")||a.get("emphasis.textStyle.fontFamily")||t.get("textStyle.fontFamily"),fontWeight:a.get("textStyle.fontWeight")||a.get("emphasis.textStyle.fontWeight")||t.get("textStyle.fontWeight"),gridSize:m,ellipticity:i.height/i.width,minRotation:f[0]*h,maxRotation:f[1]*h,clearCanvas:!d,rotateRatio:1,rotationStep:a.get("rotationStep")*h,drawOutOfBound:a.get("drawOutOfBound"),layoutAnimation:a.get("layoutAnimation"),shuffle:!1,shape:a.get("shape")}),n.addEventListener("wordclouddrawn",g),a.layoutInstance&&a.layoutInstance.dispose(),a.layoutInstance={ondraw:null,dispose:function(){n.removeEventListener("wordclouddrawn",g),n.addEventListener("wordclouddrawn",(function(t){t.preventDefault()}))}}}))})),o.registerPreprocessor((function(t){var e=(t||{}).series;!o.util.isArray(e)&&(e=e?[e]:[]);var a=["shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"];function i(t){t&&o.util.each(a,(function(e){t.hasOwnProperty(e)&&(t["text"+o.format.capitalFirst(e)]=t[e])}))}o.util.each(e,(function(t){if(t&&"wordCloud"===t.type){var e=t.textStyle||{};i(e.normal),i(e.emphasis)}}))}))},83:e=>{e.exports=t}},a={};function o(t){if(a[t])return a[t].exports;var i=a[t]={exports:{}};return e[t](i,i.exports,o),i.exports}return o.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o(638)})()})); | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("echarts")):"function"==typeof define&&define.amd?define(["echarts"],e):"object"==typeof exports?exports["echarts-wordcloud"]=e(require("echarts")):t["echarts-wordcloud"]=e(t.echarts)}(self,(function(t){return(()=>{"use strict";var e={638:(t,e,a)=>{a.r(e);var r=a(83);r.extendSeriesModel({type:"series.wordCloud",visualStyleAccessPath:"textStyle",visualStyleMapper:function(t){return{fill:t.get("color")}},visualDrawType:"fill",optionUpdated:function(){var t=this.option;t.gridSize=Math.max(Math.floor(t.gridSize),4)},getInitialData:function(t,e){var a=r.helper.createDimensions(t.data,{coordDimensions:["value"]}),i=new r.List(a,this);return i.initData(t.data),i},defaultOption:{maskImage:null,shape:"circle",keepAspect:!1,left:"center",top:"center",width:"70%",height:"80%",sizeRange:[12,60],rotationRange:[-90,90],rotationStep:45,gridSize:8,drawOutOfBound:!1,shrinkToFit:!1,textStyle:{fontWeight:"normal"}}}),r.extendChartView({type:"wordCloud",render:function(t,e,a){var i=this.group;i.removeAll();var o=t.getData(),n=t.get("gridSize");t.layoutInstance.ondraw=function(e,a,s,l){var d=o.getItemModel(s),u=d.getModel("textStyle"),f=new r.graphic.Text({style:r.helper.createTextStyle(u),scaleX:1/l.info.mu,scaleY:1/l.info.mu,x:(l.gx+l.info.gw/2)*n,y:(l.gy+l.info.gh/2)*n,rotation:l.rot});f.setStyle({x:l.info.fillTextOffsetX,y:l.info.fillTextOffsetY+.5*a,text:e,verticalAlign:"middle",fill:o.getItemVisual(s,"style").fill,fontSize:a}),i.add(f),o.setItemGraphicEl(s,f),f.ensureState("emphasis").style=r.helper.createTextStyle(d.getModel(["emphasis","textStyle"]),{state:"emphasis"}),f.ensureState("blur").style=r.helper.createTextStyle(d.getModel(["blur","textStyle"]),{state:"blur"}),r.helper.enableHoverEmphasis(f,d.get(["emphasis","focus"]),d.get(["emphasis","blurScope"])),f.stateTransition={duration:t.get("animation")?t.get(["stateAnimation","duration"]):0,easing:t.get(["stateAnimation","easing"])},f.__highDownDispatcher=!0},this._model=t},remove:function(){this.group.removeAll(),this._model.layoutInstance.dispose()},dispose:function(){this._model.layoutInstance.dispose()}}),window.setImmediate||(window.setImmediate=window.msSetImmediate||window.webkitSetImmediate||window.mozSetImmediate||window.oSetImmediate||function(){if(!window.postMessage||!window.addEventListener)return null;var t=[void 0],e="zero-timeout-message";return window.addEventListener("message",(function(a){if("string"==typeof a.data&&a.data.substr(0,e.length)===e){a.stopImmediatePropagation();var r=parseInt(a.data.substr(e.length),36);t[r]&&(t[r](),t[r]=void 0)}}),!0),window.clearImmediate=function(e){t[e]&&(t[e]=void 0)},function(a){var r=t.length;return t.push(a),window.postMessage(e+r.toString(36),"*"),r}}()||function(t){window.setTimeout(t,0)}),window.clearImmediate||(window.clearImmediate=window.msClearImmediate||window.webkitClearImmediate||window.mozClearImmediate||window.oClearImmediate||function(t){window.clearTimeout(t)});var i=function(){var t=document.createElement("canvas");if(!t||!t.getContext)return!1;var e=t.getContext("2d");return!!(e&&e.getImageData&&e.fillText&&Array.prototype.some&&Array.prototype.push)}(),o=function(){if(i){for(var t,e,a=document.createElement("canvas").getContext("2d"),r=20;r;){if(a.font=r.toString(10)+"px sans-serif",a.measureText("W").width===t&&a.measureText("m").width===e)return r+1;t=a.measureText("W").width,e=a.measureText("m").width,r--}return 0}}(),n=function(t){for(var e,a,r=t.length;r;)e=Math.floor(Math.random()*r),a=t[--r],t[r]=t[e],t[e]=a;return t},s={},l=function(t,e){if(i){var a=Math.floor(Math.random()*Date.now());Array.isArray(t)||(t=[t]),t.forEach((function(e,a){if("string"==typeof e){if(t[a]=document.getElementById(e),!t[a])throw new Error("The element id specified is not found.")}else if(!e.tagName&&!e.appendChild)throw new Error("You must pass valid HTML elements, or ID of the element.")}));var r={list:[],fontFamily:'"Trebuchet MS", "Heiti TC", "微軟正黑體", "Arial Unicode MS", "Droid Fallback Sans", sans-serif',fontWeight:"normal",color:"random-dark",minSize:0,weightFactor:1,clearCanvas:!0,backgroundColor:"#fff",gridSize:8,drawOutOfBound:!1,shrinkToFit:!1,origin:null,drawMask:!1,maskColor:"rgba(255,0,0,0.3)",maskGapWidth:.3,layoutAnimation:!0,wait:0,abortThreshold:0,abort:function(){},minRotation:-Math.PI/2,maxRotation:Math.PI/2,rotationStep:.1,shuffle:!0,rotateRatio:.1,shape:"circle",ellipticity:.65,classes:null,hover:null,click:null};if(e)for(var l in e)l in r&&(r[l]=e[l]);if("function"!=typeof r.weightFactor){var d=r.weightFactor;r.weightFactor=function(t){return t*d}}if("function"!=typeof r.shape)switch(r.shape){case"circle":default:r.shape="circle";break;case"cardioid":r.shape=function(t){return 1-Math.sin(t)};break;case"diamond":r.shape=function(t){var e=t%(2*Math.PI/4);return 1/(Math.cos(e)+Math.sin(e))};break;case"square":r.shape=function(t){return Math.min(1/Math.abs(Math.cos(t)),1/Math.abs(Math.sin(t)))};break;case"triangle-forward":r.shape=function(t){var e=t%(2*Math.PI/3);return 1/(Math.cos(e)+Math.sqrt(3)*Math.sin(e))};break;case"triangle":case"triangle-upright":r.shape=function(t){var e=(t+3*Math.PI/2)%(2*Math.PI/3);return 1/(Math.cos(e)+Math.sqrt(3)*Math.sin(e))};break;case"pentagon":r.shape=function(t){var e=(t+.955)%(2*Math.PI/5);return 1/(Math.cos(e)+.726543*Math.sin(e))};break;case"star":r.shape=function(t){var e=(t+.955)%(2*Math.PI/10);return(t+.955)%(2*Math.PI/5)-2*Math.PI/10>=0?1/(Math.cos(2*Math.PI/10-e)+3.07768*Math.sin(2*Math.PI/10-e)):1/(Math.cos(e)+3.07768*Math.sin(e))}}r.gridSize=Math.max(Math.floor(r.gridSize),4);var u,f,c,h,m,g,w,v,p=r.gridSize,y=p-r.maskGapWidth,x=Math.abs(r.maxRotation-r.minRotation),M=Math.min(r.maxRotation,r.minRotation),S=r.rotationStep;switch(r.color){case"random-dark":w=function(){return L(10,50)};break;case"random-light":w=function(){return L(50,90)};break;default:"function"==typeof r.color&&(w=r.color)}"function"==typeof r.fontWeight&&(v=r.fontWeight);var b=null;"function"==typeof r.classes&&(b=r.classes);var I,T=!1,k=[],C=function(t){var e,a,r=t.currentTarget,i=r.getBoundingClientRect();t.touches?(e=t.touches[0].clientX,a=t.touches[0].clientY):(e=t.clientX,a=t.clientY);var o=e-i.left,n=a-i.top,s=Math.floor(o*(r.width/i.width||1)/p),l=Math.floor(n*(r.height/i.height||1)/p);return k[s][l]},E=function(t){var e=C(t);I!==e&&(I=e,e?r.hover(e.item,e.dimension,t):r.hover(void 0,void 0,t))},A=function(t){var e=C(t);e&&(r.click(e.item,e.dimension,t),t.preventDefault())},O=[],F=function(t){if(O[t])return O[t];var e=8*t,a=e,i=[];for(0===t&&i.push([h[0],h[1],0]);a--;){var o=1;"circle"!==r.shape&&(o=r.shape(a/e*2*Math.PI)),i.push([h[0]+t*o*Math.cos(-a/e*2*Math.PI),h[1]+t*o*Math.sin(-a/e*2*Math.PI)*r.ellipticity,a/e*2*Math.PI])}return O[t]=i,i},D=function(){return r.abortThreshold>0&&(new Date).getTime()-g>r.abortThreshold},P=function(e,a,r,i,o){e>=f||a>=c||e<0||a<0||(u[e][a]=!1,r&&t[0].getContext("2d").fillRect(e*p,a*p,y,y),T&&(k[e][a]={item:o,dimension:i}))},R=function e(a){var i,s,l;Array.isArray(a)?(i=a[0],s=a[1]):(i=a.word,s=a.weight,l=a.attributes);var d,h,g,y=0===r.rotateRatio||Math.random()>r.rotateRatio?0:0===x?M:M+Math.round(Math.random()*x/S)*S,I=function(t){if(Array.isArray(t)){var e=t.slice();return e.splice(0,2),e}return[]}(a),k=function(t,e,a,i){var n=r.weightFactor(e);if(n<=r.minSize)return!1;var s,l=1;n<o&&(l=function(){for(var t=2;t*n<o;)t+=2;return t}()),s=v?v(t,e,n,i):r.fontWeight;var d=document.createElement("canvas"),u=d.getContext("2d",{willReadFrequently:!0});u.font=s+" "+(n*l).toString(10)+"px "+r.fontFamily;var f=u.measureText(t).width/l,c=Math.max(n*l,u.measureText("m").width,u.measureText("W").width)/l,h=f+2*c,m=3*c,g=Math.ceil(h/p),w=Math.ceil(m/p);h=g*p,m=w*p;var y=-f/2,x=.4*-c,M=Math.ceil((h*Math.abs(Math.sin(a))+m*Math.abs(Math.cos(a)))/p),S=Math.ceil((h*Math.abs(Math.cos(a))+m*Math.abs(Math.sin(a)))/p),b=S*p,I=M*p;d.setAttribute("width",b),d.setAttribute("height",I),u.scale(1/l,1/l),u.translate(b*l/2,I*l/2),u.rotate(-a),u.font=s+" "+(n*l).toString(10)+"px "+r.fontFamily,u.fillStyle="#000",u.textBaseline="middle",u.fillText(t,y*l,(x+.5*n)*l);var T=u.getImageData(0,0,b,I).data;if(D())return!1;for(var k,C,E,A=[],O=S,F=[M/2,S/2,M/2,S/2];O--;)for(k=M;k--;){E=p;t:for(;E--;)for(C=p;C--;)if(T[4*((k*p+E)*b+(O*p+C))+3]){A.push([O,k]),O<F[3]&&(F[3]=O),O>F[1]&&(F[1]=O),k<F[0]&&(F[0]=k),k>F[2]&&(F[2]=k);break t}}return{mu:l,occupied:A,bounds:F,gw:S,gh:M,fillTextOffsetX:y,fillTextOffsetY:x,fillTextWidth:f,fillTextHeight:c,fontSize:n}}(i,s,y,I);if(!k)return!1;if(D())return!1;if(!r.drawOutOfBound&&!r.shrinkToFit){var C=k.bounds;if(C[1]-C[3]+1>f||C[2]-C[0]+1>c)return!1}for(var E=m+1;E--;){var A=F(m-E);r.shuffle&&(A=[].concat(A),n(A));for(var O=0;O<A.length;O++){var R=(d=A[O],h=void 0,g=void 0,h=Math.floor(d[0]-k.gw/2),g=Math.floor(d[1]-k.gh/2),k.gw,k.gh,!!function(t,e,a,i,o){for(var n=o.length;n--;){var s=t+o[n][0],l=e+o[n][1];if(s>=f||l>=c||s<0||l<0){if(!r.drawOutOfBound)return!1}else if(!u[s][l])return!1}return!0}(h,g,0,0,k.occupied)&&(function(e,a,i,o,n,s,l,d,u,f){var c,h,m,g=i.fontSize;c=w?w(o,n,g,s,l,f):r.color,h=v?v(o,n,g,f):r.fontWeight,m=b?b(o,n,g,f):r.classes,t.forEach((function(t){if(t.getContext){var n=t.getContext("2d"),s=i.mu;n.save(),n.scale(1/s,1/s),n.font=h+" "+(g*s).toString(10)+"px "+r.fontFamily,n.fillStyle=c,n.translate((e+i.gw/2)*p*s,(a+i.gh/2)*p*s),0!==d&&n.rotate(-d),n.textBaseline="middle",n.fillText(o,i.fillTextOffsetX*s,(i.fillTextOffsetY+.5*g)*s),n.restore()}else{var l=document.createElement("span"),f="";f="rotate("+-d/Math.PI*180+"deg) ",1!==i.mu&&(f+="translateX(-"+i.fillTextWidth/4+"px) scale("+1/i.mu+")");var w={position:"absolute",display:"block",font:h+" "+g*i.mu+"px "+r.fontFamily,left:(e+i.gw/2)*p+i.fillTextOffsetX+"px",top:(a+i.gh/2)*p+i.fillTextOffsetY+"px",width:i.fillTextWidth+"px",height:i.fillTextHeight+"px",lineHeight:g+"px",whiteSpace:"nowrap",transform:f,webkitTransform:f,msTransform:f,transformOrigin:"50% 40%",webkitTransformOrigin:"50% 40%",msTransformOrigin:"50% 40%"};for(var v in c&&(w.color=c),l.textContent=o,w)l.style[v]=w[v];if(u)for(var y in u)l.setAttribute(y,u[y]);m&&(l.className+=m),t.appendChild(l)}}))}(h,g,k,i,s,m-E,d[2],y,l,I),function(e,a,i,o,n,s){var l,d,u=n.occupied,h=r.drawMask;if(h&&((l=t[0].getContext("2d")).save(),l.fillStyle=r.maskColor),T){var m=n.bounds;d={x:(e+m[3])*p,y:(a+m[0])*p,w:(m[1]-m[3]+1)*p,h:(m[2]-m[0]+1)*p}}for(var g=u.length;g--;){var w=e+u[g][0],v=a+u[g][1];w>=f||v>=c||w<0||v<0||P(w,v,h,d,s)}h&&l.restore()}(h,g,0,0,k,a),{gx:h,gy:g,rot:y,info:k}));if(R)return R}if(r.shrinkToFit)return Array.isArray(a)?a[1]=3*a[1]/4:a.weight=3*a.weight/4,e(a)}return null},z=function(e,a,r){if(a)return!t.some((function(t){var a=new CustomEvent(e,{detail:r||{}});return!t.dispatchEvent(a)}),this);t.forEach((function(t){var a=new CustomEvent(e,{detail:r||{}});t.dispatchEvent(a)}),this)};!function(){var e=t[0];if(e.getContext)f=Math.ceil(e.width/p),c=Math.ceil(e.height/p);else{var i=e.getBoundingClientRect();f=Math.ceil(i.width/p),c=Math.ceil(i.height/p)}if(z("wordcloudstart",!0)){var o,n,l,d,w;if(h=r.origin?[r.origin[0]/p,r.origin[1]/p]:[f/2,c/2],m=Math.floor(Math.sqrt(f*f+c*c)),u=[],!e.getContext||r.clearCanvas)for(t.forEach((function(t){if(t.getContext){var e=t.getContext("2d");e.fillStyle=r.backgroundColor,e.clearRect(0,0,f*(p+1),c*(p+1)),e.fillRect(0,0,f*(p+1),c*(p+1))}else t.textContent="",t.style.backgroundColor=r.backgroundColor,t.style.position="relative"})),o=f;o--;)for(u[o]=[],n=c;n--;)u[o][n]=!0;else{var v=document.createElement("canvas").getContext("2d");v.fillStyle=r.backgroundColor,v.fillRect(0,0,1,1);var y,x,M=v.getImageData(0,0,1,1).data,S=e.getContext("2d").getImageData(0,0,f*p,c*p).data;for(o=f;o--;)for(u[o]=[],n=c;n--;){x=p;t:for(;x--;)for(y=p;y--;)for(l=4;l--;)if(S[4*((n*p+x)*f*p+(o*p+y))+l]!==M[l]){u[o][n]=!1;break t}!1!==u[o][n]&&(u[o][n]=!0)}S=v=M=void 0}if(r.hover||r.click){for(T=!0,o=f+1;o--;)k[o]=[];r.hover&&e.addEventListener("mousemove",E),r.click&&(e.addEventListener("click",A),e.addEventListener("touchstart",A),e.addEventListener("touchend",(function(t){t.preventDefault()})),e.style.webkitTapHighlightColor="rgba(0, 0, 0, 0)"),e.addEventListener("wordcloudstart",(function t(){e.removeEventListener("wordcloudstart",t),e.removeEventListener("mousemove",E),e.removeEventListener("click",A),I=void 0}))}l=0;var b=!0;r.layoutAnimation?0!==r.wait?(d=window.setTimeout,w=window.clearTimeout):(d=window.setImmediate,w=window.clearImmediate):(d=function(t){t()},w=function(){b=!1});var C=function(e,a){t.forEach((function(t){t.removeEventListener(e,a)}),this)},O=function t(){C("wordcloudstart",t),w(s[a])};!function(e,a){t.forEach((function(t){t.addEventListener("wordcloudstart",a)}),this)}(0,O),s[a]=(r.layoutAnimation?d:setTimeout)((function t(){if(b){if(l>=r.list.length)return w(s[a]),z("wordcloudstop",!1),C("wordcloudstart",O),void delete s[a];g=(new Date).getTime();var e=R(r.list[l]),i=!z("wordclouddrawn",!0,{item:r.list[l],drawn:e});if(D()||i)return w(s[a]),r.abort(),z("wordcloudabort",!1),z("wordcloudstop",!1),void C("wordcloudstart",O);l++,s[a]=d(t,r.wait)}}),r.wait)}}()}function L(t,e){return"hsl("+(360*Math.random()).toFixed()+","+(30*Math.random()+70).toFixed()+"%,"+(Math.random()*(e-t)+t).toFixed()+"%)"}};l.isSupported=i,l.minFontSize=o;const d=l;if(!d.isSupported)throw new Error("Sorry your browser not support wordCloud");r.registerLayout((function(t,e){t.eachSeriesByType("wordCloud",(function(a){var i=r.helper.getLayoutRect(a.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()}),o=a.get("keepAspect"),n=a.get("maskImage"),s=n?n.width/n.height:1;o&&function(t,e){var a=t.width,r=t.height;a>r*e?(t.x+=(a-r*e)/2,t.width=r*e):(t.y+=(r-a/e)/2,t.height=a/e)}(i,s);var l=a.getData(),u=document.createElement("canvas");u.width=i.width,u.height=i.height;var f=u.getContext("2d");if(n)try{f.drawImage(n,0,0,u.width,u.height),function(t){for(var e=t.getContext("2d"),a=e.getImageData(0,0,t.width,t.height),r=e.createImageData(a),i=0,o=0,n=0;n<a.data.length;n+=4)a.data[n+3]>128&&(i+=l=a.data[n]+a.data[n+1]+a.data[n+2],++o);var s=i/o;for(n=0;n<a.data.length;n+=4){var l=a.data[n]+a.data[n+1]+a.data[n+2];a.data[n+3]<128||l>s?(r.data[n]=0,r.data[n+1]=0,r.data[n+2]=0,r.data[n+3]=0):(r.data[n]=255,r.data[n+1]=255,r.data[n+2]=255,r.data[n+3]=255)}e.putImageData(r,0,0)}(u)}catch(t){console.error("Invalid mask image"),console.error(t.toString())}var c=a.get("sizeRange"),h=a.get("rotationRange"),m=l.getDataExtent("value"),g=Math.PI/180,w=a.get("gridSize");function v(t){var e=t.detail.item;t.detail.drawn&&a.layoutInstance.ondraw&&(t.detail.drawn.gx+=i.x/w,t.detail.drawn.gy+=i.y/w,a.layoutInstance.ondraw(e[0],e[1],e[2],t.detail.drawn))}d(u,{list:l.mapArray("value",(function(t,e){var a=l.getItemModel(e);return[l.getName(e),a.get("textStyle.fontSize",!0)||r.number.linearMap(t,m,c),e]})).sort((function(t,e){return e[1]-t[1]})),fontFamily:a.get("textStyle.fontFamily")||a.get("emphasis.textStyle.fontFamily")||t.get("textStyle.fontFamily"),fontWeight:a.get("textStyle.fontWeight")||a.get("emphasis.textStyle.fontWeight")||t.get("textStyle.fontWeight"),gridSize:w,ellipticity:i.height/i.width,minRotation:h[0]*g,maxRotation:h[1]*g,clearCanvas:!n,rotateRatio:1,rotationStep:a.get("rotationStep")*g,drawOutOfBound:a.get("drawOutOfBound"),shrinkToFit:a.get("shrinkToFit"),layoutAnimation:a.get("layoutAnimation"),shuffle:!1,shape:a.get("shape")}),u.addEventListener("wordclouddrawn",v),a.layoutInstance&&a.layoutInstance.dispose(),a.layoutInstance={ondraw:null,dispose:function(){u.removeEventListener("wordclouddrawn",v),u.addEventListener("wordclouddrawn",(function(t){t.preventDefault()}))}}}))})),r.registerPreprocessor((function(t){var e=(t||{}).series;!r.util.isArray(e)&&(e=e?[e]:[]);var a=["shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"];function i(t){t&&r.util.each(a,(function(e){t.hasOwnProperty(e)&&(t["text"+r.format.capitalFirst(e)]=t[e])}))}r.util.each(e,(function(t){if(t&&"wordCloud"===t.type){var e=t.textStyle||{};i(e.normal),i(e.emphasis)}}))}))},83:e=>{e.exports=t}},a={};function r(t){if(a[t])return a[t].exports;var i=a[t]={exports:{}};return e[t](i,i.exports,r),i.exports}return r.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r(638)})()})); | ||
//# sourceMappingURL=echarts-wordcloud.min.js.map |
{ | ||
"name": "echarts-wordcloud", | ||
"version": "2.0.0", | ||
"version": "2.1.0-beta", | ||
"description": "ECharts wordcloud extension based on wordcloud2.js", | ||
@@ -21,3 +21,4 @@ "main": "index.js", | ||
"devDependencies": { | ||
"echarts": "^5.0.1", | ||
"echarts": "^5.2.2", | ||
"prettier": "^2.5.1", | ||
"webpack": "^5.11.1", | ||
@@ -24,0 +25,0 @@ "webpack-cli": "^4.3.1" |
@@ -1,6 +0,7 @@ | ||
# [Apache ECharts (incubating)](https://github.com/apache/incubator-echarts) wordcloud extension based on [wordcloud2.js](https://github.com/timdream/wordcloud2.js) | ||
# echarts-wordcloud | ||
Third-party Wordcloud extension based on [wordcloud2.js](https://github.com/timdream/wordcloud2.js) for [Apache ECharts](https://github.com/apache/echarts). | ||
![](./example/word-cloud.png) | ||
## Examples | ||
@@ -27,3 +28,3 @@ | ||
```js | ||
import * as echarts from 'echarts' | ||
import * as echarts from 'echarts'; | ||
import 'echarts-wordcloud'; | ||
@@ -54,2 +55,6 @@ ``` | ||
// Keep aspect ratio of maskImage or 1:1 for shapes | ||
// This option is supported from echarts-wordcloud@2.1.0 | ||
keepAspect: false, | ||
// A silhouette image which the white area will be excluded from drawing texts. | ||
@@ -111,4 +116,4 @@ // The shape option will continue to apply as the shape of the cloud to grow. | ||
textStyle: { | ||
shadowBlur: 10, | ||
shadowColor: '#333' | ||
textShadowBlur: 10, | ||
textShadowColor: '#333' | ||
} | ||
@@ -115,0 +120,0 @@ }, |
2007
src/layout.js
@@ -5,3 +5,3 @@ /*! | ||
* | ||
* Copyright 2011 - 2013 Tim Chien | ||
* Copyright 2011 - 2019 Tim Guan-tin Chien and contributors. | ||
* Released under the MIT license | ||
@@ -15,60 +15,68 @@ */ | ||
window.setImmediate = (function setupSetImmediate() { | ||
return window.msSetImmediate || | ||
window.webkitSetImmediate || | ||
window.mozSetImmediate || | ||
window.oSetImmediate || | ||
(function setupSetZeroTimeout() { | ||
if (!window.postMessage || !window.addEventListener) { | ||
return null; | ||
} | ||
return ( | ||
window.msSetImmediate || | ||
window.webkitSetImmediate || | ||
window.mozSetImmediate || | ||
window.oSetImmediate || | ||
(function setupSetZeroTimeout() { | ||
if (!window.postMessage || !window.addEventListener) { | ||
return null; | ||
} | ||
var callbacks = [undefined]; | ||
var message = 'zero-timeout-message'; | ||
var callbacks = [undefined]; | ||
var message = 'zero-timeout-message'; | ||
// Like setTimeout, but only takes a function argument. There's | ||
// no time argument (always zero) and no arguments (you have to | ||
// use a closure). | ||
var setZeroTimeout = function setZeroTimeout(callback) { | ||
var id = callbacks.length; | ||
callbacks.push(callback); | ||
window.postMessage(message + id.toString(36), '*'); | ||
// Like setTimeout, but only takes a function argument. There's | ||
// no time argument (always zero) and no arguments (you have to | ||
// use a closure). | ||
var setZeroTimeout = function setZeroTimeout(callback) { | ||
var id = callbacks.length; | ||
callbacks.push(callback); | ||
window.postMessage(message + id.toString(36), '*'); | ||
return id; | ||
}; | ||
return id; | ||
}; | ||
window.addEventListener('message', function setZeroTimeoutMessage(evt) { | ||
// Skipping checking event source, retarded IE confused this window | ||
// object with another in the presence of iframe | ||
if (typeof evt.data !== 'string' || | ||
evt.data.substr(0, message.length) !== message/* || | ||
evt.source !== window */) { | ||
return; | ||
} | ||
window.addEventListener( | ||
'message', | ||
function setZeroTimeoutMessage(evt) { | ||
// Skipping checking event source, retarded IE confused this window | ||
// object with another in the presence of iframe | ||
if ( | ||
typeof evt.data !== 'string' || | ||
evt.data.substr(0, message.length) !== message /* || | ||
evt.source !== window */ | ||
) { | ||
return; | ||
} | ||
evt.stopImmediatePropagation(); | ||
evt.stopImmediatePropagation(); | ||
var id = parseInt(evt.data.substr(message.length), 36); | ||
if (!callbacks[id]) { | ||
return; | ||
} | ||
var id = parseInt(evt.data.substr(message.length), 36); | ||
if (!callbacks[id]) { | ||
return; | ||
} | ||
callbacks[id](); | ||
callbacks[id] = undefined; | ||
}, true); | ||
callbacks[id](); | ||
callbacks[id] = undefined; | ||
}, | ||
true | ||
); | ||
/* specify clearImmediate() here since we need the scope */ | ||
window.clearImmediate = function clearZeroTimeout(id) { | ||
if (!callbacks[id]) { | ||
return; | ||
} | ||
/* specify clearImmediate() here since we need the scope */ | ||
window.clearImmediate = function clearZeroTimeout(id) { | ||
if (!callbacks[id]) { | ||
return; | ||
} | ||
callbacks[id] = undefined; | ||
}; | ||
callbacks[id] = undefined; | ||
}; | ||
return setZeroTimeout; | ||
})() || | ||
// fallback | ||
function setImmediateFallback(fn) { | ||
window.setTimeout(fn, 0); | ||
}; | ||
return setZeroTimeout; | ||
})() || | ||
// fallback | ||
function setImmediateFallback(fn) { | ||
window.setTimeout(fn, 0); | ||
} | ||
); | ||
})(); | ||
@@ -79,174 +87,201 @@ } | ||
window.clearImmediate = (function setupClearImmediate() { | ||
return window.msClearImmediate || | ||
window.webkitClearImmediate || | ||
window.mozClearImmediate || | ||
window.oClearImmediate || | ||
// "clearZeroTimeout" is implement on the previous block || | ||
// fallback | ||
function clearImmediateFallback(timer) { | ||
window.clearTimeout(timer); | ||
}; | ||
return ( | ||
window.msClearImmediate || | ||
window.webkitClearImmediate || | ||
window.mozClearImmediate || | ||
window.oClearImmediate || | ||
// "clearZeroTimeout" is implement on the previous block || | ||
// fallback | ||
function clearImmediateFallback(timer) { | ||
window.clearTimeout(timer); | ||
} | ||
); | ||
})(); | ||
} | ||
// Check if WordCloud can run on this browser | ||
var isSupported = (function isSupported() { | ||
var canvas = document.createElement('canvas'); | ||
if (!canvas || !canvas.getContext) { | ||
return false; | ||
} | ||
// Check if WordCloud can run on this browser | ||
var isSupported = (function isSupported() { | ||
var canvas = document.createElement('canvas'); | ||
if (!canvas || !canvas.getContext) { | ||
return false; | ||
} | ||
var ctx = canvas.getContext('2d'); | ||
if (!ctx.getImageData) { | ||
return false; | ||
} | ||
if (!ctx.fillText) { | ||
return false; | ||
} | ||
var ctx = canvas.getContext('2d'); | ||
if (!ctx) { | ||
return false; | ||
} | ||
if (!ctx.getImageData) { | ||
return false; | ||
} | ||
if (!ctx.fillText) { | ||
return false; | ||
} | ||
if (!Array.prototype.some) { | ||
return false; | ||
} | ||
if (!Array.prototype.push) { | ||
return false; | ||
} | ||
if (!Array.prototype.some) { | ||
return false; | ||
} | ||
if (!Array.prototype.push) { | ||
return false; | ||
} | ||
return true; | ||
}()); | ||
return true; | ||
})(); | ||
// Find out if the browser impose minium font size by | ||
// drawing small texts on a canvas and measure it's width. | ||
var minFontSize = (function getMinFontSize() { | ||
if (!isSupported) { | ||
return; | ||
} | ||
// Find out if the browser impose minium font size by | ||
// drawing small texts on a canvas and measure it's width. | ||
var minFontSize = (function getMinFontSize() { | ||
if (!isSupported) { | ||
return; | ||
} | ||
var ctx = document.createElement('canvas').getContext('2d'); | ||
var ctx = document.createElement('canvas').getContext('2d'); | ||
// start from 20 | ||
var size = 20; | ||
// start from 20 | ||
var size = 20; | ||
// two sizes to measure | ||
var hanWidth, mWidth; | ||
// two sizes to measure | ||
var hanWidth, mWidth; | ||
while (size) { | ||
ctx.font = size.toString(10) + 'px sans-serif'; | ||
if ((ctx.measureText('\uFF37').width === hanWidth) && | ||
(ctx.measureText('m').width) === mWidth) { | ||
return (size + 1); | ||
} | ||
while (size) { | ||
ctx.font = size.toString(10) + 'px sans-serif'; | ||
if ( | ||
ctx.measureText('\uFF37').width === hanWidth && | ||
ctx.measureText('m').width === mWidth | ||
) { | ||
return size + 1; | ||
} | ||
hanWidth = ctx.measureText('\uFF37').width; | ||
mWidth = ctx.measureText('m').width; | ||
hanWidth = ctx.measureText('\uFF37').width; | ||
mWidth = ctx.measureText('m').width; | ||
size--; | ||
} | ||
size--; | ||
} | ||
return 0; | ||
})(); | ||
return 0; | ||
})(); | ||
// Based on http://jsfromhell.com/array/shuffle | ||
var shuffleArray = function shuffleArray(arr) { | ||
for (var j, x, i = arr.length; i; | ||
j = Math.floor(Math.random() * i), | ||
x = arr[--i], arr[i] = arr[j], | ||
arr[j] = x) {} | ||
return arr; | ||
}; | ||
var getItemExtraData = function (item) { | ||
if (Array.isArray(item)) { | ||
var itemCopy = item.slice(); | ||
// remove data we already have (word and weight) | ||
itemCopy.splice(0, 2); | ||
return itemCopy; | ||
} else { | ||
return []; | ||
} | ||
}; | ||
var WordCloud = function WordCloud(elements, options) { | ||
if (!isSupported) { | ||
return; | ||
} | ||
// Based on http://jsfromhell.com/array/shuffle | ||
var shuffleArray = function shuffleArray(arr) { | ||
for (var j, x, i = arr.length; i; ) { | ||
j = Math.floor(Math.random() * i); | ||
x = arr[--i]; | ||
arr[i] = arr[j]; | ||
arr[j] = x; | ||
} | ||
return arr; | ||
}; | ||
if (!Array.isArray(elements)) { | ||
elements = [elements]; | ||
} | ||
var timer = {}; | ||
var WordCloud = function WordCloud(elements, options) { | ||
if (!isSupported) { | ||
return; | ||
} | ||
elements.forEach(function(el, i) { | ||
if (typeof el === 'string') { | ||
elements[i] = document.getElementById(el); | ||
if (!elements[i]) { | ||
throw 'The element id specified is not found.'; | ||
} | ||
} else if (!el.tagName && !el.appendChild) { | ||
throw 'You must pass valid HTML elements, or ID of the element.'; | ||
var timerId = Math.floor(Math.random() * Date.now()); | ||
if (!Array.isArray(elements)) { | ||
elements = [elements]; | ||
} | ||
elements.forEach(function (el, i) { | ||
if (typeof el === 'string') { | ||
elements[i] = document.getElementById(el); | ||
if (!elements[i]) { | ||
throw new Error('The element id specified is not found.'); | ||
} | ||
}); | ||
} else if (!el.tagName && !el.appendChild) { | ||
throw new Error( | ||
'You must pass valid HTML elements, or ID of the element.' | ||
); | ||
} | ||
}); | ||
/* Default values to be overwritten by options object */ | ||
var settings = { | ||
list: [], | ||
fontFamily: '"Trebuchet MS", "Heiti TC", "微軟正黑體", ' + | ||
'"Arial Unicode MS", "Droid Fallback Sans", sans-serif', | ||
fontWeight: 'normal', | ||
color: 'random-dark', | ||
minSize: 0, // 0 to disable | ||
weightFactor: 1, | ||
clearCanvas: true, | ||
backgroundColor: '#fff', // opaque white = rgba(255, 255, 255, 1) | ||
/* Default values to be overwritten by options object */ | ||
var settings = { | ||
list: [], | ||
fontFamily: | ||
'"Trebuchet MS", "Heiti TC", "微軟正黑體", ' + | ||
'"Arial Unicode MS", "Droid Fallback Sans", sans-serif', | ||
fontWeight: 'normal', | ||
color: 'random-dark', | ||
minSize: 0, // 0 to disable | ||
weightFactor: 1, | ||
clearCanvas: true, | ||
backgroundColor: '#fff', // opaque white = rgba(255, 255, 255, 1) | ||
gridSize: 8, | ||
drawOutOfBound: false, | ||
origin: null, | ||
gridSize: 8, | ||
drawOutOfBound: false, | ||
shrinkToFit: false, | ||
origin: null, | ||
drawMask: false, | ||
maskColor: 'rgba(255,0,0,0.3)', | ||
maskGapWidth: 0.3, | ||
drawMask: false, | ||
maskColor: 'rgba(255,0,0,0.3)', | ||
maskGapWidth: 0.3, | ||
layoutAnimation: true, | ||
layoutAnimation: true, | ||
wait: 0, | ||
abortThreshold: 0, // disabled | ||
abort: function noop() {}, | ||
wait: 0, | ||
abortThreshold: 0, // disabled | ||
abort: function noop() {}, | ||
minRotation: - Math.PI / 2, | ||
maxRotation: Math.PI / 2, | ||
rotationStep: 0.1, | ||
minRotation: -Math.PI / 2, | ||
maxRotation: Math.PI / 2, | ||
rotationStep: 0.1, | ||
shuffle: true, | ||
rotateRatio: 0.1, | ||
shuffle: true, | ||
rotateRatio: 0.1, | ||
shape: 'circle', | ||
ellipticity: 0.65, | ||
shape: 'circle', | ||
ellipticity: 0.65, | ||
classes: null, | ||
classes: null, | ||
hover: null, | ||
click: null | ||
}; | ||
hover: null, | ||
click: null | ||
}; | ||
if (options) { | ||
for (var key in options) { | ||
if (key in settings) { | ||
settings[key] = options[key]; | ||
} | ||
if (options) { | ||
for (var key in options) { | ||
if (key in settings) { | ||
settings[key] = options[key]; | ||
} | ||
} | ||
} | ||
/* Convert weightFactor into a function */ | ||
if (typeof settings.weightFactor !== 'function') { | ||
var factor = settings.weightFactor; | ||
settings.weightFactor = function weightFactor(pt) { | ||
return pt * factor; //in px | ||
}; | ||
} | ||
/* Convert weightFactor into a function */ | ||
if (typeof settings.weightFactor !== 'function') { | ||
var factor = settings.weightFactor; | ||
settings.weightFactor = function weightFactor(pt) { | ||
return pt * factor; // in px | ||
}; | ||
} | ||
/* Convert shape into a function */ | ||
if (typeof settings.shape !== 'function') { | ||
switch (settings.shape) { | ||
case 'circle': | ||
/* falls through */ | ||
default: | ||
// 'circle' is the default and a shortcut in the code loop. | ||
settings.shape = 'circle'; | ||
break; | ||
/* Convert shape into a function */ | ||
if (typeof settings.shape !== 'function') { | ||
switch (settings.shape) { | ||
case 'circle': | ||
/* falls through */ | ||
default: | ||
// 'circle' is the default and a shortcut in the code loop. | ||
settings.shape = 'circle'; | ||
break; | ||
case 'cardioid': | ||
settings.shape = function shapeCardioid(theta) { | ||
return 1 - Math.sin(theta); | ||
}; | ||
break; | ||
case 'cardioid': | ||
settings.shape = function shapeCardioid(theta) { | ||
return 1 - Math.sin(theta); | ||
}; | ||
break; | ||
/* | ||
/* | ||
To work out an X-gon, one has to calculate "m", | ||
@@ -260,888 +295,1002 @@ where 1/(cos(2*PI/X)+m*sin(2*PI/X)) = 1/(cos(0)+m*sin(0)) | ||
case 'diamond': | ||
case 'square': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+ | ||
// %28t%2C+PI%2F2%29%29%2Bsin%28mod+%28t%2C+PI%2F2%29%29%29%2C+t+%3D | ||
// +0+..+2*PI | ||
settings.shape = function shapeSquare(theta) { | ||
var thetaPrime = theta % (2 * Math.PI / 4); | ||
return 1 / (Math.cos(thetaPrime) + Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'diamond': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+ | ||
// %28t%2C+PI%2F2%29%29%2Bsin%28mod+%28t%2C+PI%2F2%29%29%29%2C+t+%3D | ||
// +0+..+2*PI | ||
settings.shape = function shapeSquare(theta) { | ||
var thetaPrime = theta % ((2 * Math.PI) / 4); | ||
return 1 / (Math.cos(thetaPrime) + Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'triangle-forward': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+ | ||
// %28t%2C+2*PI%2F3%29%29%2Bsqrt%283%29sin%28mod+%28t%2C+2*PI%2F3%29 | ||
// %29%29%2C+t+%3D+0+..+2*PI | ||
settings.shape = function shapeTriangle(theta) { | ||
var thetaPrime = theta % (2 * Math.PI / 3); | ||
return 1 / (Math.cos(thetaPrime) + | ||
Math.sqrt(3) * Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'square': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+min(1%2Fabs(cos(t | ||
// )),1%2Fabs(sin(t)))),+t+%3D+0+..+2*PI | ||
settings.shape = function shapeSquare(theta) { | ||
return Math.min( | ||
1 / Math.abs(Math.cos(theta)), | ||
1 / Math.abs(Math.sin(theta)) | ||
); | ||
}; | ||
break; | ||
case 'triangle': | ||
case 'triangle-upright': | ||
settings.shape = function shapeTriangle(theta) { | ||
var thetaPrime = (theta + Math.PI * 3 / 2) % (2 * Math.PI / 3); | ||
return 1 / (Math.cos(thetaPrime) + | ||
Math.sqrt(3) * Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'triangle-forward': | ||
// http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+ | ||
// %28t%2C+2*PI%2F3%29%29%2Bsqrt%283%29sin%28mod+%28t%2C+2*PI%2F3%29 | ||
// %29%29%2C+t+%3D+0+..+2*PI | ||
settings.shape = function shapeTriangle(theta) { | ||
var thetaPrime = theta % ((2 * Math.PI) / 3); | ||
return ( | ||
1 / (Math.cos(thetaPrime) + Math.sqrt(3) * Math.sin(thetaPrime)) | ||
); | ||
}; | ||
break; | ||
case 'pentagon': | ||
settings.shape = function shapePentagon(theta) { | ||
var thetaPrime = (theta + 0.955) % (2 * Math.PI / 5); | ||
return 1 / (Math.cos(thetaPrime) + | ||
0.726543 * Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'triangle': | ||
case 'triangle-upright': | ||
settings.shape = function shapeTriangle(theta) { | ||
var thetaPrime = (theta + (Math.PI * 3) / 2) % ((2 * Math.PI) / 3); | ||
return ( | ||
1 / (Math.cos(thetaPrime) + Math.sqrt(3) * Math.sin(thetaPrime)) | ||
); | ||
}; | ||
break; | ||
case 'star': | ||
settings.shape = function shapeStar(theta) { | ||
var thetaPrime = (theta + 0.955) % (2 * Math.PI / 10); | ||
if ((theta + 0.955) % (2 * Math.PI / 5) - (2 * Math.PI / 10) >= 0) { | ||
return 1 / (Math.cos((2 * Math.PI / 10) - thetaPrime) + | ||
3.07768 * Math.sin((2 * Math.PI / 10) - thetaPrime)); | ||
} else { | ||
return 1 / (Math.cos(thetaPrime) + | ||
3.07768 * Math.sin(thetaPrime)); | ||
} | ||
}; | ||
break; | ||
} | ||
case 'pentagon': | ||
settings.shape = function shapePentagon(theta) { | ||
var thetaPrime = (theta + 0.955) % ((2 * Math.PI) / 5); | ||
return 1 / (Math.cos(thetaPrime) + 0.726543 * Math.sin(thetaPrime)); | ||
}; | ||
break; | ||
case 'star': | ||
settings.shape = function shapeStar(theta) { | ||
var thetaPrime = (theta + 0.955) % ((2 * Math.PI) / 10); | ||
if ( | ||
((theta + 0.955) % ((2 * Math.PI) / 5)) - (2 * Math.PI) / 10 >= | ||
0 | ||
) { | ||
return ( | ||
1 / | ||
(Math.cos((2 * Math.PI) / 10 - thetaPrime) + | ||
3.07768 * Math.sin((2 * Math.PI) / 10 - thetaPrime)) | ||
); | ||
} else { | ||
return 1 / (Math.cos(thetaPrime) + 3.07768 * Math.sin(thetaPrime)); | ||
} | ||
}; | ||
break; | ||
} | ||
} | ||
/* Make sure gridSize is a whole number and is not smaller than 4px */ | ||
settings.gridSize = Math.max(Math.floor(settings.gridSize), 4); | ||
/* Make sure gridSize is a whole number and is not smaller than 4px */ | ||
settings.gridSize = Math.max(Math.floor(settings.gridSize), 4); | ||
/* shorthand */ | ||
var g = settings.gridSize; | ||
var maskRectWidth = g - settings.maskGapWidth; | ||
/* shorthand */ | ||
var g = settings.gridSize; | ||
var maskRectWidth = g - settings.maskGapWidth; | ||
/* normalize rotation settings */ | ||
var rotationRange = Math.abs(settings.maxRotation - settings.minRotation); | ||
var minRotation = Math.min(settings.maxRotation, settings.minRotation); | ||
var rotationStep = settings.rotationStep; | ||
/* normalize rotation settings */ | ||
var rotationRange = Math.abs(settings.maxRotation - settings.minRotation); | ||
var minRotation = Math.min(settings.maxRotation, settings.minRotation); | ||
var rotationStep = settings.rotationStep; | ||
/* information/object available to all functions, set when start() */ | ||
var grid, // 2d array containing filling information | ||
ngx, ngy, // width and height of the grid | ||
center, // position of the center of the cloud | ||
maxRadius; | ||
/* information/object available to all functions, set when start() */ | ||
var grid, // 2d array containing filling information | ||
ngx, | ||
ngy, // width and height of the grid | ||
center, // position of the center of the cloud | ||
maxRadius; | ||
/* timestamp for measuring each putWord() action */ | ||
var escapeTime; | ||
/* timestamp for measuring each putWord() action */ | ||
var escapeTime; | ||
/* function for getting the color of the text */ | ||
var getTextColor; | ||
function random_hsl_color(min, max) { | ||
return 'hsl(' + | ||
(Math.random() * 360).toFixed() + ',' + | ||
(Math.random() * 30 + 70).toFixed() + '%,' + | ||
(Math.random() * (max - min) + min).toFixed() + '%)'; | ||
} | ||
switch (settings.color) { | ||
case 'random-dark': | ||
getTextColor = function getRandomDarkColor() { | ||
return random_hsl_color(10, 50); | ||
}; | ||
break; | ||
/* function for getting the color of the text */ | ||
var getTextColor; | ||
function randomHslColor(min, max) { | ||
return ( | ||
'hsl(' + | ||
(Math.random() * 360).toFixed() + | ||
',' + | ||
(Math.random() * 30 + 70).toFixed() + | ||
'%,' + | ||
(Math.random() * (max - min) + min).toFixed() + | ||
'%)' | ||
); | ||
} | ||
switch (settings.color) { | ||
case 'random-dark': | ||
getTextColor = function getRandomDarkColor() { | ||
return randomHslColor(10, 50); | ||
}; | ||
break; | ||
case 'random-light': | ||
getTextColor = function getRandomLightColor() { | ||
return random_hsl_color(50, 90); | ||
}; | ||
break; | ||
case 'random-light': | ||
getTextColor = function getRandomLightColor() { | ||
return randomHslColor(50, 90); | ||
}; | ||
break; | ||
default: | ||
if (typeof settings.color === 'function') { | ||
getTextColor = settings.color; | ||
} | ||
break; | ||
} | ||
default: | ||
if (typeof settings.color === 'function') { | ||
getTextColor = settings.color; | ||
} | ||
break; | ||
} | ||
/* function for getting the classes of the text */ | ||
var getTextClasses = null; | ||
if (typeof settings.classes === 'function') { | ||
getTextClasses = settings.classes; | ||
} | ||
/* function for getting the font-weight of the text */ | ||
var getTextFontWeight; | ||
if (typeof settings.fontWeight === 'function') { | ||
getTextFontWeight = settings.fontWeight; | ||
} | ||
/* Interactive */ | ||
var interactive = false; | ||
var infoGrid = []; | ||
var hovered; | ||
/* function for getting the classes of the text */ | ||
var getTextClasses = null; | ||
if (typeof settings.classes === 'function') { | ||
getTextClasses = settings.classes; | ||
} | ||
var getInfoGridFromMouseTouchEvent = | ||
function getInfoGridFromMouseTouchEvent(evt) { | ||
var canvas = evt.currentTarget; | ||
var rect = canvas.getBoundingClientRect(); | ||
var clientX; | ||
var clientY; | ||
/** Detect if touches are available */ | ||
if (evt.touches) { | ||
clientX = evt.touches[0].clientX; | ||
clientY = evt.touches[0].clientY; | ||
} else { | ||
clientX = evt.clientX; | ||
clientY = evt.clientY; | ||
} | ||
var eventX = clientX - rect.left; | ||
var eventY = clientY - rect.top; | ||
/* Interactive */ | ||
var interactive = false; | ||
var infoGrid = []; | ||
var hovered; | ||
var x = Math.floor(eventX * ((canvas.width / rect.width) || 1) / g); | ||
var y = Math.floor(eventY * ((canvas.height / rect.height) || 1) / g); | ||
var getInfoGridFromMouseTouchEvent = function getInfoGridFromMouseTouchEvent( | ||
evt | ||
) { | ||
var canvas = evt.currentTarget; | ||
var rect = canvas.getBoundingClientRect(); | ||
var clientX; | ||
var clientY; | ||
/** Detect if touches are available */ | ||
if (evt.touches) { | ||
clientX = evt.touches[0].clientX; | ||
clientY = evt.touches[0].clientY; | ||
} else { | ||
clientX = evt.clientX; | ||
clientY = evt.clientY; | ||
} | ||
var eventX = clientX - rect.left; | ||
var eventY = clientY - rect.top; | ||
return infoGrid[x][y]; | ||
}; | ||
var x = Math.floor((eventX * (canvas.width / rect.width || 1)) / g); | ||
var y = Math.floor((eventY * (canvas.height / rect.height || 1)) / g); | ||
var wordcloudhover = function wordcloudhover(evt) { | ||
var info = getInfoGridFromMouseTouchEvent(evt); | ||
return infoGrid[x][y]; | ||
}; | ||
if (hovered === info) { | ||
return; | ||
} | ||
var wordcloudhover = function wordcloudhover(evt) { | ||
var info = getInfoGridFromMouseTouchEvent(evt); | ||
hovered = info; | ||
if (!info) { | ||
settings.hover(undefined, undefined, evt); | ||
if (hovered === info) { | ||
return; | ||
} | ||
return; | ||
} | ||
hovered = info; | ||
if (!info) { | ||
settings.hover(undefined, undefined, evt); | ||
settings.hover(info.item, info.dimension, evt); | ||
return; | ||
} | ||
}; | ||
settings.hover(info.item, info.dimension, evt); | ||
}; | ||
var wordcloudclick = function wordcloudclick(evt) { | ||
var info = getInfoGridFromMouseTouchEvent(evt); | ||
if (!info) { | ||
return; | ||
} | ||
var wordcloudclick = function wordcloudclick(evt) { | ||
var info = getInfoGridFromMouseTouchEvent(evt); | ||
if (!info) { | ||
return; | ||
} | ||
settings.click(info.item, info.dimension, evt); | ||
evt.preventDefault(); | ||
}; | ||
settings.click(info.item, info.dimension, evt); | ||
evt.preventDefault(); | ||
}; | ||
/* Get points on the grid for a given radius away from the center */ | ||
var pointsAtRadius = []; | ||
var getPointsAtRadius = function getPointsAtRadius(radius) { | ||
if (pointsAtRadius[radius]) { | ||
return pointsAtRadius[radius]; | ||
} | ||
/* Get points on the grid for a given radius away from the center */ | ||
var pointsAtRadius = []; | ||
var getPointsAtRadius = function getPointsAtRadius(radius) { | ||
if (pointsAtRadius[radius]) { | ||
return pointsAtRadius[radius]; | ||
} | ||
// Look for these number of points on each radius | ||
var T = radius * 8; | ||
// Look for these number of points on each radius | ||
var T = radius * 8; | ||
// Getting all the points at this radius | ||
var t = T; | ||
var points = []; | ||
// Getting all the points at this radius | ||
var t = T; | ||
var points = []; | ||
if (radius === 0) { | ||
points.push([center[0], center[1], 0]); | ||
if (radius === 0) { | ||
points.push([center[0], center[1], 0]); | ||
} | ||
while (t--) { | ||
// distort the radius to put the cloud in shape | ||
var rx = 1; | ||
if (settings.shape !== 'circle') { | ||
rx = settings.shape((t / T) * 2 * Math.PI); // 0 to 1 | ||
} | ||
while (t--) { | ||
// distort the radius to put the cloud in shape | ||
var rx = 1; | ||
if (settings.shape !== 'circle') { | ||
rx = settings.shape(t / T * 2 * Math.PI); // 0 to 1 | ||
} | ||
// Push [x, y, t]; t is used solely for getTextColor() | ||
points.push([ | ||
center[0] + radius * rx * Math.cos((-t / T) * 2 * Math.PI), | ||
center[1] + | ||
radius * rx * Math.sin((-t / T) * 2 * Math.PI) * settings.ellipticity, | ||
(t / T) * 2 * Math.PI | ||
]); | ||
} | ||
// Push [x, y, t]; t is used solely for getTextColor() | ||
points.push([ | ||
center[0] + radius * rx * Math.cos(-t / T * 2 * Math.PI), | ||
center[1] + radius * rx * Math.sin(-t / T * 2 * Math.PI) * | ||
settings.ellipticity, | ||
t / T * 2 * Math.PI]); | ||
} | ||
pointsAtRadius[radius] = points; | ||
return points; | ||
}; | ||
pointsAtRadius[radius] = points; | ||
return points; | ||
}; | ||
/* Return true if we had spent too much time */ | ||
var exceedTime = function exceedTime() { | ||
return ( | ||
settings.abortThreshold > 0 && | ||
new Date().getTime() - escapeTime > settings.abortThreshold | ||
); | ||
}; | ||
/* Return true if we had spent too much time */ | ||
var exceedTime = function exceedTime() { | ||
return ((settings.abortThreshold > 0) && | ||
((new Date()).getTime() - escapeTime > settings.abortThreshold)); | ||
}; | ||
/* Get the deg of rotation according to settings, and luck. */ | ||
var getRotateDeg = function getRotateDeg() { | ||
if (settings.rotateRatio === 0) { | ||
return 0; | ||
} | ||
/* Get the deg of rotation according to settings, and luck. */ | ||
var getRotateDeg = function getRotateDeg() { | ||
if (settings.rotateRatio === 0) { | ||
return 0; | ||
} | ||
if (Math.random() > settings.rotateRatio) { | ||
return 0; | ||
} | ||
if (Math.random() > settings.rotateRatio) { | ||
return 0; | ||
} | ||
if (rotationRange === 0) { | ||
return minRotation; | ||
} | ||
if (rotationRange === 0) { | ||
return minRotation; | ||
} | ||
return minRotation + Math.round(Math.random() * rotationRange / rotationStep) * rotationStep; | ||
}; | ||
return minRotation + Math.round(Math.random() * rotationRange / rotationStep) * rotationStep; | ||
}; | ||
var getTextInfo = function getTextInfo( | ||
word, | ||
weight, | ||
rotateDeg, | ||
extraDataArray | ||
) { | ||
// calculate the acutal font size | ||
// fontSize === 0 means weightFactor function wants the text skipped, | ||
// and size < minSize means we cannot draw the text. | ||
var debug = false; | ||
var fontSize = settings.weightFactor(weight); | ||
if (fontSize <= settings.minSize) { | ||
return false; | ||
} | ||
var getTextInfo = function getTextInfo(word, weight, rotateDeg) { | ||
// calculate the acutal font size | ||
// fontSize === 0 means weightFactor function wants the text skipped, | ||
// and size < minSize means we cannot draw the text. | ||
var debug = false; | ||
var fontSize = settings.weightFactor(weight); | ||
if (fontSize <= settings.minSize) { | ||
return false; | ||
} | ||
// Scale factor here is to make sure fillText is not limited by | ||
// the minium font size set by browser. | ||
// It will always be 1 or 2n. | ||
var mu = 1; | ||
if (fontSize < minFontSize) { | ||
mu = (function calculateScaleFactor() { | ||
var mu = 2; | ||
while (mu * fontSize < minFontSize) { | ||
mu += 2; | ||
} | ||
return mu; | ||
})(); | ||
} | ||
// Scale factor here is to make sure fillText is not limited by | ||
// the minium font size set by browser. | ||
// It will always be 1 or 2n. | ||
var mu = 1; | ||
if (fontSize < minFontSize) { | ||
mu = (function calculateScaleFactor() { | ||
var mu = 2; | ||
while (mu * fontSize < minFontSize) { | ||
mu += 2; | ||
} | ||
return mu; | ||
})(); | ||
} | ||
// Get fontWeight that will be used to set fctx.font | ||
var fontWeight; | ||
if (getTextFontWeight) { | ||
fontWeight = getTextFontWeight(word, weight, fontSize, extraDataArray); | ||
} else { | ||
fontWeight = settings.fontWeight; | ||
} | ||
var fcanvas = document.createElement('canvas'); | ||
var fctx = fcanvas.getContext('2d', { willReadFrequently: true }); | ||
var fcanvas = document.createElement('canvas'); | ||
var fctx = fcanvas.getContext('2d', { willReadFrequently: true }); | ||
fctx.font = settings.fontWeight + ' ' + | ||
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily; | ||
fctx.font = | ||
fontWeight + | ||
' ' + | ||
(fontSize * mu).toString(10) + | ||
'px ' + | ||
settings.fontFamily; | ||
// Estimate the dimension of the text with measureText(). | ||
var fw = fctx.measureText(word).width / mu; | ||
var fh = Math.max(fontSize * mu, | ||
fctx.measureText('m').width, | ||
fctx.measureText('\uFF37').width) / mu; | ||
// Estimate the dimension of the text with measureText(). | ||
var fw = fctx.measureText(word).width / mu; | ||
var fh = | ||
Math.max( | ||
fontSize * mu, | ||
fctx.measureText('m').width, | ||
fctx.measureText('\uFF37').width | ||
) / mu; | ||
// Create a boundary box that is larger than our estimates, | ||
// so text don't get cut of (it sill might) | ||
var boxWidth = fw + fh * 2; | ||
var boxHeight = fh * 3; | ||
var fgw = Math.ceil(boxWidth / g); | ||
var fgh = Math.ceil(boxHeight / g); | ||
boxWidth = fgw * g; | ||
boxHeight = fgh * g; | ||
// Create a boundary box that is larger than our estimates, | ||
// so text don't get cut of (it sill might) | ||
var boxWidth = fw + fh * 2; | ||
var boxHeight = fh * 3; | ||
var fgw = Math.ceil(boxWidth / g); | ||
var fgh = Math.ceil(boxHeight / g); | ||
boxWidth = fgw * g; | ||
boxHeight = fgh * g; | ||
// Calculate the proper offsets to make the text centered at | ||
// the preferred position. | ||
// Calculate the proper offsets to make the text centered at | ||
// the preferred position. | ||
// This is simply half of the width. | ||
var fillTextOffsetX = - fw / 2; | ||
// Instead of moving the box to the exact middle of the preferred | ||
// position, for Y-offset we move 0.4 instead, so Latin alphabets look | ||
// vertical centered. | ||
var fillTextOffsetY = - fh * 0.4; | ||
// This is simply half of the width. | ||
var fillTextOffsetX = -fw / 2; | ||
// Instead of moving the box to the exact middle of the preferred | ||
// position, for Y-offset we move 0.4 instead, so Latin alphabets look | ||
// vertical centered. | ||
var fillTextOffsetY = -fh * 0.4; | ||
// Calculate the actual dimension of the canvas, considering the rotation. | ||
var cgh = Math.ceil((boxWidth * Math.abs(Math.sin(rotateDeg)) + | ||
boxHeight * Math.abs(Math.cos(rotateDeg))) / g); | ||
var cgw = Math.ceil((boxWidth * Math.abs(Math.cos(rotateDeg)) + | ||
boxHeight * Math.abs(Math.sin(rotateDeg))) / g); | ||
var width = cgw * g; | ||
var height = cgh * g; | ||
// Calculate the actual dimension of the canvas, considering the rotation. | ||
var cgh = Math.ceil( | ||
(boxWidth * Math.abs(Math.sin(rotateDeg)) + | ||
boxHeight * Math.abs(Math.cos(rotateDeg))) / | ||
g | ||
); | ||
var cgw = Math.ceil( | ||
(boxWidth * Math.abs(Math.cos(rotateDeg)) + | ||
boxHeight * Math.abs(Math.sin(rotateDeg))) / | ||
g | ||
); | ||
var width = cgw * g; | ||
var height = cgh * g; | ||
fcanvas.setAttribute('width', width); | ||
fcanvas.setAttribute('height', height); | ||
fcanvas.setAttribute('width', width); | ||
fcanvas.setAttribute('height', height); | ||
if (debug) { | ||
// Attach fcanvas to the DOM | ||
document.body.appendChild(fcanvas); | ||
// Save it's state so that we could restore and draw the grid correctly. | ||
fctx.save(); | ||
} | ||
if (debug) { | ||
// Attach fcanvas to the DOM | ||
document.body.appendChild(fcanvas); | ||
// Save it's state so that we could restore and draw the grid correctly. | ||
fctx.save(); | ||
} | ||
// Scale the canvas with |mu|. | ||
fctx.scale(1 / mu, 1 / mu); | ||
fctx.translate(width * mu / 2, height * mu / 2); | ||
fctx.rotate(- rotateDeg); | ||
// Scale the canvas with |mu|. | ||
fctx.scale(1 / mu, 1 / mu); | ||
fctx.translate((width * mu) / 2, (height * mu) / 2); | ||
fctx.rotate(-rotateDeg); | ||
// Once the width/height is set, ctx info will be reset. | ||
// Set it again here. | ||
fctx.font = settings.fontWeight + ' ' + | ||
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily; | ||
// Once the width/height is set, ctx info will be reset. | ||
// Set it again here. | ||
fctx.font = | ||
fontWeight + | ||
' ' + | ||
(fontSize * mu).toString(10) + | ||
'px ' + | ||
settings.fontFamily; | ||
// Fill the text into the fcanvas. | ||
// XXX: We cannot because textBaseline = 'top' here because | ||
// Firefox and Chrome uses different default line-height for canvas. | ||
// Please read https://bugzil.la/737852#c6. | ||
// Here, we use textBaseline = 'middle' and draw the text at exactly | ||
// 0.5 * fontSize lower. | ||
fctx.fillStyle = '#000'; | ||
fctx.textBaseline = 'middle'; | ||
fctx.fillText(word, fillTextOffsetX * mu, | ||
(fillTextOffsetY + fontSize * 0.5) * mu); | ||
// Fill the text into the fcanvas. | ||
// XXX: We cannot because textBaseline = 'top' here because | ||
// Firefox and Chrome uses different default line-height for canvas. | ||
// Please read https://bugzil.la/737852#c6. | ||
// Here, we use textBaseline = 'middle' and draw the text at exactly | ||
// 0.5 * fontSize lower. | ||
fctx.fillStyle = '#000'; | ||
fctx.textBaseline = 'middle'; | ||
fctx.fillText( | ||
word, | ||
fillTextOffsetX * mu, | ||
(fillTextOffsetY + fontSize * 0.5) * mu | ||
); | ||
// Get the pixels of the text | ||
var imageData = fctx.getImageData(0, 0, width, height).data; | ||
// Get the pixels of the text | ||
var imageData = fctx.getImageData(0, 0, width, height).data; | ||
if (exceedTime()) { | ||
return false; | ||
} | ||
if (exceedTime()) { | ||
return false; | ||
} | ||
if (debug) { | ||
// Draw the box of the original estimation | ||
fctx.strokeRect(fillTextOffsetX * mu, | ||
fillTextOffsetY, fw * mu, fh * mu); | ||
fctx.restore(); | ||
} | ||
if (debug) { | ||
// Draw the box of the original estimation | ||
fctx.strokeRect(fillTextOffsetX * mu, fillTextOffsetY, fw * mu, fh * mu); | ||
fctx.restore(); | ||
} | ||
// Read the pixels and save the information to the occupied array | ||
var occupied = []; | ||
var gx = cgw, gy, x, y; | ||
var bounds = [cgh / 2, cgw / 2, cgh / 2, cgw / 2]; | ||
while (gx--) { | ||
gy = cgh; | ||
while (gy--) { | ||
y = g; | ||
singleGridLoop: { | ||
while (y--) { | ||
x = g; | ||
while (x--) { | ||
if (imageData[((gy * g + y) * width + | ||
(gx * g + x)) * 4 + 3]) { | ||
occupied.push([gx, gy]); | ||
// Read the pixels and save the information to the occupied array | ||
var occupied = []; | ||
var gx = cgw; | ||
var gy, x, y; | ||
var bounds = [cgh / 2, cgw / 2, cgh / 2, cgw / 2]; | ||
while (gx--) { | ||
gy = cgh; | ||
while (gy--) { | ||
y = g; | ||
/* eslint no-labels: ['error', { 'allowLoop': true }] */ | ||
singleGridLoop: while (y--) { | ||
x = g; | ||
while (x--) { | ||
if (imageData[((gy * g + y) * width + (gx * g + x)) * 4 + 3]) { | ||
occupied.push([gx, gy]); | ||
if (gx < bounds[3]) { | ||
bounds[3] = gx; | ||
} | ||
if (gx > bounds[1]) { | ||
bounds[1] = gx; | ||
} | ||
if (gy < bounds[0]) { | ||
bounds[0] = gy; | ||
} | ||
if (gy > bounds[2]) { | ||
bounds[2] = gy; | ||
} | ||
if (gx < bounds[3]) { | ||
bounds[3] = gx; | ||
} | ||
if (gx > bounds[1]) { | ||
bounds[1] = gx; | ||
} | ||
if (gy < bounds[0]) { | ||
bounds[0] = gy; | ||
} | ||
if (gy > bounds[2]) { | ||
bounds[2] = gy; | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; | ||
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5); | ||
} | ||
break singleGridLoop; | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; | ||
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5); | ||
} | ||
break singleGridLoop; | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(0, 0, 255, 0.5)'; | ||
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5); | ||
} | ||
} | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(0, 0, 255, 0.5)'; | ||
fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5); | ||
} | ||
} | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; | ||
fctx.fillRect(bounds[3] * g, | ||
bounds[0] * g, | ||
(bounds[1] - bounds[3] + 1) * g, | ||
(bounds[2] - bounds[0] + 1) * g); | ||
} | ||
if (debug) { | ||
fctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; | ||
fctx.fillRect( | ||
bounds[3] * g, | ||
bounds[0] * g, | ||
(bounds[1] - bounds[3] + 1) * g, | ||
(bounds[2] - bounds[0] + 1) * g | ||
); | ||
} | ||
// Return information needed to create the text on the real canvas | ||
return { | ||
mu: mu, | ||
occupied: occupied, | ||
bounds: bounds, | ||
gw: cgw, | ||
gh: cgh, | ||
fillTextOffsetX: fillTextOffsetX, | ||
fillTextOffsetY: fillTextOffsetY, | ||
fillTextWidth: fw, | ||
fillTextHeight: fh, | ||
fontSize: fontSize | ||
}; | ||
// Return information needed to create the text on the real canvas | ||
return { | ||
mu: mu, | ||
occupied: occupied, | ||
bounds: bounds, | ||
gw: cgw, | ||
gh: cgh, | ||
fillTextOffsetX: fillTextOffsetX, | ||
fillTextOffsetY: fillTextOffsetY, | ||
fillTextWidth: fw, | ||
fillTextHeight: fh, | ||
fontSize: fontSize | ||
}; | ||
}; | ||
/* Determine if there is room available in the given dimension */ | ||
var canFitText = function canFitText(gx, gy, gw, gh, occupied) { | ||
// Go through the occupied points, | ||
// return false if the space is not available. | ||
var i = occupied.length; | ||
while (i--) { | ||
var px = gx + occupied[i][0]; | ||
var py = gy + occupied[i][1]; | ||
/* Determine if there is room available in the given dimension */ | ||
var canFitText = function canFitText(gx, gy, gw, gh, occupied) { | ||
// Go through the occupied points, | ||
// return false if the space is not available. | ||
var i = occupied.length; | ||
while (i--) { | ||
var px = gx + occupied[i][0]; | ||
var py = gy + occupied[i][1]; | ||
if (px >= ngx || py >= ngy || px < 0 || py < 0) { | ||
if (!settings.drawOutOfBound) { | ||
return false; | ||
} | ||
continue; | ||
} | ||
if (!grid[px][py]) { | ||
if (px >= ngx || py >= ngy || px < 0 || py < 0) { | ||
if (!settings.drawOutOfBound) { | ||
return false; | ||
} | ||
continue; | ||
} | ||
return true; | ||
}; | ||
/* Actually draw the text on the grid */ | ||
var drawText = function drawText(gx, gy, info, word, weight, | ||
distance, theta, rotateDeg, attributes) { | ||
var fontSize = info.fontSize; | ||
var color; | ||
if (getTextColor) { | ||
color = getTextColor(word, weight, fontSize, distance, theta); | ||
} else { | ||
color = settings.color; | ||
if (!grid[px][py]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
var classes; | ||
if (getTextClasses) { | ||
classes = getTextClasses(word, weight, fontSize, distance, theta); | ||
} else { | ||
classes = settings.classes; | ||
} | ||
/* Actually draw the text on the grid */ | ||
var drawText = function drawText( | ||
gx, | ||
gy, | ||
info, | ||
word, | ||
weight, | ||
distance, | ||
theta, | ||
rotateDeg, | ||
attributes, | ||
extraDataArray | ||
) { | ||
var fontSize = info.fontSize; | ||
var color; | ||
if (getTextColor) { | ||
color = getTextColor( | ||
word, | ||
weight, | ||
fontSize, | ||
distance, | ||
theta, | ||
extraDataArray | ||
); | ||
} else { | ||
color = settings.color; | ||
} | ||
var dimension; | ||
var bounds = info.bounds; | ||
dimension = { | ||
x: (gx + bounds[3]) * g, | ||
y: (gy + bounds[0]) * g, | ||
w: (bounds[1] - bounds[3] + 1) * g, | ||
h: (bounds[2] - bounds[0] + 1) * g | ||
}; | ||
// get fontWeight that will be used to set ctx.font and font style rule | ||
var fontWeight; | ||
if (getTextFontWeight) { | ||
fontWeight = getTextFontWeight(word, weight, fontSize, extraDataArray); | ||
} else { | ||
fontWeight = settings.fontWeight; | ||
} | ||
elements.forEach(function(el) { | ||
if (el.getContext) { | ||
var ctx = el.getContext('2d'); | ||
var mu = info.mu; | ||
var classes; | ||
if (getTextClasses) { | ||
classes = getTextClasses(word, weight, fontSize, extraDataArray); | ||
} else { | ||
classes = settings.classes; | ||
} | ||
// Save the current state before messing it | ||
ctx.save(); | ||
ctx.scale(1 / mu, 1 / mu); | ||
elements.forEach(function (el) { | ||
if (el.getContext) { | ||
var ctx = el.getContext('2d'); | ||
var mu = info.mu; | ||
ctx.font = settings.fontWeight + ' ' + | ||
(fontSize * mu).toString(10) + 'px ' + settings.fontFamily; | ||
ctx.fillStyle = color; | ||
// Save the current state before messing it | ||
ctx.save(); | ||
ctx.scale(1 / mu, 1 / mu); | ||
// Translate the canvas position to the origin coordinate of where | ||
// the text should be put. | ||
ctx.translate((gx + info.gw / 2) * g * mu, | ||
(gy + info.gh / 2) * g * mu); | ||
ctx.font = | ||
fontWeight + | ||
' ' + | ||
(fontSize * mu).toString(10) + | ||
'px ' + | ||
settings.fontFamily; | ||
ctx.fillStyle = color; | ||
if (rotateDeg !== 0) { | ||
ctx.rotate(- rotateDeg); | ||
} | ||
// Translate the canvas position to the origin coordinate of where | ||
// the text should be put. | ||
ctx.translate((gx + info.gw / 2) * g * mu, (gy + info.gh / 2) * g * mu); | ||
// Finally, fill the text. | ||
if (rotateDeg !== 0) { | ||
ctx.rotate(-rotateDeg); | ||
} | ||
// XXX: We cannot because textBaseline = 'top' here because | ||
// Firefox and Chrome uses different default line-height for canvas. | ||
// Please read https://bugzil.la/737852#c6. | ||
// Here, we use textBaseline = 'middle' and draw the text at exactly | ||
// 0.5 * fontSize lower. | ||
ctx.textBaseline = 'middle'; | ||
ctx.fillText(word, info.fillTextOffsetX * mu, | ||
(info.fillTextOffsetY + fontSize * 0.5) * mu); | ||
// Finally, fill the text. | ||
// The below box is always matches how <span>s are positioned | ||
/* ctx.strokeRect(info.fillTextOffsetX, info.fillTextOffsetY, | ||
// XXX: We cannot because textBaseline = 'top' here because | ||
// Firefox and Chrome uses different default line-height for canvas. | ||
// Please read https://bugzil.la/737852#c6. | ||
// Here, we use textBaseline = 'middle' and draw the text at exactly | ||
// 0.5 * fontSize lower. | ||
ctx.textBaseline = 'middle'; | ||
ctx.fillText( | ||
word, | ||
info.fillTextOffsetX * mu, | ||
(info.fillTextOffsetY + fontSize * 0.5) * mu | ||
); | ||
// The below box is always matches how <span>s are positioned | ||
/* ctx.strokeRect(info.fillTextOffsetX, info.fillTextOffsetY, | ||
info.fillTextWidth, info.fillTextHeight); */ | ||
// Restore the state. | ||
ctx.restore(); | ||
} else { | ||
// drawText on DIV element | ||
var span = document.createElement('span'); | ||
var transformRule = ''; | ||
transformRule = 'rotate(' + (- rotateDeg / Math.PI * 180) + 'deg) '; | ||
if (info.mu !== 1) { | ||
transformRule += | ||
'translateX(-' + (info.fillTextWidth / 4) + 'px) ' + | ||
'scale(' + (1 / info.mu) + ')'; | ||
// Restore the state. | ||
ctx.restore(); | ||
} else { | ||
// drawText on DIV element | ||
var span = document.createElement('span'); | ||
var transformRule = ''; | ||
transformRule = 'rotate(' + (-rotateDeg / Math.PI) * 180 + 'deg) '; | ||
if (info.mu !== 1) { | ||
transformRule += | ||
'translateX(-' + | ||
info.fillTextWidth / 4 + | ||
'px) ' + | ||
'scale(' + | ||
1 / info.mu + | ||
')'; | ||
} | ||
var styleRules = { | ||
position: 'absolute', | ||
display: 'block', | ||
font: | ||
fontWeight + ' ' + fontSize * info.mu + 'px ' + settings.fontFamily, | ||
left: (gx + info.gw / 2) * g + info.fillTextOffsetX + 'px', | ||
top: (gy + info.gh / 2) * g + info.fillTextOffsetY + 'px', | ||
width: info.fillTextWidth + 'px', | ||
height: info.fillTextHeight + 'px', | ||
lineHeight: fontSize + 'px', | ||
whiteSpace: 'nowrap', | ||
transform: transformRule, | ||
webkitTransform: transformRule, | ||
msTransform: transformRule, | ||
transformOrigin: '50% 40%', | ||
webkitTransformOrigin: '50% 40%', | ||
msTransformOrigin: '50% 40%' | ||
}; | ||
if (color) { | ||
styleRules.color = color; | ||
} | ||
span.textContent = word; | ||
for (var cssProp in styleRules) { | ||
span.style[cssProp] = styleRules[cssProp]; | ||
} | ||
if (attributes) { | ||
for (var attribute in attributes) { | ||
span.setAttribute(attribute, attributes[attribute]); | ||
} | ||
var styleRules = { | ||
'position': 'absolute', | ||
'display': 'block', | ||
'font': settings.fontWeight + ' ' + | ||
(fontSize * info.mu) + 'px ' + settings.fontFamily, | ||
'left': ((gx + info.gw / 2) * g + info.fillTextOffsetX) + 'px', | ||
'top': ((gy + info.gh / 2) * g + info.fillTextOffsetY) + 'px', | ||
'width': info.fillTextWidth + 'px', | ||
'height': info.fillTextHeight + 'px', | ||
'lineHeight': fontSize + 'px', | ||
'whiteSpace': 'nowrap', | ||
'transform': transformRule, | ||
'webkitTransform': transformRule, | ||
'msTransform': transformRule, | ||
'transformOrigin': '50% 40%', | ||
'webkitTransformOrigin': '50% 40%', | ||
'msTransformOrigin': '50% 40%' | ||
}; | ||
if (color) { | ||
styleRules.color = color; | ||
} | ||
span.textContent = word; | ||
for (var cssProp in styleRules) { | ||
span.style[cssProp] = styleRules[cssProp]; | ||
} | ||
if (attributes) { | ||
for (var attribute in attributes) { | ||
span.setAttribute(attribute, attributes[attribute]); | ||
} | ||
} | ||
if (classes) { | ||
span.className += classes; | ||
} | ||
el.appendChild(span); | ||
} | ||
}); | ||
}; | ||
/* Help function to updateGrid */ | ||
var fillGridAt = function fillGridAt(x, y, drawMask, dimension, item) { | ||
if (x >= ngx || y >= ngy || x < 0 || y < 0) { | ||
return; | ||
if (classes) { | ||
span.className += classes; | ||
} | ||
el.appendChild(span); | ||
} | ||
}); | ||
}; | ||
grid[x][y] = false; | ||
/* Help function to updateGrid */ | ||
var fillGridAt = function fillGridAt(x, y, drawMask, dimension, item) { | ||
if (x >= ngx || y >= ngy || x < 0 || y < 0) { | ||
return; | ||
} | ||
if (drawMask) { | ||
var ctx = elements[0].getContext('2d'); | ||
ctx.fillRect(x * g, y * g, maskRectWidth, maskRectWidth); | ||
} | ||
grid[x][y] = false; | ||
if (interactive) { | ||
infoGrid[x][y] = { item: item, dimension: dimension }; | ||
} | ||
}; | ||
if (drawMask) { | ||
var ctx = elements[0].getContext('2d'); | ||
ctx.fillRect(x * g, y * g, maskRectWidth, maskRectWidth); | ||
} | ||
/* Update the filling information of the given space with occupied points. | ||
if (interactive) { | ||
infoGrid[x][y] = { item: item, dimension: dimension }; | ||
} | ||
}; | ||
/* Update the filling information of the given space with occupied points. | ||
Draw the mask on the canvas if necessary. */ | ||
var updateGrid = function updateGrid(gx, gy, gw, gh, info, item) { | ||
var occupied = info.occupied; | ||
var drawMask = settings.drawMask; | ||
var ctx; | ||
if (drawMask) { | ||
ctx = elements[0].getContext('2d'); | ||
ctx.save(); | ||
ctx.fillStyle = settings.maskColor; | ||
} | ||
var updateGrid = function updateGrid(gx, gy, gw, gh, info, item) { | ||
var occupied = info.occupied; | ||
var drawMask = settings.drawMask; | ||
var ctx; | ||
if (drawMask) { | ||
ctx = elements[0].getContext('2d'); | ||
ctx.save(); | ||
ctx.fillStyle = settings.maskColor; | ||
} | ||
var dimension; | ||
if (interactive) { | ||
var bounds = info.bounds; | ||
dimension = { | ||
x: (gx + bounds[3]) * g, | ||
y: (gy + bounds[0]) * g, | ||
w: (bounds[1] - bounds[3] + 1) * g, | ||
h: (bounds[2] - bounds[0] + 1) * g | ||
}; | ||
} | ||
var dimension; | ||
if (interactive) { | ||
var bounds = info.bounds; | ||
dimension = { | ||
x: (gx + bounds[3]) * g, | ||
y: (gy + bounds[0]) * g, | ||
w: (bounds[1] - bounds[3] + 1) * g, | ||
h: (bounds[2] - bounds[0] + 1) * g | ||
}; | ||
} | ||
var i = occupied.length; | ||
while (i--) { | ||
var px = gx + occupied[i][0]; | ||
var py = gy + occupied[i][1]; | ||
var i = occupied.length; | ||
while (i--) { | ||
var px = gx + occupied[i][0]; | ||
var py = gy + occupied[i][1]; | ||
if (px >= ngx || py >= ngy || px < 0 || py < 0) { | ||
continue; | ||
} | ||
fillGridAt(px, py, drawMask, dimension, item); | ||
if (px >= ngx || py >= ngy || px < 0 || py < 0) { | ||
continue; | ||
} | ||
if (drawMask) { | ||
ctx.restore(); | ||
} | ||
}; | ||
fillGridAt(px, py, drawMask, dimension, item); | ||
} | ||
/* putWord() processes each item on the list, | ||
if (drawMask) { | ||
ctx.restore(); | ||
} | ||
}; | ||
/* putWord() processes each item on the list, | ||
calculate it's size and determine it's position, and actually | ||
put it on the canvas. */ | ||
var putWord = function putWord(item) { | ||
var word, weight, attributes; | ||
if (Array.isArray(item)) { | ||
word = item[0]; | ||
weight = item[1]; | ||
} else { | ||
word = item.word; | ||
weight = item.weight; | ||
attributes = item.attributes; | ||
} | ||
var rotateDeg = getRotateDeg(); | ||
var putWord = function putWord(item) { | ||
var word, weight, attributes; | ||
if (Array.isArray(item)) { | ||
word = item[0]; | ||
weight = item[1]; | ||
} else { | ||
word = item.word; | ||
weight = item.weight; | ||
attributes = item.attributes; | ||
} | ||
var rotateDeg = getRotateDeg(); | ||
// get info needed to put the text onto the canvas | ||
var info = getTextInfo(word, weight, rotateDeg); | ||
var extraDataArray = getItemExtraData(item); | ||
// not getting the info means we shouldn't be drawing this one. | ||
if (!info) { | ||
return false; | ||
} | ||
// get info needed to put the text onto the canvas | ||
var info = getTextInfo(word, weight, rotateDeg, extraDataArray); | ||
if (exceedTime()) { | ||
// not getting the info means we shouldn't be drawing this one. | ||
if (!info) { | ||
return false; | ||
} | ||
if (exceedTime()) { | ||
return false; | ||
} | ||
// If drawOutOfBound is set to false, | ||
// skip the loop if we have already know the bounding box of | ||
// word is larger than the canvas. | ||
if (!settings.drawOutOfBound && !settings.shrinkToFit) { | ||
var bounds = info.bounds; | ||
if (bounds[1] - bounds[3] + 1 > ngx || bounds[2] - bounds[0] + 1 > ngy) { | ||
return false; | ||
} | ||
} | ||
// If drawOutOfBound is set to false, | ||
// skip the loop if we have already know the bounding box of | ||
// word is larger than the canvas. | ||
if (!settings.drawOutOfBound) { | ||
var bounds = info.bounds; | ||
if ((bounds[1] - bounds[3] + 1) > ngx || | ||
(bounds[2] - bounds[0] + 1) > ngy) { | ||
return false; | ||
} | ||
} | ||
// Determine the position to put the text by | ||
// start looking for the nearest points | ||
var r = maxRadius + 1; | ||
// Determine the position to put the text by | ||
// start looking for the nearest points | ||
var r = maxRadius + 1; | ||
var tryToPutWordAtPoint = function (gxy) { | ||
var gx = Math.floor(gxy[0] - info.gw / 2); | ||
var gy = Math.floor(gxy[1] - info.gh / 2); | ||
var gw = info.gw; | ||
var gh = info.gh; | ||
var tryToPutWordAtPoint = function(gxy) { | ||
var gx = Math.floor(gxy[0] - info.gw / 2); | ||
var gy = Math.floor(gxy[1] - info.gh / 2); | ||
var gw = info.gw; | ||
var gh = info.gh; | ||
// If we cannot fit the text at this position, return false | ||
// and go to the next position. | ||
if (!canFitText(gx, gy, gw, gh, info.occupied)) { | ||
return false; | ||
} | ||
// If we cannot fit the text at this position, return false | ||
// and go to the next position. | ||
if (!canFitText(gx, gy, gw, gh, info.occupied)) { | ||
return false; | ||
} | ||
// Actually put the text on the canvas | ||
drawText( | ||
gx, | ||
gy, | ||
info, | ||
word, | ||
weight, | ||
maxRadius - r, | ||
gxy[2], | ||
rotateDeg, | ||
attributes, | ||
extraDataArray | ||
); | ||
// Actually put the text on the canvas | ||
drawText(gx, gy, info, word, weight, | ||
(maxRadius - r), gxy[2], rotateDeg, attributes); | ||
// Mark the spaces on the grid as filled | ||
updateGrid(gx, gy, gw, gh, info, item); | ||
// Mark the spaces on the grid as filled | ||
updateGrid(gx, gy, gw, gh, info, item); | ||
return { | ||
gx: gx, | ||
gy: gy, | ||
rot: rotateDeg, | ||
info: info | ||
}; | ||
return { | ||
gx: gx, | ||
gy: gy, | ||
rot: rotateDeg, | ||
info: info | ||
}; | ||
}; | ||
while (r--) { | ||
var points = getPointsAtRadius(maxRadius - r); | ||
while (r--) { | ||
var points = getPointsAtRadius(maxRadius - r); | ||
if (settings.shuffle) { | ||
points = [].concat(points); | ||
shuffleArray(points); | ||
if (settings.shuffle) { | ||
points = [].concat(points); | ||
shuffleArray(points); | ||
} | ||
// Try to fit the words by looking at each point. | ||
// array.some() will stop and return true | ||
// when putWordAtPoint() returns true. | ||
for (var i = 0; i < points.length; i++) { | ||
var res = tryToPutWordAtPoint(points[i]); | ||
if (res) { | ||
return res; | ||
} | ||
} | ||
// Try to fit the words by looking at each point. | ||
// array.some() will stop and return true | ||
// when putWordAtPoint() returns true. | ||
for (var i = 0; i < points.length; i++) { | ||
var res = tryToPutWordAtPoint(points[i]); | ||
if (res) { | ||
return res; | ||
} | ||
// var drawn = points.some(tryToPutWordAtPoint); | ||
// if (drawn) { | ||
// // leave putWord() and return true | ||
// return true; | ||
// } | ||
if (settings.shrinkToFit) { | ||
if (Array.isArray(item)) { | ||
item[1] = (item[1] * 3) / 4; | ||
} else { | ||
item.weight = (item.weight * 3) / 4; | ||
} | ||
// var drawn = points.some(tryToPutWordAtPoint); | ||
// if (drawn) { | ||
// // leave putWord() and return true | ||
// return true; | ||
// } | ||
return putWord(item); | ||
} | ||
// we tried all distances but text won't fit, return null | ||
return null; | ||
}; | ||
} | ||
// we tried all distances but text won't fit, return null | ||
return null; | ||
}; | ||
/* Send DOM event to all elements. Will stop sending event and return | ||
/* Send DOM event to all elements. Will stop sending event and return | ||
if the previous one is canceled (for cancelable events). */ | ||
var sendEvent = function sendEvent(type, cancelable, detail) { | ||
if (cancelable) { | ||
return !elements.some(function(el) { | ||
var evt = document.createEvent('CustomEvent'); | ||
evt.initCustomEvent(type, true, cancelable, detail || {}); | ||
return !el.dispatchEvent(evt); | ||
}, this); | ||
} else { | ||
elements.forEach(function(el) { | ||
var evt = document.createEvent('CustomEvent'); | ||
evt.initCustomEvent(type, true, cancelable, detail || {}); | ||
el.dispatchEvent(evt); | ||
}, this); | ||
} | ||
}; | ||
var sendEvent = function sendEvent(type, cancelable, details) { | ||
if (cancelable) { | ||
return !elements.some(function (el) { | ||
var event = new CustomEvent(type, { | ||
detail: details || {} | ||
}); | ||
return !el.dispatchEvent(event); | ||
}, this); | ||
} else { | ||
elements.forEach(function (el) { | ||
var event = new CustomEvent(type, { | ||
detail: details || {} | ||
}); | ||
el.dispatchEvent(event); | ||
}, this); | ||
} | ||
}; | ||
/* Start drawing on a canvas */ | ||
var start = function start() { | ||
// For dimensions, clearCanvas etc., | ||
// we only care about the first element. | ||
var canvas = elements[0]; | ||
/* Start drawing on a canvas */ | ||
var start = function start() { | ||
// For dimensions, clearCanvas etc., | ||
// we only care about the first element. | ||
var canvas = elements[0]; | ||
if (canvas.getContext) { | ||
ngx = Math.ceil(canvas.width / g); | ||
ngy = Math.ceil(canvas.height / g); | ||
} else { | ||
var rect = canvas.getBoundingClientRect(); | ||
ngx = Math.ceil(rect.width / g); | ||
ngy = Math.ceil(rect.height / g); | ||
} | ||
if (canvas.getContext) { | ||
ngx = Math.ceil(canvas.width / g); | ||
ngy = Math.ceil(canvas.height / g); | ||
} else { | ||
var rect = canvas.getBoundingClientRect(); | ||
ngx = Math.ceil(rect.width / g); | ||
ngy = Math.ceil(rect.height / g); | ||
} | ||
// Sending a wordcloudstart event which cause the previous loop to stop. | ||
// Do nothing if the event is canceled. | ||
if (!sendEvent('wordcloudstart', true)) { | ||
return; | ||
} | ||
// Sending a wordcloudstart event which cause the previous loop to stop. | ||
// Do nothing if the event is canceled. | ||
if (!sendEvent('wordcloudstart', true)) { | ||
return; | ||
} | ||
// Determine the center of the word cloud | ||
center = (settings.origin) ? | ||
[settings.origin[0]/g, settings.origin[1]/g] : | ||
[ngx / 2, ngy / 2]; | ||
// Determine the center of the word cloud | ||
center = settings.origin | ||
? [settings.origin[0] / g, settings.origin[1] / g] | ||
: [ngx / 2, ngy / 2]; | ||
// Maxium radius to look for space | ||
maxRadius = Math.floor(Math.sqrt(ngx * ngx + ngy * ngy)); | ||
// Maxium radius to look for space | ||
maxRadius = Math.floor(Math.sqrt(ngx * ngx + ngy * ngy)); | ||
/* Clear the canvas only if the clearCanvas is set, | ||
/* Clear the canvas only if the clearCanvas is set, | ||
if not, update the grid to the current canvas state */ | ||
grid = []; | ||
grid = []; | ||
var gx, gy, i; | ||
if (!canvas.getContext || settings.clearCanvas) { | ||
elements.forEach(function(el) { | ||
if (el.getContext) { | ||
var ctx = el.getContext('2d'); | ||
ctx.fillStyle = settings.backgroundColor; | ||
ctx.clearRect(0, 0, ngx * (g + 1), ngy * (g + 1)); | ||
ctx.fillRect(0, 0, ngx * (g + 1), ngy * (g + 1)); | ||
} else { | ||
el.textContent = ''; | ||
el.style.backgroundColor = settings.backgroundColor; | ||
el.style.position = 'relative'; | ||
} | ||
}); | ||
var gx, gy, i; | ||
if (!canvas.getContext || settings.clearCanvas) { | ||
elements.forEach(function (el) { | ||
if (el.getContext) { | ||
var ctx = el.getContext('2d'); | ||
ctx.fillStyle = settings.backgroundColor; | ||
ctx.clearRect(0, 0, ngx * (g + 1), ngy * (g + 1)); | ||
ctx.fillRect(0, 0, ngx * (g + 1), ngy * (g + 1)); | ||
} else { | ||
el.textContent = ''; | ||
el.style.backgroundColor = settings.backgroundColor; | ||
el.style.position = 'relative'; | ||
} | ||
}); | ||
/* fill the grid with empty state */ | ||
gx = ngx; | ||
while (gx--) { | ||
grid[gx] = []; | ||
gy = ngy; | ||
while (gy--) { | ||
grid[gx][gy] = true; | ||
} | ||
/* fill the grid with empty state */ | ||
gx = ngx; | ||
while (gx--) { | ||
grid[gx] = []; | ||
gy = ngy; | ||
while (gy--) { | ||
grid[gx][gy] = true; | ||
} | ||
} else { | ||
/* Determine bgPixel by creating | ||
} | ||
} else { | ||
/* Determine bgPixel by creating | ||
another canvas and fill the specified background color. */ | ||
var bctx = document.createElement('canvas').getContext('2d'); | ||
var bctx = document.createElement('canvas').getContext('2d'); | ||
bctx.fillStyle = settings.backgroundColor; | ||
bctx.fillRect(0, 0, 1, 1); | ||
var bgPixel = bctx.getImageData(0, 0, 1, 1).data; | ||
bctx.fillStyle = settings.backgroundColor; | ||
bctx.fillRect(0, 0, 1, 1); | ||
var bgPixel = bctx.getImageData(0, 0, 1, 1).data; | ||
/* Read back the pixels of the canvas we got to tell which part of the | ||
/* Read back the pixels of the canvas we got to tell which part of the | ||
canvas is empty. | ||
(no clearCanvas only works with a canvas, not divs) */ | ||
var imageData = | ||
canvas.getContext('2d').getImageData(0, 0, ngx * g, ngy * g).data; | ||
var imageData = canvas | ||
.getContext('2d') | ||
.getImageData(0, 0, ngx * g, ngy * g).data; | ||
gx = ngx; | ||
var x, y; | ||
while (gx--) { | ||
grid[gx] = []; | ||
gy = ngy; | ||
while (gy--) { | ||
y = g; | ||
singleGridLoop: while (y--) { | ||
x = g; | ||
while (x--) { | ||
i = 4; | ||
while (i--) { | ||
if (imageData[((gy * g + y) * ngx * g + | ||
(gx * g + x)) * 4 + i] !== bgPixel[i]) { | ||
grid[gx][gy] = false; | ||
break singleGridLoop; | ||
} | ||
gx = ngx; | ||
var x, y; | ||
while (gx--) { | ||
grid[gx] = []; | ||
gy = ngy; | ||
while (gy--) { | ||
y = g; | ||
/* eslint no-labels: ['error', { 'allowLoop': true }] */ | ||
singleGridLoop: while (y--) { | ||
x = g; | ||
while (x--) { | ||
i = 4; | ||
while (i--) { | ||
if ( | ||
imageData[((gy * g + y) * ngx * g + (gx * g + x)) * 4 + i] !== | ||
bgPixel[i] | ||
) { | ||
grid[gx][gy] = false; | ||
break singleGridLoop; | ||
} | ||
} | ||
} | ||
if (grid[gx][gy] !== false) { | ||
grid[gx][gy] = true; | ||
} | ||
} | ||
if (grid[gx][gy] !== false) { | ||
grid[gx][gy] = true; | ||
} | ||
} | ||
imageData = bctx = bgPixel = undefined; | ||
} | ||
// fill the infoGrid with empty state if we need it | ||
if (settings.hover || settings.click) { | ||
imageData = bctx = bgPixel = undefined; | ||
} | ||
interactive = true; | ||
// fill the infoGrid with empty state if we need it | ||
if (settings.hover || settings.click) { | ||
interactive = true; | ||
/* fill the grid with empty state */ | ||
gx = ngx + 1; | ||
while (gx--) { | ||
infoGrid[gx] = []; | ||
} | ||
/* fill the grid with empty state */ | ||
gx = ngx + 1; | ||
while (gx--) { | ||
infoGrid[gx] = []; | ||
} | ||
if (settings.hover) { | ||
canvas.addEventListener('mousemove', wordcloudhover); | ||
} | ||
if (settings.hover) { | ||
canvas.addEventListener('mousemove', wordcloudhover); | ||
} | ||
if (settings.click) { | ||
canvas.addEventListener('click', wordcloudclick); | ||
canvas.addEventListener('touchstart', wordcloudclick); | ||
canvas.addEventListener('touchend', function (e) { | ||
e.preventDefault(); | ||
}); | ||
canvas.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)'; | ||
} | ||
canvas.addEventListener('wordcloudstart', function stopInteraction() { | ||
canvas.removeEventListener('wordcloudstart', stopInteraction); | ||
canvas.removeEventListener('mousemove', wordcloudhover); | ||
canvas.removeEventListener('click', wordcloudclick); | ||
hovered = undefined; | ||
if (settings.click) { | ||
canvas.addEventListener('click', wordcloudclick); | ||
canvas.addEventListener('touchstart', wordcloudclick); | ||
canvas.addEventListener('touchend', function (e) { | ||
e.preventDefault(); | ||
}); | ||
canvas.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)'; | ||
} | ||
i = 0; | ||
var loopingFunction, stoppingFunction; | ||
var layouting = true; | ||
if (!settings.layoutAnimation) { | ||
loopingFunction = function (cb) { | ||
cb(); | ||
} | ||
stoppingFunction = function () { | ||
layouting = false; | ||
} | ||
} | ||
else if (settings.wait !== 0) { | ||
loopingFunction = window.setTimeout; | ||
stoppingFunction = window.clearTimeout; | ||
} else { | ||
loopingFunction = window.setImmediate; | ||
stoppingFunction = window.clearImmediate; | ||
} | ||
canvas.addEventListener('wordcloudstart', function stopInteraction() { | ||
canvas.removeEventListener('wordcloudstart', stopInteraction); | ||
var addEventListener = function addEventListener(type, listener) { | ||
elements.forEach(function(el) { | ||
el.addEventListener(type, listener); | ||
}, this); | ||
}; | ||
canvas.removeEventListener('mousemove', wordcloudhover); | ||
canvas.removeEventListener('click', wordcloudclick); | ||
hovered = undefined; | ||
}); | ||
} | ||
var removeEventListener = function removeEventListener(type, listener) { | ||
elements.forEach(function(el) { | ||
el.removeEventListener(type, listener); | ||
}, this); | ||
i = 0; | ||
var loopingFunction, stoppingFunction; | ||
var layouting = true; | ||
if (!settings.layoutAnimation) { | ||
loopingFunction = function (cb) { | ||
cb(); | ||
}; | ||
var anotherWordCloudStart = function anotherWordCloudStart() { | ||
removeEventListener('wordcloudstart', anotherWordCloudStart); | ||
stoppingFunction(timer); | ||
stoppingFunction = function () { | ||
layouting = false; | ||
}; | ||
} else if (settings.wait !== 0) { | ||
loopingFunction = window.setTimeout; | ||
stoppingFunction = window.clearTimeout; | ||
} else { | ||
loopingFunction = window.setImmediate; | ||
stoppingFunction = window.clearImmediate; | ||
} | ||
addEventListener('wordcloudstart', anotherWordCloudStart); | ||
var addEventListener = function addEventListener(type, listener) { | ||
elements.forEach(function (el) { | ||
el.addEventListener(type, listener); | ||
}, this); | ||
}; | ||
// At least wait the following code before call the first iteration. | ||
var timer = (settings.layoutAnimation ? loopingFunction : setTimeout)(function loop() { | ||
var removeEventListener = function removeEventListener(type, listener) { | ||
elements.forEach(function (el) { | ||
el.removeEventListener(type, listener); | ||
}, this); | ||
}; | ||
var anotherWordCloudStart = function anotherWordCloudStart() { | ||
removeEventListener('wordcloudstart', anotherWordCloudStart); | ||
stoppingFunction(timer[timerId]); | ||
}; | ||
addEventListener('wordcloudstart', anotherWordCloudStart); | ||
// At least wait the following code before call the first iteration. | ||
timer[timerId] = (settings.layoutAnimation ? loopingFunction : setTimeout)( | ||
function loop() { | ||
if (!layouting) { | ||
@@ -1151,14 +1300,16 @@ return; | ||
if (i >= settings.list.length) { | ||
stoppingFunction(timer); | ||
stoppingFunction(timer[timerId]); | ||
sendEvent('wordcloudstop', false); | ||
removeEventListener('wordcloudstart', anotherWordCloudStart); | ||
delete timer[timerId]; | ||
return; | ||
} | ||
escapeTime = (new Date()).getTime(); | ||
escapeTime = new Date().getTime(); | ||
var drawn = putWord(settings.list[i]); | ||
var canceled = !sendEvent('wordclouddrawn', true, { | ||
item: settings.list[i], drawn: drawn }); | ||
item: settings.list[i], | ||
drawn: drawn | ||
}); | ||
if (exceedTime() || canceled) { | ||
stoppingFunction(timer); | ||
stoppingFunction(timer[timerId]); | ||
settings.abort(); | ||
@@ -1171,13 +1322,15 @@ sendEvent('wordcloudabort', false); | ||
i++; | ||
timer = loopingFunction(loop, settings.wait); | ||
}, settings.wait); | ||
}; | ||
// All set, start the drawing | ||
start(); | ||
timer[timerId] = loopingFunction(loop, settings.wait); | ||
}, | ||
settings.wait | ||
); | ||
}; | ||
WordCloud.isSupported = isSupported; | ||
WordCloud.minFontSize = minFontSize; | ||
// All set, start the drawing | ||
start(); | ||
}; | ||
export default WordCloud; | ||
WordCloud.isSupported = isSupported; | ||
WordCloud.minFontSize = minFontSize; | ||
export default WordCloud; |
@@ -9,3 +9,3 @@ import * as echarts from 'echarts/lib/echarts'; | ||
if (!wordCloudLayoutHelper.isSupported) { | ||
throw new Error('Sorry your browser not support wordCloud'); | ||
throw new Error('Sorry your browser not support wordCloud'); | ||
} | ||
@@ -15,177 +15,201 @@ | ||
function updateCanvasMask(maskCanvas) { | ||
var ctx = maskCanvas.getContext('2d'); | ||
var imageData = ctx.getImageData( | ||
0, 0, maskCanvas.width, maskCanvas.height); | ||
var newImageData = ctx.createImageData(imageData); | ||
var ctx = maskCanvas.getContext('2d'); | ||
var imageData = ctx.getImageData(0, 0, maskCanvas.width, maskCanvas.height); | ||
var newImageData = ctx.createImageData(imageData); | ||
var toneSum = 0; | ||
var toneCnt = 0; | ||
for (var i = 0; i < imageData.data.length; i += 4) { | ||
var alpha = imageData.data[i + 3]; | ||
if (alpha > 128) { | ||
var tone = imageData.data[i] | ||
+ imageData.data[i + 1] | ||
+ imageData.data[i + 2]; | ||
toneSum += tone; | ||
++toneCnt; | ||
} | ||
var toneSum = 0; | ||
var toneCnt = 0; | ||
for (var i = 0; i < imageData.data.length; i += 4) { | ||
var alpha = imageData.data[i + 3]; | ||
if (alpha > 128) { | ||
var tone = | ||
imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]; | ||
toneSum += tone; | ||
++toneCnt; | ||
} | ||
var threshold = toneSum / toneCnt; | ||
} | ||
var threshold = toneSum / toneCnt; | ||
for (var i = 0; i < imageData.data.length; i += 4) { | ||
var tone = imageData.data[i] | ||
+ imageData.data[i + 1] | ||
+ imageData.data[i + 2]; | ||
var alpha = imageData.data[i + 3]; | ||
for (var i = 0; i < imageData.data.length; i += 4) { | ||
var tone = | ||
imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]; | ||
var alpha = imageData.data[i + 3]; | ||
if (alpha < 128 || tone > threshold) { | ||
// Area not to draw | ||
newImageData.data[i] = 0; | ||
newImageData.data[i + 1] = 0; | ||
newImageData.data[i + 2] = 0; | ||
newImageData.data[i + 3] = 0; | ||
} | ||
else { | ||
// Area to draw | ||
// The color must be same with backgroundColor | ||
newImageData.data[i] = 255; | ||
newImageData.data[i + 1] = 255; | ||
newImageData.data[i + 2] = 255; | ||
newImageData.data[i + 3] = 255; | ||
} | ||
if (alpha < 128 || tone > threshold) { | ||
// Area not to draw | ||
newImageData.data[i] = 0; | ||
newImageData.data[i + 1] = 0; | ||
newImageData.data[i + 2] = 0; | ||
newImageData.data[i + 3] = 0; | ||
} else { | ||
// Area to draw | ||
// The color must be same with backgroundColor | ||
newImageData.data[i] = 255; | ||
newImageData.data[i + 1] = 255; | ||
newImageData.data[i + 2] = 255; | ||
newImageData.data[i + 3] = 255; | ||
} | ||
} | ||
ctx.putImageData(newImageData, 0, 0); | ||
ctx.putImageData(newImageData, 0, 0); | ||
} | ||
echarts.registerLayout(function (ecModel, api) { | ||
ecModel.eachSeriesByType('wordCloud', function (seriesModel) { | ||
var gridRect = echarts.helper.getLayoutRect( | ||
seriesModel.getBoxLayoutParams(), { | ||
width: api.getWidth(), | ||
height: api.getHeight() | ||
} | ||
); | ||
var data = seriesModel.getData(); | ||
ecModel.eachSeriesByType('wordCloud', function (seriesModel) { | ||
var gridRect = echarts.helper.getLayoutRect( | ||
seriesModel.getBoxLayoutParams(), | ||
{ | ||
width: api.getWidth(), | ||
height: api.getHeight() | ||
} | ||
); | ||
var canvas = document.createElement('canvas'); | ||
canvas.width = gridRect.width; | ||
canvas.height = gridRect.height; | ||
var keepAspect = seriesModel.get('keepAspect'); | ||
var maskImage = seriesModel.get('maskImage'); | ||
var ratio = maskImage ? maskImage.width / maskImage.height : 1; | ||
keepAspect && adjustRectAspect(gridRect, ratio); | ||
var ctx = canvas.getContext('2d'); | ||
var maskImage = seriesModel.get('maskImage'); | ||
if (maskImage) { | ||
try { | ||
ctx.drawImage(maskImage, 0, 0, canvas.width, canvas.height); | ||
updateCanvasMask(canvas); | ||
} | ||
catch (e) { | ||
console.error('Invalid mask image'); | ||
console.error(e.toString()); | ||
} | ||
} | ||
var data = seriesModel.getData(); | ||
var sizeRange = seriesModel.get('sizeRange'); | ||
var rotationRange = seriesModel.get('rotationRange'); | ||
var valueExtent = data.getDataExtent('value'); | ||
var canvas = document.createElement('canvas'); | ||
canvas.width = gridRect.width; | ||
canvas.height = gridRect.height; | ||
var DEGREE_TO_RAD = Math.PI / 180; | ||
var gridSize = seriesModel.get('gridSize'); | ||
wordCloudLayoutHelper(canvas, { | ||
list: data.mapArray('value', function (value, idx) { | ||
var itemModel = data.getItemModel(idx); | ||
return [ | ||
data.getName(idx), | ||
itemModel.get('textStyle.fontSize', true) | ||
|| echarts.number.linearMap(value, valueExtent, sizeRange), | ||
idx | ||
]; | ||
}).sort(function (a, b) { | ||
// Sort from large to small in case there is no more room for more words | ||
return b[1] - a[1]; | ||
}), | ||
fontFamily: seriesModel.get('textStyle.fontFamily') | ||
|| seriesModel.get('emphasis.textStyle.fontFamily') | ||
|| ecModel.get('textStyle.fontFamily'), | ||
fontWeight: seriesModel.get('textStyle.fontWeight') | ||
|| seriesModel.get('emphasis.textStyle.fontWeight') | ||
|| ecModel.get('textStyle.fontWeight'), | ||
var ctx = canvas.getContext('2d'); | ||
if (maskImage) { | ||
try { | ||
ctx.drawImage(maskImage, 0, 0, canvas.width, canvas.height); | ||
updateCanvasMask(canvas); | ||
} catch (e) { | ||
console.error('Invalid mask image'); | ||
console.error(e.toString()); | ||
} | ||
} | ||
gridSize: gridSize, | ||
var sizeRange = seriesModel.get('sizeRange'); | ||
var rotationRange = seriesModel.get('rotationRange'); | ||
var valueExtent = data.getDataExtent('value'); | ||
ellipticity: gridRect.height / gridRect.width, | ||
var DEGREE_TO_RAD = Math.PI / 180; | ||
var gridSize = seriesModel.get('gridSize'); | ||
wordCloudLayoutHelper(canvas, { | ||
list: data | ||
.mapArray('value', function (value, idx) { | ||
var itemModel = data.getItemModel(idx); | ||
return [ | ||
data.getName(idx), | ||
itemModel.get('textStyle.fontSize', true) || | ||
echarts.number.linearMap(value, valueExtent, sizeRange), | ||
idx | ||
]; | ||
}) | ||
.sort(function (a, b) { | ||
// Sort from large to small in case there is no more room for more words | ||
return b[1] - a[1]; | ||
}), | ||
fontFamily: | ||
seriesModel.get('textStyle.fontFamily') || | ||
seriesModel.get('emphasis.textStyle.fontFamily') || | ||
ecModel.get('textStyle.fontFamily'), | ||
fontWeight: | ||
seriesModel.get('textStyle.fontWeight') || | ||
seriesModel.get('emphasis.textStyle.fontWeight') || | ||
ecModel.get('textStyle.fontWeight'), | ||
minRotation: rotationRange[0] * DEGREE_TO_RAD, | ||
maxRotation: rotationRange[1] * DEGREE_TO_RAD, | ||
gridSize: gridSize, | ||
clearCanvas: !maskImage, | ||
ellipticity: gridRect.height / gridRect.width, | ||
rotateRatio: 1, | ||
minRotation: rotationRange[0] * DEGREE_TO_RAD, | ||
maxRotation: rotationRange[1] * DEGREE_TO_RAD, | ||
rotationStep: seriesModel.get('rotationStep') * DEGREE_TO_RAD, | ||
clearCanvas: !maskImage, | ||
drawOutOfBound: seriesModel.get('drawOutOfBound'), | ||
rotateRatio: 1, | ||
layoutAnimation: seriesModel.get('layoutAnimation'), | ||
rotationStep: seriesModel.get('rotationStep') * DEGREE_TO_RAD, | ||
shuffle: false, | ||
drawOutOfBound: seriesModel.get('drawOutOfBound'), | ||
shrinkToFit: seriesModel.get('shrinkToFit'), | ||
shape: seriesModel.get('shape') | ||
}); | ||
layoutAnimation: seriesModel.get('layoutAnimation'), | ||
function onWordCloudDrawn(e) { | ||
var item = e.detail.item; | ||
if (e.detail.drawn && seriesModel.layoutInstance.ondraw) { | ||
e.detail.drawn.gx += gridRect.x / gridSize; | ||
e.detail.drawn.gy += gridRect.y / gridSize; | ||
seriesModel.layoutInstance.ondraw( | ||
item[0], item[1], item[2], e.detail.drawn | ||
); | ||
} | ||
} | ||
shuffle: false, | ||
canvas.addEventListener('wordclouddrawn', onWordCloudDrawn); | ||
shape: seriesModel.get('shape') | ||
}); | ||
if (seriesModel.layoutInstance) { | ||
// Dispose previous | ||
seriesModel.layoutInstance.dispose(); | ||
} | ||
function onWordCloudDrawn(e) { | ||
var item = e.detail.item; | ||
if (e.detail.drawn && seriesModel.layoutInstance.ondraw) { | ||
e.detail.drawn.gx += gridRect.x / gridSize; | ||
e.detail.drawn.gy += gridRect.y / gridSize; | ||
seriesModel.layoutInstance.ondraw( | ||
item[0], | ||
item[1], | ||
item[2], | ||
e.detail.drawn | ||
); | ||
} | ||
} | ||
seriesModel.layoutInstance = { | ||
ondraw: null, | ||
canvas.addEventListener('wordclouddrawn', onWordCloudDrawn); | ||
dispose: function () { | ||
canvas.removeEventListener('wordclouddrawn', onWordCloudDrawn); | ||
// Abort | ||
canvas.addEventListener('wordclouddrawn', function (e) { | ||
// Prevent default to cancle the event and stop the loop | ||
e.preventDefault(); | ||
}); | ||
} | ||
}; | ||
}); | ||
if (seriesModel.layoutInstance) { | ||
// Dispose previous | ||
seriesModel.layoutInstance.dispose(); | ||
} | ||
seriesModel.layoutInstance = { | ||
ondraw: null, | ||
dispose: function () { | ||
canvas.removeEventListener('wordclouddrawn', onWordCloudDrawn); | ||
// Abort | ||
canvas.addEventListener('wordclouddrawn', function (e) { | ||
// Prevent default to cancle the event and stop the loop | ||
e.preventDefault(); | ||
}); | ||
} | ||
}; | ||
}); | ||
}); | ||
echarts.registerPreprocessor(function (option) { | ||
var series = (option || {}).series; | ||
!echarts.util.isArray(series) && (series = series ? [series] : []); | ||
var series = (option || {}).series; | ||
!echarts.util.isArray(series) && (series = series ? [series] : []); | ||
var compats = ['shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY']; | ||
var compats = ['shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY']; | ||
echarts.util.each(series, function (seriesItem) { | ||
if (seriesItem && seriesItem.type === 'wordCloud') { | ||
var textStyle = seriesItem.textStyle || {}; | ||
echarts.util.each(series, function (seriesItem) { | ||
if (seriesItem && seriesItem.type === 'wordCloud') { | ||
var textStyle = seriesItem.textStyle || {}; | ||
compatTextStyle(textStyle.normal); | ||
compatTextStyle(textStyle.emphasis); | ||
compatTextStyle(textStyle.normal); | ||
compatTextStyle(textStyle.emphasis); | ||
} | ||
}); | ||
function compatTextStyle(textStyle) { | ||
textStyle && | ||
echarts.util.each(compats, function (key) { | ||
if (textStyle.hasOwnProperty(key)) { | ||
textStyle['text' + echarts.format.capitalFirst(key)] = textStyle[key]; | ||
} | ||
}); | ||
}); | ||
} | ||
}); | ||
function compatTextStyle(textStyle) { | ||
textStyle && echarts.util.each(compats, function (key) { | ||
if (textStyle.hasOwnProperty(key)) { | ||
textStyle['text' + echarts.format.capitalFirst(key)] = textStyle[key]; | ||
} | ||
}); | ||
} | ||
}); | ||
function adjustRectAspect(gridRect, aspect) { | ||
// var outerWidth = gridRect.width + gridRect.x * 2; | ||
// var outerHeight = gridRect.height + gridRect.y * 2; | ||
var width = gridRect.width; | ||
var height = gridRect.height; | ||
if (width > height * aspect) { | ||
gridRect.x += (width - height * aspect) / 2; | ||
gridRect.width = height * aspect; | ||
} else { | ||
gridRect.y += (height - width / aspect) / 2; | ||
gridRect.height = width / aspect; | ||
} | ||
} |
import * as echarts from 'echarts/lib/echarts'; | ||
echarts.extendSeriesModel({ | ||
type: 'series.wordCloud', | ||
type: 'series.wordCloud', | ||
visualStyleAccessPath: 'textStyle', | ||
visualStyleMapper: function (model) { | ||
return { | ||
fill: model.get('color') | ||
}; | ||
}, | ||
visualDrawType: 'fill', | ||
visualStyleAccessPath: 'textStyle', | ||
visualStyleMapper: function (model) { | ||
return { | ||
fill: model.get('color') | ||
}; | ||
}, | ||
visualDrawType: 'fill', | ||
optionUpdated: function () { | ||
var option = this.option; | ||
option.gridSize = Math.max(Math.floor(option.gridSize), 4); | ||
}, | ||
optionUpdated: function () { | ||
var option = this.option; | ||
option.gridSize = Math.max(Math.floor(option.gridSize), 4); | ||
}, | ||
getInitialData: function (option, ecModel) { | ||
var dimensions = echarts.helper.createDimensions(option.data, { | ||
coordDimensions: ['value'] | ||
}); | ||
var list = new echarts.List(dimensions, this); | ||
list.initData(option.data); | ||
return list; | ||
}, | ||
getInitialData: function (option, ecModel) { | ||
var dimensions = echarts.helper.createDimensions(option.data, { | ||
coordDimensions: ['value'] | ||
}); | ||
var list = new echarts.List(dimensions, this); | ||
list.initData(option.data); | ||
return list; | ||
}, | ||
// Most of options are from https://github.com/timdream/wordcloud2.js/blob/gh-pages/API.md | ||
defaultOption: { | ||
maskImage: null, | ||
// Most of options are from https://github.com/timdream/wordcloud2.js/blob/gh-pages/API.md | ||
defaultOption: { | ||
// Shape can be 'circle', 'cardioid', 'diamond', 'triangle-forward', 'triangle', 'pentagon', 'star' | ||
shape: 'circle', | ||
keepAspect: false, | ||
maskImage: null, | ||
left: 'center', | ||
// Shape can be 'circle', 'cardioid', 'diamond', 'triangle-forward', 'triangle', 'pentagon', 'star' | ||
shape: 'circle', | ||
top: 'center', | ||
left: 'center', | ||
width: '70%', | ||
top: 'center', | ||
height: '80%', | ||
width: '70%', | ||
sizeRange: [12, 60], | ||
height: '80%', | ||
rotationRange: [-90, 90], | ||
sizeRange: [12, 60], | ||
rotationStep: 45, | ||
rotationRange: [-90, 90], | ||
gridSize: 8, | ||
rotationStep: 45, | ||
drawOutOfBound: false, | ||
shrinkToFit: false, | ||
gridSize: 8, | ||
drawOutOfBound: false, | ||
textStyle: { | ||
fontWeight: 'normal' | ||
} | ||
textStyle: { | ||
fontWeight: 'normal' | ||
} | ||
} | ||
}); |
import * as echarts from 'echarts/lib/echarts'; | ||
echarts.extendChartView({ | ||
type: 'wordCloud', | ||
type: 'wordCloud', | ||
render: function (seriesModel, ecModel, api) { | ||
var group = this.group; | ||
group.removeAll(); | ||
render: function (seriesModel, ecModel, api) { | ||
var group = this.group; | ||
group.removeAll(); | ||
var data = seriesModel.getData(); | ||
var data = seriesModel.getData(); | ||
var gridSize = seriesModel.get('gridSize'); | ||
var gridSize = seriesModel.get('gridSize'); | ||
seriesModel.layoutInstance.ondraw = function (text, size, dataIdx, drawn) { | ||
var itemModel = data.getItemModel(dataIdx); | ||
var textStyleModel = itemModel.getModel('textStyle'); | ||
seriesModel.layoutInstance.ondraw = function (text, size, dataIdx, drawn) { | ||
var itemModel = data.getItemModel(dataIdx); | ||
var textStyleModel = itemModel.getModel('textStyle'); | ||
var textEl = new echarts.graphic.Text({ | ||
style: echarts.helper.createTextStyle(textStyleModel), | ||
scaleX: 1 / drawn.info.mu, | ||
scaleY: 1 / drawn.info.mu, | ||
x: (drawn.gx + drawn.info.gw / 2) * gridSize, | ||
y: (drawn.gy + drawn.info.gh / 2) * gridSize, | ||
rotation: drawn.rot | ||
}); | ||
textEl.setStyle({ | ||
x: drawn.info.fillTextOffsetX, | ||
y: drawn.info.fillTextOffsetY + size * 0.5, | ||
text: text, | ||
verticalAlign: 'middle', | ||
fill: data.getItemVisual(dataIdx, 'style').fill, | ||
fontSize: size | ||
}); | ||
var textEl = new echarts.graphic.Text({ | ||
style: echarts.helper.createTextStyle(textStyleModel), | ||
scaleX: 1 / drawn.info.mu, | ||
scaleY: 1 / drawn.info.mu, | ||
x: (drawn.gx + drawn.info.gw / 2) * gridSize, | ||
y: (drawn.gy + drawn.info.gh / 2) * gridSize, | ||
rotation: drawn.rot | ||
}); | ||
textEl.setStyle({ | ||
x: drawn.info.fillTextOffsetX, | ||
y: drawn.info.fillTextOffsetY + size * 0.5, | ||
text: text, | ||
verticalAlign: 'middle', | ||
fill: data.getItemVisual(dataIdx, 'style').fill, | ||
fontSize: size | ||
}); | ||
group.add(textEl); | ||
group.add(textEl); | ||
data.setItemGraphicEl(dataIdx, textEl); | ||
data.setItemGraphicEl(dataIdx, textEl); | ||
textEl.ensureState('emphasis').style = echarts.helper.createTextStyle( | ||
itemModel.getModel(['emphasis', 'textStyle']), | ||
{ | ||
state: 'emphasis' | ||
} | ||
); | ||
textEl.ensureState('blur').style = echarts.helper.createTextStyle( | ||
itemModel.getModel(['blur', 'textStyle']), | ||
{ | ||
state: 'blur' | ||
} | ||
); | ||
textEl.ensureState('emphasis').style = echarts.helper.createTextStyle(itemModel.getModel(['emphasis', 'textStyle']), { | ||
state: 'emphasis' | ||
}); | ||
textEl.ensureState('blur').style = echarts.helper.createTextStyle(itemModel.getModel(['blur', 'textStyle']), { | ||
state: 'blur' | ||
}); | ||
echarts.helper.enableHoverEmphasis( | ||
textEl, | ||
itemModel.get(['emphasis', 'focus']), | ||
itemModel.get(['emphasis', 'blurScope']) | ||
); | ||
echarts.helper.enableHoverEmphasis( | ||
textEl, | ||
itemModel.get(['emphasis', 'focus']), | ||
itemModel.get(['emphasis', 'blurScope']) | ||
); | ||
textEl.stateTransition = { | ||
duration: seriesModel.get('animation') | ||
? seriesModel.get(['stateAnimation', 'duration']) | ||
: 0, | ||
easing: seriesModel.get(['stateAnimation', 'easing']) | ||
}; | ||
// TODO | ||
textEl.__highDownDispatcher = true; | ||
}; | ||
textEl.stateTransition = { | ||
duration: seriesModel.get('animation') ? seriesModel.get(['stateAnimation', 'duration']) : 0, | ||
easing: seriesModel.get(['stateAnimation', 'easing']) | ||
}; | ||
// TODO | ||
textEl.__highDownDispatcher = true; | ||
}; | ||
this._model = seriesModel; | ||
}, | ||
this._model = seriesModel; | ||
}, | ||
remove: function () { | ||
this.group.removeAll(); | ||
remove: function () { | ||
this.group.removeAll(); | ||
this._model.layoutInstance.dispose(); | ||
}, | ||
this._model.layoutInstance.dispose(); | ||
}, | ||
dispose: function () { | ||
this._model.layoutInstance.dispose(); | ||
} | ||
dispose: function () { | ||
this._model.layoutInstance.dispose(); | ||
} | ||
}); |
module.exports = (env, options) => { | ||
return { | ||
entry: { | ||
'echarts-wordcloud': __dirname + '/index.js' | ||
}, | ||
output: { | ||
libraryTarget: 'umd', | ||
library: ['echarts-wordcloud'], | ||
path: __dirname + '/dist', | ||
filename: options.mode === 'production' ? '[name].min.js' : '[name].js' | ||
}, | ||
optimization: { | ||
concatenateModules: true | ||
}, | ||
devtool: 'source-map', | ||
externals: { | ||
'echarts/lib/echarts': 'echarts' | ||
} | ||
}; | ||
}; | ||
return { | ||
entry: { | ||
'echarts-wordcloud': __dirname + '/index.js' | ||
}, | ||
output: { | ||
libraryTarget: 'umd', | ||
library: ['echarts-wordcloud'], | ||
path: __dirname + '/dist', | ||
filename: options.mode === 'production' ? '[name].min.js' : '[name].js' | ||
}, | ||
optimization: { | ||
concatenateModules: true | ||
}, | ||
devtool: 'source-map', | ||
externals: { | ||
'echarts/lib/echarts': 'echarts' | ||
} | ||
}; | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
254287
18
3129
130
0
4
3