drag, resize and rotate with mouse - rotation

I need to be able to resize, rotate, and move an image. I have managed to find the code below that works but I need to either have a rotation point on the image where I can use it to rotate it with mouse or have the slider to move with the image. The problem starts when the image and the slider are too far apart. I would prefer to have the slider as part of the image if possible, somehow connected. Many thanks in advance for your help. The image is a protractor where it needs to be moved and rotated to measure angles.
Below is my codes
HTML
<!-- Mockup img downloaded from www.magicmockups.com -->
<!-- Change the mockup image to any image of your choice in the img tag bellow. -->
<!-- XXX: WARNING: When the pen is saved, the movable div is RESET to the original
position/size/rotation. -->
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
</div>
<div style="margin-top: 20px">ROTATION SLIDER:</div>
<div id="slider" style="display:inline-block; width: 50%; margin: 10px 0 10px1 0px;">
</div>
<div style="display: inline-block; position: relative;">
<!-- CHANGE IMG TO YOUR IMG -->
<div id="movable" >https://link to image</div>
Javascript
var currentUnit = "pixels"
$( function() {
$("input[type=radio]").checkboxradio();
$("#slider").slider({
min: -360,
max: 360,
slide: function(event, ui) {
// Set the slider's correct value for "value".
$(this).slider('value', ui.value);
$("#movable").css('transform', 'rotate(' + ui.value + 'deg)')
updateText()
}
});
$("#movable").draggable({
drag: function(event, ui) {
updateText()
}
})
$("#movable").resizable({
resize: function(event, ui) {
updateText()
}
})
// Init the text.
updateText();
});
function getPixelDimensions() {
precision = 100
// Save current transform (rotation).
originalTransform = $("#movable").css('transform')
// Remove rotation to make sure position() is the CSS position, not the bounding rect
position.
$("#movable").css('transform', 'rotate(0deg)')
position = $("#movable").position()
// Restore rotation.
$("#movable").css('transform', originalTransform)
dim = {
top: Math.round(position.top * precision) / precision,
left: Math.round(position.left * precision) / precision,
width: Math.round($("#movable")[0].clientWidth * precision) / precision,
height: Math.round($("#movable")[0].clientHeight * precision) / precision
}
return dim;
}
function getPercentageDimensions() {
}
function updateText() {
if(currentUnit == "pixels") {
dim = getPixelDimensions();
sufix = "px"
} else {
dim = getPercentageDimensions();
sufix = "%"
}
$(".d").remove()
for(prop in dim) {
$("#dimensions").append( "<div class='d'>" + prop + ": " + dim[prop] + sufix + "</div>");
}
$("#dimensions").append( "<div class='d'>rotate: " + $("#slider").slider("value") +
"deg</div>");
//console.log($("#outer").position().top)
}
$('input').change(function() {
if(this.id == "radio-1") {
currentUnit = "pixels";
updateText();
} else if(this.id.search("radio") != -1){
currentUnit = "percentage";
updateText();
}
})
function previewFile() {
var preview = document.querySelector('img'); //selects the query named img
var file = document.querySelector('input[type=file]').files[0]; //sames as here
var reader = new FileReader();
reader.onloadend = function () {
preview.src = reader.result;
}
if (file) {
reader.readAsDataURL(file); //reads the data as a URL
} else {
preview.src = "";
}
}
CSS
#movable {
position: absolute;
text-align: center;
/*Manually change values here.*/
width: 400px;
height: 400px;
top: 0px;
left: 0px;
transform: rotate(0deg);
}

Related

Submit button becomes invisible after speech input (Web Speech API)

