/** @constructor */
MusicXMLAnalyzer.DashboardView = function(){
var that = {},
$logMessages = null,
dashboardMessageCounter = null,
$fileSelector = null,
$plainFacts = null,
$plainFacts2 = null,
$plainFacts3 = null,
$overallStatistics = null,
noteDistribution = null,
intervalDistribution = null,
keyDistribution = null,
noteTypeDistribution = null,
meterDistribution = null,
instruments = null,
segmentColors = [
"#64b5f6", // blue-300
"#81c784", // green-300
"#dce775", // lime-300
"#ff8a65", // deep-orange-300
"#ba68c8", // purple-300
"#4db6ac", // teal-300
"#fff176", // yellow-300
"#ffb74d", // orange-300
"#90a4ae", // blue-grey-300
"#1e88e5", // blue-600
"#43a047", // green-600
"#c0ca33", // lime-600
"#f4511e", // deep-orange-600
"#8e24aa", // purple-600
"#00897b", // teal-600
"#fdd835", // yellow-600
"#fb8c00", // orange-600
"#546e7a", // blue-grey-600
"#0d47a1", // blue-900
"#1b5e20", // green-900
"#827717", // lime-900
"#bf360c", // deep-orange-900
"#4a148c", // purple-900
"#004d40", // teal-900
"#f57f17", // yellow-900
"#e65100", // orange-900
"#263238", // blue-grey-900
],
$scoreButtonContainer = null,
/**
* Init function
* @function
* @public
*/
init = function(){
$logMessages = $('#dashboardMessages');
initLogMessages();
$showingResultsFor = $('#showingResultsFor');
$fileSelector = $('#fileSelector');
$plainFacts = $('#plainFacts');
$plainFacts2 = $('#plainFacts2');
$plainFacts3 = $('#plainFacts3');
$overallStatistics = $('#overallStatistics');
$scoreButtonContainer = $('#score_button_container');
},
/**
* Method to initiate log messages
* @function
* @public
*/
initLogMessages = function() {
dashboardMessageCounter = 0;
$logMessages.show();
$logMessages.animate({
height: 70
}, 10);
addLogMessage('Fetching results from database ...');
},
/**
* Method to animate the log message box
* @function
* @public
*/
disposeLogMessages = function() {
window.setTimeout(function() {
$logMessages.animate({
height: 0
},
700,
function() {
$logMessages.hide();
$logMessages.empty();
});
}, 1500);
},
/**
* Method to add a log message
* @function
* @public
*
* @param {string} msg message to be added
*/
addLogMessage = function(msg) {
$('#log' + (dashboardMessageCounter - 3)).animate({
"marginTop": "-30px"
}, 200);
$logMessages.append('<div id="log' + dashboardMessageCounter + '"></div>');
$('#log' + dashboardMessageCounter).typed({
strings: ['<p>' + msg + '</p>'],
backDelay: 100000000000000,
typeSpeed: 0,
backSpeed: 0,
loop: true,
});
dashboardMessageCounter++;
},
/**
* Method to initate the file selector on dashboard
* @function
* @public
*
* @param {array} data array containing information to user uploaded files
*/
initFileSelector = function(data) {
$fileSelector.empty();
var selectorElement = '<select class="form-control btn-material-blue-grey-100" name="fileSelector">';
selectorElement += '<option value="all" class="btn-material-blue-grey-100"> - All - </option>';
var showingResultsForText = '<h4>Showing results for:</h4>';
for (var i = 0; i < data.length; i++) {
selectorElement += '<option value="';
selectorElement += data[i].id;
selectorElement += '" class="btn-material-blue-grey-100">';
selectorElement += data[i].value.artist;
selectorElement += ' - ';
selectorElement += data[i].value.title;
selectorElement += ' (';
selectorElement += /[^/]*$/.exec(data[i].value.file_url)[0];
selectorElement += ')';
selectorElement += '</option>';
}
selectorElement += '</select>';
$showingResultsFor.append(showingResultsForText);
$fileSelector.append(selectorElement);
$fileSelector.find('select').on('change', onFileSelectorChange);
},
/**
* Method description
* @function
* @public
*
* @param {Event} event The triggered event
*/
onFileSelectorChange = function(event) {
$(that).trigger('onFileSelectorChange', [ $fileSelector.find('select').val() ]);
},
/**
* Method to append number of notes to html-view
* @function
* @public
*
* @param {int} results number of total notes
*/
initCountNotes = function(results){
$overallStatistics.empty();
$overallStatistics.append('<h3 class="text-center">Overall statistics</h3>');
$plainFacts.empty();
$plainFacts.append('<li><strong>Total notes: </strong>' + results + '</li>');
$plainFacts.find('li').on('change', onFileSelectorChange);
},
/**
* Method to append number of rests to html-view
* @function
* @public
*
* @param {int} results number of total rests
*/
initCountRests = function(results){
$plainFacts.append('<li><strong>Total rests: </strong>' + results + '</li>');
},
/**
* Method to append number of measures to html-view
* @function
* @public
*
* @param {int} results number of measures notes
*/
initCountMeasures = function(results){
$plainFacts2.empty();
$plainFacts2.append('<li><strong>Total measures: </strong>' + results + '</li>');
},
/**
* Method to append most frequent to html-view
* @function
* @public
*
* @param {string} results most frequent note
*/
initMostFrequentNote = function(results){
$plainFacts2.append('<li><strong>Most frequent note: </strong>' + results + '</li>');
},
/**
* Method to append instruments to html-view
* @function
* @public
*
* @param {array} results array containing all instruments
*/
initInstruments = function(results) {
$plainFacts3.empty();
$plainFacts3.append('<li><strong>Instruments: </strong></li>');
$plainFacts3.append("<li>" + results.join(", ") + "</li>");
},
/**
* Method to create barchart representing the note distribution
* @function
* @public
*
* @param {object} data objet containing information about the distribution of notes
*/
initNoteDistribution = function(data) {
$('#bar_noteDistribution').empty();
var containerWidth = $('#bar_noteDistribution').width() - 30;
var margin ={ top:20, right:30, bottom:50, left: 40 },
width = containerWidth - margin.left - margin.right,
height= 300 - margin.top - margin.bottom +30;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
// key on x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
// key on y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#bar_noteDistribution")
.append("svg") //append svg element inside #bar_noteDistribution
.attr("width", width+(2*margin.left)+margin.right) //set width
.attr("height", height+margin.top+margin.bottom) //set height
.append("g")
.attr("transform", "translate(" + margin.left + ",50)");
// transform data object
x.domain(data.map(function(d){ return d.label; }));
y.domain([0, d3.max(data, function(d){return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0,"+ height+")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Count");
svg.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2) -20)
.attr("text-anchor", "middle")
.style("font-size", "23px")
.style("font-weight", 300)
.style("font-family", 'RobotoDraft','Roboto','Helvetica Neue','Helvetica','Arial','sans-serif')
.text("Note distribution");
svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.label); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
//.on("click", tip.show);
svg.selectAll("text.bar")
.data(data)
.enter()
.append("text")
.attr("class", "bar-value")
.attr("x", function(d) { return x(d.label); })
.attr("y", function(d) { return y(d.value); })
.text(function(d) { return d.value; })
.attr("dx", "2.5em")
.attr("dy", "-.5em");
},
/**
* Method to create barchart representing the interval distribution
* @function
* @public
*
* @param {object} data objet containing information about the distribution of intervals
*/
initIntervalDistribution = function(data) {
$('#bar_intervalDistribution').empty();
var containerWidth = $('#bar_intervalDistribution').width() - 30;
var margin ={ top:20, right:30, bottom:130, left: 40 },
width = containerWidth - margin.left - margin.right,
height= 300 - margin.top - margin.bottom+80;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
// key on x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
// key on y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#bar_intervalDistribution")
.append("svg") //append svg element inside #bar_intervalDistribution
.attr("width", width+(2*margin.left)+margin.right) //set width
.attr("height", height+margin.top+margin.bottom) //set height
.append("g")
.attr("transform", "translate(" + margin.left + ",50)");
// transform data object
x.domain(data.map(function(d){ return d.label; }));
y.domain([0, d3.max(data, function(d){return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0,"+ height+")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "start")
.attr("transform", "rotate(45)");
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Count");
svg.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2) - 20)
.attr("text-anchor", "middle")
.style("font-size", "23px")
.style("font-weight", 300)
.style("font-family", 'RobotoDraft','Roboto','Helvetica Neue','Helvetica','Arial','sans-serif')
.text("Interval distribution");
svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.label); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
svg.selectAll("text.bar")
.data(data)
.enter()
.append("text")
.attr("class", "bar-value")
.attr("x", function(d) { return x(d.label); })
.attr("y", function(d) { return y(d.value); })
.text(function(d) { return d.value; })
.attr("dx", "1em")
.attr("dy", "-.5em");
},
/**
* Method to create piechart representing the key distribution
* @function
* @public
*
* @param {object} data objet containing information about the distribution of keys
*/
initKeyDistribution = function(data) {
if (keyDistribution) {
keyDistribution.destroy();
}
keyDistribution = new d3pie("pie_keyDistribution", {
header: {
title: {
text: "Key distribution"
}
},
data: {
content: data
},
tooltips: {
enabled: true,
type: "placeholder",
string: "{label}: ({value}) {percentage}%",
placeholderParser: function(index, data) {
data.label = data.label + " ";
data.percentage = data.percentage;
data.value = data.value;
}
},
misc: {
colors: {
segments: segmentColors
}
}
});
},
/**
* Method to create piechart representing the note-length distribution
* @function
* @public
*
* @param {object} data objet containing information about the distribution of note-lengths
*/
initNoteTypeDistribution = function(data) {
if (noteTypeDistribution) {
noteTypeDistribution.destroy();
}
noteTypeDistribution = new d3pie("pie_noteTypeDistribution", {
header: {
title: {
text: "Note duration"
}
},
data: {
content: data
},
tooltips: {
enabled: true,
type: "placeholder",
string: "{label}: ({value}) {percentage}%",
placeholderParser: function(index, data) {
data.label = data.label + " ";
data.percentage = data.percentage;
data.value = data.value;
}
},
misc: {
colors: {
segments: segmentColors
}
}
});
},
/**
* Method to create piechart representing the meter distribution
* @function
* @public
*
* @param {object} data objet containing information about the distribution of meters
*/
initMeterDistribution = function(data) {
var data2;
if(typeof(data) == 'string'){
data2 = [{label: data, value: 1}];
}else{
data2 = data;
}
if (meterDistribution) {
meterDistribution.destroy();
}
meterDistribution = new d3pie("pie_meterDistribution", {
header: {
title: {
text: "Meters"
}
},
data: {
content: data2
},
tooltips: {
enabled: true,
type: "placeholder",
string: "{label}: ({value}) {percentage}%",
placeholderParser: function(index, data) {
data.label = data.label + " ";
data.percentage = data.percentage;
data.value = data.value;
}
},
misc: {
colors: {
segments: segmentColors
}
}
});
},
initScoreButton = function(id) {
$scoreButtonContainer.empty();
if (id !== 'all') {
console.log("initScoreButton", id);
var button = '<a class="btn btn-success" target="_blank" href="/score/' + id + '" onclick="ga(\'send\', \'event\', { eventCategory: \'Dashboard: View Score\', eventAction: \'Click\' })"><span>Show Score</span></a>';
$scoreButtonContainer.append(button);
}
};
that.init = init;
that.disposeLogMessages = disposeLogMessages;
that.addLogMessage = addLogMessage;
that.initFileSelector = initFileSelector;
that.initNoteDistribution = initNoteDistribution;
that.initIntervalDistribution = initIntervalDistribution;
that.initKeyDistribution = initKeyDistribution;
that.initNoteTypeDistribution = initNoteTypeDistribution;
that.initMeterDistribution = initMeterDistribution;
that.initCountNotes = initCountNotes;
that.initCountRests = initCountRests;
that.initCountMeasures = initCountMeasures;
that.initMostFrequentNote = initMostFrequentNote;
that.initInstruments = initInstruments;
that.initScoreButton = initScoreButton;
return that;
}