Colouring markers based on value of another column - plotly.js - javascript

I'm currently writing a reporting dashboard for work and thanks to some changes in what is supported, my fancy and dynamic RShiny app is a no go so i'm trying to port it all over to javascript. I know almost no js, having written everything i know in python and R, and it is causing a significant head ache.
Problem:
I'm importing a csv and attempting to have dynamically produced graphs show a variety of things, coloured by a particular columns values.
So to keep it simple, (the real csv has almost twenty columns at this point and a few hundred rows of data) lets say i have:
#X.sample_id, key, project_type, manual_interventions
BulBa, 152, AAA, 12000
CharZard, 100, AAB, 5000
PikaChu, 117, AAA, 10
SquirTle, 090, AAB, 1000
Plotting X.sample_id as the X-axis, manual_interventions as Y and most importantly I need them to be coloured by project_type. Now i know this as easy as anything in python and R, the problem is that I don't want to produce a few dozen graphs to show variations of the same thing (there will be options for y-axis to be a fair few different things), especially when this can be done dynamically.
So far I have written:
<select onchange="selectedY()" style="right: 10px; top: 20px;" id="dataBtn" title="Choose data">
<option value="">Choose options</option>
<option value="X.sample_id">TolID</option>
<option value="key">GRIT Key</option>
<option value="manual_interventions">Manual Interventions</option>
</select>
<div class="card-body" id="chart"></div>
<script>
var data = [];
var layout= {title: 'Graph', 'xaxis': {'title': 'X-axis'},
autosize: true,
width: 500,
height: 500};
var allRows = null
Plotly.d3.csv('assets/data/pulled_data_sorted.csv', function(err, data){
allRows = data
});
Plotly.plot("chart", data, layout, type="scatter", mode="markers");
function selectedY() {
var axisValue = $("#dataBtn :selected").val();
var x_row = [], y_data = [];
for (var i=0; i<allRows.length; i++) {
xrow = i;
row = allRows[i];
x_row.push(xrow);
y_data.push(+row[axisValue]);
var dataRow = {
x: x_row,
y: y_data,
mode: 'markers',
type: 'scatter',
line: {
color: '#62B5E5',
width: 2
}
};
}
layout['yaxis'] = {'title': {'text': axisValue}}
Plotly.restyle('chart', 'y', [[]]);
Plotly.plot("chart", [dataRow], layout, type="scatter", mode="markers");
}
</script>
This produces:
Which is pretty far from the publication level it needs to be.
Admittedly, this was adapted from another persons work as i've found it difficult getting to grips with this, and i think there isn't much information showing any particular in-depth use of csv's and plotly (maybe i'm not looking in the right place, i don't know).
So in the end I have no properly formatted X axis, I need to add an option to change the column of data X-axis uses, and no colour by project_type and I can't understand how to go further.
Any help is much appreciated.
Thanks.

I have fixed this now, i was relying on this working much like Python or R where colour is essentially figured out by the script rather than being explicitly given to it.
I'm aware this is still rough and i have alot to learn but i'm posting this in case it helps any future prospective js student.
<select onchange="selected()" style="right: 10px; top: 20px;" id="dataBtn" title="Choose data">
<option value="#sample_id">Tol</option>
<option value="key">Key</option>
<option value="manual_interventions">Manual Interventions</option>
</select>
<select onchange="selected()" style="right: 10px; top: 20px;" id="dataBtn2" title="Choose data">
<option value="manual_interventions">Manual Interventions</option>
<option value="length change">Percentage Genome Length Change</option>
</select>
<select onchange="selected()" style="right: 10px; top: 20px;" id="dataBtn3" title="Choose data">
<option value="project_type">Project Type</option>
<option value="'prefix">Clade Prefix</option>
</select>
<script>
function selected() {
makeplot()
}
function makeplot() {
Plotly.d3.csv('CSV FILE',
function(data){ processData(data) });
}
var layout = {
autosize: true,
width: 800,
height: 800,
xaxis:
{title: {text: "ID"}},
yaxis:
{title: {text: "manual_interactions"}}
}
function processData(allRows) {
console.log(allRows);
var x = [], y = [], colour = [];
var xaxis = $("#dataBtn").val()
var yaxis = $("#dataBtn2").val()
var colaxis = $("#dataBtn3").val()
for (var i=0; i<allRows.length; i++) {
row = allRows[i];
x.push( row[xaxis] );
y.push( row[yaxis] );
colour.push( getColour(row[colaxis]) );
}
console.log( 'X',x, 'Y',y, 'colour', colour);
makePlotly( x, y, colour, layout );
}
function rand_colour () {
var r = Math.random()*255;
var g = Math.random()*255;
var b = Math.random()*255;
return 'rgb('+r+','+g+','+b+')'
}
var colour_map = {}
function getColour(project) {
console.log(colour_map)
if (project in colour_map) {return colour_map[project]}
colour_map[project] = rand_colour();
console.log(colour_map[project]);
return colour_map[project]
}
function makePlotly( x, y, colour, layout){
var traces = [{
x: x,
y: y,
type: "scatter",
mode: "markers",
marker: {
color: colour,
width: 2
}
}];
Plotly.react('chart', traces, layout, {displayModeBar: true});
}
makeplot();
</script>
This has resulted in the following being produced, it's not perfect but it is definitely much closer to what i need than before. It is coloured by the number of projects in use.