This code contains the user interface of the banking chatbot. I have used Mozilla's Web Speech API to implement the speech to text feature. After implementing it, I have faced a major bug. As soon as the user starts the speech recognition by clicking on the "Speak" button; the textarea automatically increases in size and covers or hides the Submit button which is preventing the user from submitting his/her query. I haven't been able to locate the error.
//initialize speech recognition API
window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition(); //initialize my instance of speech recognition
recognition.interimResults = true; //return results while still working on current recognition
//this is where your speech-to-text results will appear
let p = document.createElement("p")
const words = document.querySelector(".words-container")
words.appendChild(p)
//I want to select and change the color of the body, but this could be any HTML element on your page
let body = document.querySelector("body")
let cap_css_colors = ["AliceBlue","AntiqueWhite","Aqua","Aquamarine","Azure","Beige","Bisque","Black","BlanchedAlmond","Blue","BlueViolet","Brown","BurlyWood","CadetBlue","Chartreuse","Chocolate","Coral","CornflowerBlue","Cornsilk","Crimson","Cyan","DarkBlue","DarkCyan","DarkGoldenRod","DarkGray","DarkGrey","DarkGreen","DarkKhaki","DarkMagenta","DarkOliveGreen","Darkorange","DarkOrchid","DarkRed","DarkSalmon","DarkSeaGreen","DarkSlateBlue","DarkSlateGray","DarkSlateGrey","DarkTurquoise","DarkViolet","DeepPink","DeepSkyBlue","DimGray","DimGrey","DodgerBlue","FireBrick","FloralWhite","ForestGreen","Fuchsia","Gainsboro","GhostWhite","Gold","GoldenRod","Gray","Grey","Green","GreenYellow","HoneyDew","HotPink","IndianRed","Indigo","Ivory","Khaki","Lavender","LavenderBlush","LawnGreen","LemonChiffon","LightBlue","LightCoral","LightCyan","LightGoldenRodYellow","LightGray","LightGrey","LightGreen","LightPink","LightSalmon","LightSeaGreen","LightSkyBlue","LightSlateGray","LightSlateGrey","LightSteelBlue","LightYellow","Lime","LimeGreen","Linen","Magenta","Maroon","MediumAquaMarine","MediumBlue","MediumOrchid","MediumPurple","MediumSeaGreen","MediumSlateBlue","MediumSpringGreen","MediumTurquoise","MediumVioletRed","MidnightBlue","MintCream","MistyRose","Moccasin","NavajoWhite","Navy","OldLace","Olive","OliveDrab","Orange","OrangeRed","Orchid","PaleGoldenRod","PaleGreen","PaleTurquoise","PaleVioletRed","PapayaWhip","PeachPuff","Peru","Pink","Plum","PowderBlue","Purple","Red","RosyBrown","RoyalBlue","SaddleBrown","Salmon","SandyBrown","SeaGreen","SeaShell","Sienna","Silver","SkyBlue","SlateBlue","SlateGray","SlateGrey","Snow","SpringGreen","SteelBlue","Tan","Teal","Thistle","Tomato","Turquoise","Violet","Wheat","White","WhiteSmoke","Yellow","YellowGreen"];
const CSS_COLORS = cap_css_colors.map(color => {
//I need to change all color names to lower case, because comparison between words will be case sensitive
return color.toLowerCase()
})
//once speech recognition determines it has a "result", grab the texts of that result, join all of them, and add to paragraph
recognition.addEventListener("result", e => {
const transcript = Array.from(e.results)
.map(result => result[0])
.map(result => result.transcript)
.join("")
p.innerText = transcript
//once speech recognition determines it has a final result, create a new paragraph and append it to the words-container
//this way every time you add a new p to hold your speech-to-text every time you're finished with the previous results
if (e.results[0].isFinal) {
p = document.createElement("p")
words.appendChild(p)
}
//for each result, map through all color names and check if current result (transcript) contains that color
//i.e. see if a person said any color name you know
CSS_COLORS.forEach(color => {
//if find a match, change your background color to that color
if (transcript.includes(color)) {
body.style.backgroundColor = color;
}
})
})
//add your functionality to the start and stop buttons
function startRecording() {
recognition.start();
recognition.addEventListener("end", recognition.start)
document.getElementById("stop").addEventListener("click", stopRecording)
}
function stopRecording() {
console.log("okay I'll stop")
recognition.removeEventListener("end", recognition.start)
recognition.stop();
}
ul {
list-style: none;
padding: 0;
}
p {
color: #444;
}
button:focus {
outline: 0;
}
.container {
max-width: 700px;
margin: 0 auto;
padding: 100px 50px;
text-align: center;
}
.container h1 {
margin-bottom: 20px;
}
.page-description {
font-size: 1.1rem;
margin: 0 auto;
}
.tz-link {
font-size: 1em;
color: #1da7da;
text-decoration: none;
}
.no-browser-support {
display: none;
font-size: 1.2rem;
color: #e64427;
margin-top: 35px;
}
.app {
margin: 40px auto;
}
#note-textarea {
margin: 20px 0;
}
#recording-instructions {
margin: 15px auto 60px;
}
#notes {
padding-top: 20px;
}
.note .header {
font-size: 0.9em;
color: #888;
margin-bottom: 10px;
}
.note .delete-note,
.note .listen-note {
text-decoration: none;
margin-left: 15px;
}
.note .content {
margin-bottom: 40px;
}
#media (max-width: 768px) {
.container {
padding: 50px 25px;
}
button {
margin-bottom: 10px;
}
}
/* -- Demo ads -- */
#media (max-width: 1200px) {
#bsaHolder{ display:none;}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MJ BOT </title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="{{ url_for('static', filename='styles/style.css') }}">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<!-- partial:index.partial.html -->
<section class="msger">
<header class="msger-header">
<div class="msger-header-title">
<i class=""></i> MJ Chatbot <i class=""></i>
</div>
</header>
<main class="msger-chat">
<div class="msg left-msg">
<div class="msg-img" style="background-image: url(https://image.flaticon.com/icons/svg/145/145867.svg)"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name"></div>
</div>
<div class="msg-text">
<p> {{ questionAsked }} </p>
</div>
</div>
</div>
</main>
<article>
<main class="msger-chat">
<div class="msg right-msg">
<div class="msg-img" style="background-image: url(https://image.flaticon.com/icons/svg/327/327779.svg)"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name"></div>
</div>
<div class="msg-text">
<p> {{ response }}</p>
</div>
</div>
</div>
</article>
</main>
<form id="output" class="msger-inputarea" action="signup" method="post">
<input id="output" class="msger-input" type="text" name="question"></input>
<input id='play' class="msger-send-btn" type="submit" value="Submit Message !" > </input>
<input type="button" value="Speak" onclick="runSpeechRecognition()"></input>
<button id='stop'></button>
</form>
<button id=play style="font-size:24px">Listen <i class="fas fa-file-audio"></i></button>
Send Query to Agent !
</section>
<script >onload = function() {
if ('speechSynthesis' in window) with(speechSynthesis) {
var playEle = document.querySelector('#play');
var pauseEle = document.querySelector('#pause');
var stopEle = document.querySelector('#stop');
var flag = false;
playEle.addEventListener('click', onClickPlay);
pauseEle.addEventListener('click', onClickPause);
stopEle.addEventListener('click', onClickStop);
function onClickPlay() {
if(!flag){
flag = true;
utterance = new SpeechSynthesisUtterance(document.querySelector('article').textContent);
utterance.voice = getVoices()[0];
utterance.onend = function(){
flag = false; playEle.className = pauseEle.className = ''; stopEle.className = 'stopped';
};
playEle.className = 'played';
stopEle.className = '';
speak(utterance);
}
if (paused) { /* unpause/resume narration */
playEle.className = 'played';
pauseEle.className = '';
resume();
}
}
function onClickPause() {
if(speaking && !paused){ /* pause narration */
pauseEle.className = 'paused';
playEle.className = '';
pause();
}
}
function onClickStop() {
if(speaking){ /* stop narration */
/* for safari */
stopEle.className = 'stopped';
playEle.className = pauseEle.className = '';
flag = false;
cancel();
}
}
}
else { /* speech synthesis not supported */
msg = document.createElement('h5');
msg.textContent = "Detected no support for Speech Synthesis";
msg.style.textAlign = 'center';
msg.style.backgroundColor = 'red';
msg.style.color = 'white';
msg.style.marginTop = msg.style.marginBottom = 0;
document.body.insertBefore(msg, document.querySelector('div'));
}
}
</script>
<script>
/* JS comes here */
function runSpeechRecognition() {
// get output div reference
var output = document.getElementById("output");
// get action element reference
var action = document.getElementById("help");
// new speech recognition object
var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition;
var recognition = new SpeechRecognition();
// This runs when the speech recognition service starts
recognition.onstart = function() {
action.innerHTML = "<small>listening, please speak...</small>";
};
recognition.onspeechend = function() {
action.innerHTML = "<small>stopped listening, hope you are done...</small>";
recognition.stop();
}
// This runs when the speech recognition service returns result
recognition.onresult = function(event) {
var transcript = event.results[0][0].transcript;
var confidence = event.results[0][0].confidence;
output.innerHTML = "<b></b> " + transcript + "<br/> <b></b> " ;
output.classList.remove("hide");
};
// start recognition
recognition.start();
}
</script>
<!-- partial -->
<script src='https://use.fontawesome.com/releases/v5.0.13/js/all.js'></script>
</body>
</html>
recognition.onend = (event) => {
//insert your code to display button here
}