Related

Draw pie chart using spring boot, thymeleaf, js, highchart but can't

I'm new with js and spring, now i want to create a html dashboard and this page will have a small div with pie chart. But i can't create pie chart.
I try some tutorial in youtube but now i want to pass value to ajax or something like that to get the pie chart.
Here is my Code:
admin_homepage.html:
<div class="col-xl-4 col-lg-5">
<div class="card shadow mb-4">
<!-- Thay chart vào thẻ div này -->
<div class="card-body">
<div class="chart-pie pt-4 pb-2">
<div id="chartContainer" style="height: 370px; width: 100%;"></div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$.ajax({
/* for pie chart */
url: "admin_home",
success: function(result){
/* pie chart starts here */
var series = [];
var data = [];
for(var i = 0; i < result.length; i++){
var object = {};
object.name = result[i].catName.toUpperCase();
object.y = result[i].catCount;
data.push(object);
}
var seriesObject = {
name: 'Course By Category',
colorByPoint: true,
data: data
};
series.push(seriesObject);
drawPieChart(series);
/* pie chart ends here */
}
});
/* for pie chart */
function drawPieChart(series){
Highcharts.chart('chartContainer', {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Browser market shares in January, 2018'
},
tooltip: {
formatter: function() {
return '<strong>'+this.key+': </strong>'+ this.y;
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.y}'
}
}
},
series: series
});
}
</script>
My Controller
#GetMapping("/admin_home")
public String viewHomePage(){
// Get list of course and count
List<CountCourse> pieChart = dashBoardRepository.countCourseByCategory();
model.addAttribute("pieChart",pieChart);
return "Admin_Homepage";
}
All i want is pass value of catName, catCount to pie chart but i can't
Any one help me. Many thanks.
Because you are using a Thymeleaf template, you are not required to use $.ajax({...}) to retrieve the pie chart data. Instead you can provide the data directly to the Thymeleaf template.
(Alternatively, you can continue to use an Ajax call - in which case, The Thymeleaf template will be rendered to HTML - and then as a separate step, the Ajax call will fetch the pie chart data.)
The following assumes the first approach (no Ajax needed):
No Ajax Needed
I took your Thymeleaf template in the question and made some changes to the script:
I removed the Ajax call.
I added a Thymeleaf variable to hold the chart data.
Here is the updated script:
<script th:inline="javascript">
// this simply wraps the code in a function
// that waits for the DOM to be ready:
(function () {
// this is populated by Thymeleaf:
var pieChartData = /*[[${pieChartData}]]*/ [];
var series = [];
var data = [];
for (var i = 0; i < pieChartData.length; i++) {
var object = {};
object.name = pieChartData[i].catName.toUpperCase();
object.y = pieChartData[i].catCount;
data.push(object);
}
var seriesObject = {
name: 'Course By Category',
colorByPoint: true,
data: data
};
series.push(seriesObject);
drawPieChart(series);
// draw the pie chart:
function drawPieChart(series) {
Highcharts.chart('chartContainer', {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Your Heading Goes Here'
},
tooltip: {
formatter: function () {
return '<strong>' + this.key + ': </strong>' + this.y;
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.y}'
}
}
},
// use the series data defined earlier:
series: series
});
}
})();
</script>
The key points about this script are:
The script tag looks like this:
<script th:inline="javascript">
This tells Thymeleaf that the script will contain one or more Thymeleaf expressions.
In our case we have one expression - here it is:
var pieChartData = /*[[${pieChartData}]]*/ [];
This syntax will cause Thymeleaf to replace the pieChartData variable with the data structure provided by the Java controller.
Here is that piece from the controller:
List<CountCourse> pieChartData = dashBoardRepository.countCourseByCategory();
model.addAttribute("pieChartData", pieChartData);
return "admin_homepage";
This assumes you have a CountCourse object which contains String catName and int catCount.
Thymeleaf will take the List<CountCourse> pieChartData data and generate the following JavaScript for you (using my test data):
var pieChartData = [
{"catName":"Humanities","catCount":123},
{"catName":"Sciences","catCount":145},
{"catName":"Other","catCount":67}
];
After that, I use the same logic as you have in your Ajax success function to convert this raw data into HightCharts pie chart data.
The end result is the following HTML page:
With Ajax
If you want to use your Ajax approach instead of this, then you need to build a separate end point which will return the pie chart data directly to the Ajax handler in your JavaScript code.
When you take this approach, you no longer need to use the Thymeleaf attribute:
var pieChartData = /*[[${pieChartData}]]*/ []; // NO LONGER NEEDED
And you no longer need to pass this data to your model in the controller:
model.addAttribute("pieChartData", pieChartData); // NO LONGER NEEDED
Instead, you need to continue using your $.ajax code and you need to build a separate end-point which returns the pieChartData as JSON for that Ajax call:
$.ajax({
/* for pie chart */
url: "piechart_data_json", // some new URL for your JSON pie chart data
...
});
Given you are using Thymeleaf already, I think there is no need for this approach.
Update
Just to explain the following syntax a bit more:
var pieChartData = /*[[${pieChartData}]]*/ [];
It looks like an empty JavaScript array []. But in fact, there is more to it.
The Thymeleaf variable ${pieChartData} receives the data from the controller.
Because the variable is in a <script> tag, it's not sufficient just to use the standard Thymeleaf ${pieChartData} expression. You also have to surround that expression with [[ and ]]. This is because ${pieChartData} is actually valid JavaScript - for example, as used in string interpolation.
That gives us this:
var pieChartData = [[${pieChartData}]];
This is all you need. This will work.
The problem here is, it's not valid JavaScript, so your IDE may highlight it as having a syntax error.
To work around this, you can take one extra step. You can "hide" the expression in a JavaScript comment - and then provide a valid value (the empty array). This keeps the JavaScript syntax checker happy in your IDE.
Thymeleaf will locate the variable inside that comment and remove it - and also remove the placeholder [] value.
That is how Thymeleaf pushes the Java model data into the template in this case.

Create a Stacked Column Chart with series in javascript

I am trying to create a stacked column chart with 2 categories "Kosten" & "Stromertrag". Every category has it's own column with different values. Below is the attachment of what I want to achieve.
I tried to implement 4-5 chart libraries like canvasjs, highchart, etc. But they want the array of data. But in my case I have below json from which I want to build the same.
{
substratkosten: "9,000"
kosten1: "156,600"
kosten2: "298,286"
kosten3: "64,800"
strom2: "583,200"
substrat: "108,000"
}
Where first four values is for 'Kosten' category and last 2 values is for 'Stromertrag' category. I also tried to change the chart columns color, but I didn't found any property to achieve the same.
Can anyone please help to achieve the same?
Thanks in advance.
You can parse data in the format accepted by CanvasJS and render the chart. Below is the working code.
var jsonData = {
substratkosten: "9,000",
kosten1: "156,600",
kosten2: "298,286",
kosten3: "64,800",
strom2: "583,200",
substrat: "108,000"
};
var data = [];
for (var key in jsonData) {
if(key.includes("kosten")){
data.push({type: "stackedColumn", indexLabel: "{y}", dataPoints: [{x: 1, label: "Kosten", y: parseFloat(jsonData[key].replace(/,/g, ''))}]});
}
else {
data.push({type: "stackedColumn", indexLabel: "{y}", dataPoints: [{x: 2, label: "Stromertrag", y: parseFloat(jsonData[key].replace(/,/g, ''))}]})
}
}
var chart = new CanvasJS.Chart("chartContainer", {
title:{
text: "StackedColumn Chart"
},
data: data
});
chart.render();
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<div id="chartContainer" style="height: 300px; width: 100%;"></div>