Tooltips with rich HTML content does not help in creating desired UI?

I have an XYChart with data object like
chart.data = [{
"Area": "Korangi",
"AreaNumber": 120,
"SubArea": [{
"SubAreaName": "Korangi-1",
"SubAreaNumber": 60
}, {
"SubAreaName": "Korangi-2",
"SubAreaNumber": 60
}
]
}];
and a series tooltipHTML adapter as
series.tooltipHTML = `<center><strong> {Area}:</strong>
<strong> {AreaNumber}%</strong></center>
<hr />`;
series.adapter.add("tooltipHTML",
function (html, target) {
if (
target.tooltipDataItem.dataContext &&
target.tooltipDataItem.dataContext.SubArea &&
target.tooltipDataItem.dataContext.SubArea.length
) {
var nameTalClientsNumberCells = "";
Cells = "";
target.tooltipDataItem.dataContext.SubArea.forEach(part => {
if (part.SubAreaName != null) {
nameTalClientsNumberCells +=
`<tr><td><strong>${part.SubAreaName}</strong>:&nbsp ${part
.SubAreaNumber}%</td></tr>`;
}
//TalClientsNumberCells += `<td>${part.SubAreaNumber}</td>`;
});
html += `<table>
${nameTalClientsNumberCells}
</table>`;
}
return html;
});
For I have tried bootstrap classes but non of them works in tooltipHTML.
what I want is like this
but I tried so far is like this
Please help or refer if there is another way of adding really rich HTML in tooltip
A link to the codepen
What you're doing is fine. I just didn't see you used any bootstrap4 css class. You can achieve what you want with either bootstrap4 built-in classes, or your own custom styles.
//I don't need to set tooltipHTML since I have the adapter hook up to return
// custom HTML anyway
/*
series.tooltipHTML = `<center><strong> {Area}:</strong>
<strong> {AreaNumber}%</strong></center>
<hr />`;
*/
series.adapter.add("tooltipHTML", function (html, target) {
let data = target.tooltipDataItem.dataContext;
if (data) {
let html = `
<div class="custom-tooltip-container">
<div class="col-left">
<h5>${data.Area}</h5>
<ul class="list-unstyled">
${data.SubArea.map(part =>
`
<li class="part">
<span class="name">${part.SubAreaName}</span>
<span class="area">${part.SubAreaNumber}%</span>
</li>
`
).join('')}
</ul>
</div>
<div class='col-right'>
<span class="badge badge-pill badge-success">${data.AreaNumber}%</span>
</div>
</div>
`;
return html;
}
return '';
});
And here is the custom styles:
#chart {
height: 31rem;
}
.custom-tooltip-container {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
min-width: 13rem;
}
.custom-tooltip-container .col-left {
width: 70%;
}
.custom-tooltip-container .col-right {
width: 30%;
text-align: center;
}
.custom-tooltip-container .col-right .badge {
font-size: 1.1rem;
}
.custom-tooltip-container .part {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
}
Again, you can do whatever you want. Here as demo I just quickly put things together.
demo: https://jsfiddle.net/davidliang2008/6g4u2qw8/61/