Meteor with Chart js cannot read property ‘getContext’ of null only AFTER new document in collection?

I have two selects that set session vars and after the 2nd is selected, a chart is loaded. This works just fine until I add a new document to the collection that serves as a key “index” of sorts in the compiling of the dataset. This new document has one common field with the document that generates the dataset that won’t render – that field is “season”.
The charts do not display the collection directly, as you will see there is quite a bit of data massaging going on (probably could withstand some improvements, but that’s not my issue).
My issue is that after inserting a new document with a common “season” field, any other docs used in the dataset with that same “season” field throw the getContext is null error. Other charts with different “season” fields display just fine… until I insert a document with that “season” field. If I remove that newly/recently inserted document, the chart renders again just fine.
Apologies for the data overload included below - I am dumbfounded here.
fyi, thisWeek.js:72 is from the “redrawChart” function below:
var ctx = document.getElementById("twChart").getContext("2d");
Error:
thisWeek.js:72 Uncaught TypeError: Cannot read property 'getContext' of null
at redrawChart (thisWeek.js:72)
at LocalCollection.Cursor.change #slateSelect (thisWeek.js:213)
at blaze.js?hash=f33d3dfed63a491d24e3aa07ad66c24b5fe8c761:3775
at Function.Template._withTemplateInstanceFunc (blaze.js?hash=f33d3dfed63a491d24e3aa07ad66c24b5fe8c761:3744)
at Blaze.View.<anonymous> (blaze.js?hash=f33d3dfed63a491d24e3aa07ad66c24b5fe8c761:3774)
at blaze.js?hash=f33d3dfed63a491d24e3aa07ad66c24b5fe8c761:2617
at Object.Blaze._withCurrentView (blaze.js?hash=f33d3dfed63a491d24e3aa07ad66c24b5fe8c761:2271)
at Blaze._DOMRange.<anonymous> (blaze.js?hash=f33d3dfed63a491d24e3aa07ad66c24b5fe8c761:2616)
at HTMLSelectElement.<anonymous> (blaze.js?hash=f33d3dfed63a491d24e3aa07ad66c24b5fe8c761:863)
at HTMLDivElement.dispatch (jquery.js?hash=0c5fac3e4b18ec685e561deac3634fb49bc807e5:4722)
html:
<form class="form-inline">
<div class="form-group">
<label for="slateSsnSelect">Season/Tournament</label>
<select name="sltSsn" id="slateSsnSelect" class="form-control">
{{#each slateseason in slateseasons}}
<option value="{{slateseason.season}}">{{slateseason.season}}</option>
{{/each}}
</select>
</div>
<div class="form-group">
<label for="slateSelect">Slate</label>
<select name="sltChrtSlct" id="slateSelect" class="form-control">
<option>Select a Slate...</option>
{{#each slate in slates}}
<option value="{{slate.slatename}}">{{slate.slatename}}</option>
{{/each}}
</select>
</div>
</form>
Template Events:
'change #slateSsnSelect': function(){
Session.set('slateSeason', $('#slateSsnSelect').val() );
},
'change #slateSelect': function(){
$('#twChart').remove();
$('.chrtDiv').append('<canvas id="twChart" class="img-responsive"></canvas>');
Session.set('selectedSlate', $('#slateSelect').val() );
var lg = Meteor.user().profile.lgName
var ssn = Session.get('slateSeason');
var slt = Session.get('selectedSlate');
redrawChart();
}
Template helpers:
slateseasons: function(){
var ssnsQry = Seasons.find({},{sort: {createdAt:-1} }).fetch();
if(ssnsQry.length > 0){
Session.set('slateSeason', ssnsQry[0].season);
return ssnsQry;
}
},
slates: function(){
var sltSsn = Session.get('slateSeason');
var sltsQry = Slates.find({season: sltSsn},{sort: {createdAt:-1} }).fetch();
if(sltsQry.length > 0){
Session.set('selectedSlate', sltsQry[0].slatename);
return sltsQry;
}
}
Chart function:
function redrawChart(){
$('#twChart').remove();
$('.chrtDiv').append('<canvas id="twChart" class="img-responsive"></canvas>');
var lg = Meteor.user().profile.lgName;
var lgType = Leagues.find({lgname: lg},{fields: {scoreType:1} }).fetch();
var players = _.uniq(Results.find({"league": lg},
{sort: {"user":1}},
{fields: {"user":1, _id:0}}).fetch().map(function(x) { return x.user;}), true );
var ssn = Session.get('slateSeason');
var slt = Session.get('selectedSlate');
var dataset = Results.find({"season":ssn, "slate":slt, "league": lg}, {fields: {"user":1, "pointsWon":1 }}, {sort:{"user":1}}).fetch();
var plyrsArr = _(dataset).groupBy('user');
var ptsArr = _(plyrsArr).map(function(g, key){
return { user: key,
pointsWon: _(g).reduce(function(m,x) { return m + x.pointsWon;}, 0) };
});
var labels = [], data=[];
ptsArr.forEach(function(item) {
labels.push(item.user);
data.push(parseFloat(item.pointsWon));
});
var totdata = {
labels: labels,
datasets: [{
fillColor: "#E16B6B",
data: data
}]
};
var options = {
responsive: true,
scaleBeginAtZero:true,
maintainAspectRatio: false,
barBeginAtOrigin:true
};
var ctx = document.getElementById("twChart").getContext("2d");
ctx.canvas.width = 300;
ctx.canvas.height = 400;
new Chart(ctx).Bar(totdata, options);
}
After many hours of banging my head against a brick wall, I finally realized that the helper function in the html creating the chart div was the problem.
Right below the html shown above I had:
{{#if chartExists}}
<div class="chrtDiv">
<canvas id="twChart" class="img-responsive"></canvas>
</div>
{{else}}
<div>
<h2> <img src="dunno.png" class="img-responsive" alt=""> No data...</h2>
</div>
{{/if}}
The “chartExists” helper was just checking to see if there was any Results documents available via the find() filters “season” and “slate”. I tried to be cute there by showing a gif in the event there was no data for the chart. not sure why exactly… I tried setting a session var with the resulting dataset used to populate the chart and have the helper check for that, but that didn’t work either.
I also put the canvas in the “else” section of the html but that also failed. Somehow that helper won’t let that canvas render…

Slickgrid - Change Editor of Single Cell, and not Entire Column

Here's the scenario:
There are a number of rows of selectable data
One column (#1) has dropdown editor with two options
The next column (#2) can have either a text editor, or dropdown editor - depending on the option selected in the first dropdown, see example:
_________Column #1_________|_________Column #2_________
1     select opt 1      |      *dropdown editor*    
2     select opt 1      |      *dropdown editor*    
3     select opt 1      |      *dropdown editor*    
4     select opt 2      |      *text editor*         <--- is this possible?
5     select opt 1      |      *dropdown editor*    
6     select opt 1      |      *dropdown editor*    
Is it even possible to change the editor of a single cell based on the input/change of another cell? It appears as though you can't change editors on a cell level, but only on a column level.
Any help is greatly appreciated, many hours spent on this already; and haven't found a solution or even similar question. Thanks
Update
This is getting close perhaps:
var currentRowIndex = object.grid.getActiveCell().row,
nextCellIndex = object.grid.getActiveCell().cell + 1;
object.grid.setActiveCell(currentRowIndex, nextCellIndex);
object.grid.editActiveCell(this.editors.textEditor);
But this doesn't ensure that the editor remains; for example^ a text editor. When changing the value in the first column (#1), and enabling the text editor in column #2 as above - after this edit takes place - the original editor is still in place in column #2.
I want the editor to remain the same - but can't find how to do this on a cell level rather than a column level. Thanks
Browsing the source (getEditor line 1381) pertaining to the retrieval of editors reveals a few different options are available.
column metadata
column definition
editorFactory grid option
Given that you require a different column value from within the row, I would approach the problem using the column metadata as it receives the rowIndex as an argument.
var viewModel = {options: ['LongText', 'Text', 'Checkbox']}
function apply() {
var grid, data = [];
var options = {
editable: true,
enableCellNavigation: true,
asyncEditorLoading: false,
autoEdit: true,
forcefitColumns: false
};
var columns = [{
id: "data",
name: "Editor Type",
field: "type",
width: 120,
cssClass: "cell-title" ,
formatter: function(row){
var key = 'input'+row;
if(!viewModel[key]){
viewModel[key] = ko.observable();
viewModel[key].subscribe(function(nv){
data[row].type = nv
})
}
setTimeout(function(){ ko.applyBindings(viewModel, document.getElementById(key)) }, 250);
return '<select id="'+key+'", data-bind="options: options, value: '+key+'"></select>'
}
},
{
id: "other",
name: "Other",
field: "other",
width: 120,
cssClass: "cell-title",
}];
for (var i = 0; i < 5; i++) {
var d = (data[i] = {});
d["type"] = "";
d["other"] = "Default " + i;
}
grid = new Slick.Grid("#myGrid", data, columns, options);
//ko.applyBindings(viewModel)
data.getItemMetadata=function(row){
var rowData = data[row]
//console.log(rowData)
var metaData = {columns:{other: {}}}
metaData.columns.other.editor = Slick.Editors[rowData.type]
return metaData
}
}
apply()
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<link rel="stylesheet" type="text/css" href="http://JLynch7.github.io/SlickGrid/slick.grid.css">
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.0/jquery-ui.min.js"></script>
<script src="http://JLynch7.github.io/SlickGrid/slick.dataview.js"></script>
<script src='http://mleibman.github.io/SlickGrid/lib/jquery.event.drag-2.2.js'></script>
<script src='http://JLynch7.github.io/SlickGrid/slick.core.js'></script>
<script src='http://JLynch7.github.io/SlickGrid/slick.grid.js'></script>
<script src='http://JLynch7.github.io/SlickGrid/slick.formatters.js'></script>
<script src='http://JLynch7.github.io/SlickGrid/slick.editors.js'></script>
<div id='container'>
<div id="myGrid" style="width:600px;height:300px;"></div>
</div>

Using Variable Within JavaScript Object Literal

Basically, I'm having trouble getting information from an input box and using that variable within an object literal.
Here is my code:
$("document").ready(function() {
function updateEvent(){
render();
}
function render() {
//get values from input
var gallons = $("#gallons").val();
var electricity = $("#electricity").val();
var energy = $("#energy").val();
var figure = new Highcharts.Chart({
series: [{
name: 'Throttle',
data: [0, 0, 30 , 0, energy, 30, 0, 0] //Variables will not work in here, no specific error but data doesn't show
}, {
name: 'Power',
data: [30, 30 , 30, 30, 30, 30, 30, 30]
}]
});
I've tried embedding a function and doing other tricks but nothing has worked, I could be doing it wrong, I'm not sure. Is there any way to use a variable within an object literal without completely re-coding the structure.
Any help would be greatly appreciated.
How to get a value from an input box and use that value within an object literal?
function doStuff(){
var realIncome = document.getElementById("income").value;
var objLit= { realIncome: realIncome,
desiredIncome: realIncome * 4 };
console.log(objLit);
document.getElementById("out").innerText =
objLit.realIncome + " " + objLit.desiredIncome;
}
And the HTML
Big Number
<input id="income" name="income" type="number"
placeholder="please enter your desired income">
<button name="calculate" onclick="doStuff()">click me </button>​
Your income <span id="out"></span><br />
Please take a look at my demo code: http://jsfiddle.net/mppAM/2/

Categories

Resources