Ace editor multiple cursors

In the example below, the split button should remove all semicolons and create a new editor with the following text until the next semicolon.
However, there is an odd bug where all editors except the first one have the cursor flashing at the end of the line, but only the last field has actually got the cursor (e.g. typing after pressing split will put text in the last field). How do I prevent this?
$(function() {
function aceinit() {
var e = ace.edit(this),
t = $(this);
e.setTheme("ace/theme/sqlserver");
e.setOptions({
maxLines: Infinity,
highlightActiveLine: false,
tabSize: 8,
useSoftTabs: false,
fixedWidthGutter: true
});
e.getSession().setMode("ace/mode/sql");
e.getSession().on('change', function() {
$('.output').css('opacity', '0.3');
$('.markdown-toggle').hide();
});
e.commands.bindKey("Tab", null);
e.commands.bindKey("Shift-Tab", null);
e.setAutoScrollEditorIntoView(true);
return e;
}
$('.ace').each(function() {
aceinit.call(this);
});
$('body').on('click', '.plus', function() {
$('main').children().slice(0, 3).clone().insertBefore($(this));
$('.ace').each(function() {
aceinit.call(this);
});
var b = $(this).prev().prev('.batch');
var e = aceinit.call(b.find('.ace').get(0));
e.setValue("");
e.resize();
e.focus();
b.find('.results').html('');
});
$('body').on('click', '.split', function() {
var b = $(this).prev('.batch');
e = ace.edit(b.find('.ace').get(0));
s = ';';
b.find('.results').html('');
setTimeout(function() {
var split = e.getValue().split((new RegExp(s, 'im')));
$.each(split, function(i, v) {
if (v.trim()) {
if (i > 0) {
if (!b.find('.ace').filter(function() {
return ace.edit(this).getValue() === '';
}).length) {
b.next().next('.plus').click();
}
b = b.nextAll().eq(2);
e = ace.edit(b.find('.ace').get(0));
}
e.setValue(split[i].replace(/\s+$/, '').replace(/^\s+/, ''), 1);
}
});
}, 0);
});
});
.batch {
flex-direction: column;
min-width: 0;
overflow-x: hidden;
margin-left: 0.5em;
}
.query.ace {
border: 1px solid #aaa;
height: 1em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://dbfiddle.uk/ace/ace.js"></script>
<main>
add batch
<div class="batch" style="display: flex;">
<div class="ace query">text1
;
text2
;
text3
;
text4
;
text5
;</div>
</div>
split
add batch
</main>
this happens because you are calling
e.focus();
after creating each editor, call it only for the one which you want to be focused.

Angular UI-Grid tree level with different templates

I'm new here, I'm working with Angular UI-Grid, and I have a small problem. The grid that I handle has a tree structure, that works perfect. But to make it easier to follow that structure for the user, I want to implement different colors per level.
I have created this Plunker with two examples, the first is how you should see the different colors per level, and the second is how it behaves today. Has anyone done something like this or do you have any idea how to fix it?
app.js:
var app = angular.module('app', ['ui.grid','ui.grid.treeView']);
app.controller('MainCtrl', ['$scope', '$http','uiGridTreeViewConstants', function ($scope, $http, uiGridTreeViewConstants) {
$scope.myData = [
{"ubi_id":321,"ubi_nom":"America","ubi_pad_id":null,"ubi_tip_id":1,"$$treeLevel":0},
{"ubi_id":322,"ubi_nom":"Sudamerica","ubi_pad_id":321,"ubi_tip_id":2,"$$treeLevel":1},
...
];
var rowtpl = '';
rowtpl += '<div ng-class="{\'nivelGrilla-{{row.entity.$$treeLevel}}\': row.entity.$$treeLevel != \'undefined\' }">';
rowtpl += '<div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name"';
rowtpl += 'class="ui-grid-cell" ng-class="{ \'ui-grid-row-header-cell\': col.isRowHeader }" ui-grid-cell>';
rowtpl += '</div></div>';
$scope.gridOptions = {
data:'myData',
rowTemplate:rowtpl,
};
}]);
css:
.nivelGrilla-0{
background-color: #254158;
color: white;
}
.nivelGrilla-1{
background-color: #3F6F96;
color: white;
}
html:
<div id="grid1" ui-grid="gridOptions" ui-grid-tree-view class="grid"></div>
I found a solution, the div that contains the entire row, makes a call to a function that returns the name of the class according to the row level.
Here's a plnkr with my solution
js:
var rowtpl = '';
rowtpl += '<div class=\"{{grid.appScope.rowLevel(row)}}\">';
rowtpl += '<div ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name"';
rowtpl += 'class="ui-grid-cell" ng-class="{ \'ui-grid-row-header-cell\': col.isRowHeader }" ui-grid-cell>';
rowtpl += '</div>';
rowtpl += '</div>';
$scope.gridOptions2 = {
data:'myData',
rowTemplate:rowtpl,
};
css:
.ui-grid-row .ui-grid-cell {
background-color: inherit !important;
color: inherit !important;
}
.ui-grid-row .ui-grid-cell.ui-grid-cell {
background-color: #f0f0ee;
border-bottom: solid 1px #d4d4d4;
}
.rowLevel-0{
background-color: #254158;
color: white;
}
.rowLevel-1{
background-color: #3F6F96;
color: white;
}
.rowLevel-2{
background-color: #5289B6;
}
You can specify a 'cellClass' within your grid's 'columnDefs' and switch the rows according to the tree level. For example:
$scope.gridOptions = {
data:'myData',
columnDefs: [
{ field: 'Id', name: 'Ubi Id'},
{ field: 'Country', name: 'Ubi Nom'},
cellClass: function(grid, row, col, rowRenderIndex, colRenderIndex) {
switch(row.treeLevel)
{
case 0:
return 'red';
case 1:
return 'blue';
case 2:
return 'green';
default:
break;
}
}
]
};

Kendo Display Multiple Bar Charts on Web Page

I'm using Kendo UI and trying to display multiple charts on a single web page. Everything works until I try to add a second bar chart. One of the charts does not display and just shows a generic chart image (it looks like it is not finding the data but if I reorder they reverse what is happening). I can display multiple line charts. Any ideas about what might be happening?
Below is my html for the two charts without the SVG info:
<div kendo-chart="" k-options="vm.barChartOptions" ng-show="this.dataItem.visible" class="move k-block ng-scope k-chart" id="costPerPound" style="float: left; margin: 5px 0px; position: relative;" data-uid="b543ff9a-ee57-4ce7-a39d-8f69a0505a2b" role="option" aria-selected="false" data-role="chart"></div>
<div kendo-chart="" k-options="vm.barChartOptions" ng-show="this.dataItem.visible" class="move k-block ng-scope k-chart" id="numShipments" style="float: left; margin: 5px 0px; position: relative;" data-uid="97094bf1-4366-4974-b92f-edf36d1980f4" role="option" aria-selected="false" data-role="chart"></div>
Here are is the k-options info. As you can see I am setting most of my information at render.
vm.barChartOptions = {
dataSource: vm.chartData_datasource,
series: [
{
}
],
valueAxis: {
line: {
visible: false
},
labels: {
rotation: "auto"
}
},
tooltip: {
visible: true,
template: "#= series.name #: #= value #"
},
render: function (e) {
var chart = e.sender;
var chartData = vm.findChartData(e);
if (chartData != null) {
chartData.categoryAxisField = vm.firstToLower(chartData.categoryAxisField);
chart.options.title.text = chartData.title;
chart.options.name = chartData.htmlID;
chart.options.categoryAxis.field = chartData.categoryAxisField;
chart.options.categoryAxis.labels.format = chartData.categoryAxisLabel;
chart.options.legend.position = chartData.legendPosition;
chart.options.seriesDefaults.type = chartData.chartType;
for (var i = 0; i < chart.options.series.length; i++) {
chart.options.series[i].type = chartData.chartType;
chart.options.series[i].field = vm.firstToLower(chartData.dataField);
}
}
}
}
I had same issue. It can be resolved by just providing different name
to each chart.

Resources