From 6db8b795298b73a5409e89c02fb11c0bfa0a11b4 Mon Sep 17 00:00:00 2001
From: Jaden Diefenbaugh <blakcap@users.noreply.github.com>
Date: Wed, 22 Feb 2017 13:40:40 +0100
Subject: [PATCH] whitespace cleaning experiment utils

---
 .../static/experiments/js/utils.js            | 3738 ++++++++---------
 1 file changed, 1869 insertions(+), 1869 deletions(-)

diff --git a/beat/web/experiments/static/experiments/js/utils.js b/beat/web/experiments/static/experiments/js/utils.js
index ac71620ef..12b27a8cc 100644
--- a/beat/web/experiments/static/experiments/js/utils.js
+++ b/beat/web/experiments/static/experiments/js/utils.js
@@ -18,22 +18,22 @@
  *
  * You should have received a copy of the GNU Affero Public License along
  * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
-*/
+ */
 /* beat.experiments.utils.js
 
    Experiment-related utility classes and functions
-*/
+   */
 
 
 // Declaration of our namespaces
 if (beat === undefined)
-    var beat = {}
+	var beat = {}
 
 if (beat.experiments === undefined)
-    beat.experiments = {}
+	beat.experiments = {}
 
 if (beat.experiments.utils === undefined)
-    beat.experiments.utils = {}
+	beat.experiments.utils = {}
 
 
 //------------------------------------------------------------------------------
@@ -59,439 +59,439 @@ if (beat.experiments.utils === undefined)
 // }
 //------------------------------------------------------------------------------
 beat.experiments.utils.displayPlot = function(prefix, container, value, available_plotters,
-                                              available_plotter_parameter, replace_container_content, callback)
+	available_plotter_parameter, replace_container_content, callback)
 {
-    var table_element = null;
-    var state_merged = value.merged;
-    if(state_merged == undefined)
-        state_merged = true;
-
-    // Function to regenerate graph
-    function _regenerate_graph(plotter_selector)
-    {
-        var data = JSON.parse(JSON.stringify(value));
-
-        data.content_type = 'image/png';
-        data.base64 = true;
-
-        $.ajaxSetup({
-            beforeSend: function(xhr, settings) {
-                var csrftoken = $.cookie('csrftoken');
-                xhr.setRequestHeader('X-CSRFToken', csrftoken);
-            }
-        });
-
-        $.ajax({
-            type: "POST",
-            url: prefix + '/plotters/plot/',
-            data: data,
-
-            success: function(data) {
-                if (replace_container_content)
-                {
-                    while (container.firstChild)
-                        container.removeChild(container.firstChild);
-                }
-
-                if (table_element !== null)
-                    $(table_element).remove();
-
-                // adds table that will have the two elements
-                table_element = document.createElement('table');
-                table_element.className = 'plotter';
-                container.appendChild(table_element);
-
-                var tr = document.createElement('tr');
-                table_element.appendChild(tr);
-
-                var td = document.createElement('td');
-                td.appendChild(plotter_selector);
-                tr.appendChild(td);
-
-                // adds image place holder
-                var img = document.createElement('img');
-                img.id = container.id + '_graph';
-                //img.width = value['width'];
-                //img.height = value['height'];
-                img.className = 'img-responsive';
-                img.src = "data:" + value['content_type'] + ";base64," + data;
-
-                tr = document.createElement('tr');
-                table_element.appendChild(tr);
-
-                td = document.createElement('td');
-                td.appendChild(img);
-                tr.appendChild(td);
-
-                $(container).removeClass('progress');
-            },
-
-            statusCode: {
-                404: function(data, status, message) {
-                    container.textContent = message + ' (' + data.status + ')';
-                    $(container).addClass('error');
-                    $(container).removeClass('progress');
-                },
-                500: function(data, status, message) {
-                    container.textContent = message + ' (' + data.status + ')';
-                    $(container).addClass('error');
-                    $(container).removeClass('progress');
-                },
-            },
-
-        });
-
-    }
-
-    // Function to regenerate multiple graphs
-    function _regenerate_multiple_graph(plotter_selector)
-    {
-        var data = JSON.parse(JSON.stringify(value));
-        data.content_type = 'image/png';
-        data.base64 = true;
-
-        // create the containers
-        if (replace_container_content)
-        {
-            while (container.firstChild)
-                container.removeChild(container.firstChild);
-        }
-
-        if (table_element !== null)
-            $(table_element).remove();
-
-        // adds table that will have the two elements
-        table_element = document.createElement('table');
-        table_element.className = 'plotter';
-        container.appendChild(table_element);
-
-        var tr = document.createElement('tr');
-        table_element.appendChild(tr);
-
-        var td = document.createElement('td');
-        td.appendChild(plotter_selector);
-        tr.appendChild(td);
-
-        $.ajaxSetup({
-            beforeSend: function(xhr, settings) {
-                var csrftoken = $.cookie('csrftoken');
-                xhr.setRequestHeader('X-CSRFToken', csrftoken);
-            }
-        });
-
-        function getPlot(_data)
-        {
-            $.ajax({
-                type: "POST",
-                url: prefix + '/plotters/plot/',
-                data: _data,
-
-                success: function(data) {
-
-                    if(_data.experiment.length == 1)
-                    {
-                        var tr_title = document.createElement('tr');
-                        table_element.appendChild(tr_title);
-
-                        var td_title = document.createElement('td');
-                        td_title.className = "td_title"
-                        td_title.textContent = _data.legend;
-                        tr_title.appendChild(td_title);
-                    }
-                    // adds image place holder
-                    var img = document.createElement('img');
-                    img.id = container.id + '_graph';
-                    //img.width = value['width'];
-                    //img.height = value['height'];
-                    img.className = 'img-responsive  chart';
-                    img.src = "data:" + value['content_type'] + ";base64," + data;
-
-                    tr = document.createElement('tr');
-                    table_element.appendChild(tr);
-
-                    td = document.createElement('td');
-                    td.appendChild(img);
-                    tr.appendChild(td);
-
-                    $(container).removeClass('progress');
-                },
-
-                statusCode: {
-                    404: function(data, status, message) {
-                        container.textContent = message + ' (' + data.status + ')';
-                        $(container).addClass('error');
-                        $(container).removeClass('progress');
-                    },
-                    500: function(data, status, message) {
-                        container.textContent = message + ' (' + data.status + ')';
-                        $(container).addClass('error');
-                        $(container).removeClass('progress');
-                    },
-                },
-
-            });
-        }
-
-        if(!state_merged)
-        {
-            for(var i = 0; i < data.experiment.length; ++i)
-            {
-                // This step is required to copy elements from object in javascript!
-                // if we do a basic copy using "=" we have a reference to the object (pointer)
-                var data_per_experiment = JSON.parse(JSON.stringify(data));
-
-                data_per_experiment.experiment = [data.experiment[i]];
-                if(data.legend != undefined)
-                {
-                    data_per_experiment.legend = data.legend.split("&")[i];
-                }
-                else
-                {
-                    data_per_experiment.legend = data.experiment[i];
-                }
-
-                getPlot(data_per_experiment);
-            }
-        }
-        else
-        {
-            getPlot(data);
-        }
-
-    }
-
-
-    // Creates a selector box for chart plotters and parameters
-    function _create_selector(what, selected, options, other_what, other_selected, other_options)
-    {
-        var span = document.createElement('span');
-
-        if (options.length > 1)
-        {
-            var title = document.createElement('span');
-            //title.innerHTML = 'Plotter: ';
-            title.innerHTML = 'Plotter: ';
-            span.appendChild(title);
-
-            var selector = document.createElement('select');
-            selector.id = container.id + '_settings_select';
-            selector.className = 'settings_select';
-            span.appendChild(selector);
-
-            for (var i = 0; i < options.length; ++i)
-            {
-                var opt = document.createElement('option');
-                opt.value = options[i];
-                opt.innerHTML = options[i];
-
-                if (options[i] === selected)
-                    opt.selected = true;
-
-                selector.appendChild(opt);
-            }
-
-            $(selector).change(function() {
-                value[what] = $(selector).val();
-
-                var plotter_selector = _create_selector(what, value[what], available_plotters, other_what,
-                                                        value[other_what], available_plotter_parameter);
-                _regenerate_graph(plotter_selector);
-
-                if (callback)
-                    callback($(selector).val(), value[other_what]);
-            });
-        }
-
-        if (other_options.length > 1)
-        {
-            var title = document.createElement('span');
-            //title.innerHTML = 'Plotter: ';
-            title.innerHTML = ' Plotter parameter: ';
-            span.appendChild(title);
-
-            var selector_plotterparameter = document.createElement('select');
-            selector_plotterparameter.id = container.id + '_settings_select_plotterparameter';
-            selector_plotterparameter.className = 'settings_select_plotterparameter';
-            span.appendChild(selector_plotterparameter);
-
-            for (var i = 0; i < other_options.length; ++i)
-            {
-                var opt = document.createElement('option');
-                opt.value = other_options[i];
-                opt.innerHTML = other_options[i];
-
-                if (other_options[i] === other_selected)
-                {
-                    opt.selected = true;
-                }
-
-                selector_plotterparameter.appendChild(opt);
-            }
-
-            $(selector_plotterparameter).change(function() {
-                value[other_what] = $(selector_plotterparameter).val();
-
-                var plotter_selector = _create_selector(what, value[what], available_plotters, other_what,
-                                                        value[other_what], available_plotter_parameter);
-                _regenerate_graph(plotter_selector);
-
-                if (callback)
-                    callback(value[what], $(selector_plotterparameter).val());
-            });
-        }
-
-        return span;
-    }
-
-    // Creates a selector box for chart plotters and parameters
-    function _create_selector_with_merge_button(what, selected, options, other_what, other_selected, other_options, multiple_experiments_flag, expand_plots)
-    {
-        var span = document.createElement('span');
-
-        if (options.length > 1)
-        {
-            var title = document.createElement('span');
-            //title.innerHTML = 'Plotter: ';
-            title.innerHTML = 'Plotter: ';
-            span.appendChild(title);
-
-            var selector = document.createElement('select');
-            selector.id = container.id + '_settings_select';
-            selector.className = 'settings_select';
-            span.appendChild(selector);
-
-            for (var i = 0; i < options.length; ++i)
-            {
-                var opt = document.createElement('option');
-                opt.value = options[i];
-                opt.innerHTML = options[i];
-
-                if (options[i] === selected)
-                    opt.selected = true;
-
-                selector.appendChild(opt);
-            }
-
-            $(selector).change(function() {
-                value[what] = $(selector).val();
-
-                var plotter_selector = null;
-                if(!multiple_experiments_flag)
-                    plotter_selector = _create_selector_with_merge_button(what, value[what], available_plotters, other_what,
-                                                        value[other_what], available_plotter_parameter, false);
-                else
-                    plotter_selector = _create_selector_with_merge_button(what, value[what], available_plotters, other_what,
-                                                        value[other_what], available_plotter_parameter, true);
-                _regenerate_multiple_graph(plotter_selector);
-
-                if (callback)
-                    callback($(selector).val(), value[other_what]);
-            });
-        }
-
-        if (other_options.length > 1)
-        {
-            var title = document.createElement('span');
-            //title.innerHTML = 'Plotter: ';
-            title.innerHTML = ' Plotter parameter: ';
-            span.appendChild(title);
-
-            var selector_plotterparameter = document.createElement('select');
-            selector_plotterparameter.id = container.id + '_settings_select_plotterparameter';
-            selector_plotterparameter.className = 'settings_select_plotterparameter';
-            span.appendChild(selector_plotterparameter);
-
-            for (var i = 0; i < other_options.length; ++i)
-            {
-                var opt = document.createElement('option');
-                opt.value = other_options[i];
-                opt.innerHTML = other_options[i];
-
-                if (other_options[i] === other_selected)
-                {
-                    opt.selected = true;
-                }
-
-                selector_plotterparameter.appendChild(opt);
-            }
-
-            $(selector_plotterparameter).change(function() {
-                value[other_what] = $(selector_plotterparameter).val();
-
-                var plotter_selector = null;
-                if(!multiple_experiments_flag)
-                    plotter_selector = _create_selector_with_merge_button(what, value[what], available_plotters, other_what,
-                                                        value[other_what], available_plotter_parameter, false);
-                else
-                    plotter_selector = _create_selector_with_merge_button(what, value[what], available_plotters, other_what,
-                                                        value[other_what], available_plotter_parameter, true, !value["merged"]);
-                _regenerate_multiple_graph(plotter_selector);
-
-                if (callback)
-                    callback(value[what], $(selector_plotterparameter).val(), value["merged"]);
-            });
-
-        var button_merge = document.createElement('a');
-        button_merge.id = container.id + '_button_merge_a';
-
-        if(expand_plots)
-        {
-            button_merge.className = 'btn btn-xs btn-primary merge_unmerge expand';
-            button_merge.innerHTML = "<i class='fa fa-compress fa-lg'></i> Merge";
-        }
-        else
-        {
-            button_merge.className = 'btn btn-xs btn-primary merge_unmerge merge';
-            button_merge.innerHTML = "<i class='fa fa-expand fa-lg'></i> Expand";
-        }
-        span.appendChild(button_merge);
-
-        $(button_merge).click(function() {
-            if(! $(button_merge).hasClass("expand"))
-            {
-                $(button_merge).addClass("expand");
-                $(button_merge).removeClass("merge");
-                $(button_merge).children().removeClass("fa-compress");
-                $(button_merge).children().addClass("fa-expand");
-                var plotter_selector = _create_selector_with_merge_button("plotter", value["plotter"], available_plotters, "parameter", value["parameter"], available_plotter_parameter, true, true);
-                state_merged = false;
-            }
-            else
-            {
-                $(button_merge).addClass("merge");
-                $(button_merge).removeClass("expand");
-                $(button_merge).children().removeClass("fa-expand");
-                $(button_merge).children().addClass("fa-compress");
-                var plotter_selector = _create_selector_with_merge_button("plotter", value["plotter"], available_plotters, "parameter", value["parameter"], available_plotter_parameter, true, false);
-                state_merged = true;
-            }
-            value.merged = state_merged;
-            _regenerate_multiple_graph(plotter_selector);
-            if (callback)
-                callback(value["plotter"], value["parameter"], value["merged"]);
-                //callback(value["plotter"], $(plotter_selector).val());
-
-        });
-
-
-        }
-
-        return span;
-    }
-
-    if(Array.isArray(value.experiment) && value.experiment.length > 1)
-    {
-        //creates button merge/unmerge and plotter/plotter_parameter selector
-        var plotter_selector = _create_selector_with_merge_button("plotter", value["plotter"], available_plotters, "parameter", value["parameter"], available_plotter_parameter, true, !state_merged);
-        _regenerate_multiple_graph(plotter_selector);
-    }
-    else
-    {
-        // creates plotter/plotter_parameter selector
-        var plotter_selector = _create_selector("plotter", value["plotter"], available_plotters, "parameter", value["parameter"], available_plotter_parameter);
-        _regenerate_graph(plotter_selector);
-
-    }
+	var table_element = null;
+	var state_merged = value.merged;
+	if(state_merged == undefined)
+		state_merged = true;
+
+	// Function to regenerate graph
+	function _regenerate_graph(plotter_selector)
+	{
+		var data = JSON.parse(JSON.stringify(value));
+
+		data.content_type = 'image/png';
+		data.base64 = true;
+
+		$.ajaxSetup({
+			beforeSend: function(xhr, settings) {
+				var csrftoken = $.cookie('csrftoken');
+				xhr.setRequestHeader('X-CSRFToken', csrftoken);
+			}
+		});
+
+		$.ajax({
+			type: "POST",
+			url: prefix + '/plotters/plot/',
+			data: data,
+
+			success: function(data) {
+				if (replace_container_content)
+				{
+					while (container.firstChild)
+						container.removeChild(container.firstChild);
+				}
+
+				if (table_element !== null)
+					$(table_element).remove();
+
+				// adds table that will have the two elements
+				table_element = document.createElement('table');
+				table_element.className = 'plotter';
+				container.appendChild(table_element);
+
+				var tr = document.createElement('tr');
+				table_element.appendChild(tr);
+
+				var td = document.createElement('td');
+				td.appendChild(plotter_selector);
+				tr.appendChild(td);
+
+				// adds image place holder
+				var img = document.createElement('img');
+				img.id = container.id + '_graph';
+				//img.width = value['width'];
+				//img.height = value['height'];
+				img.className = 'img-responsive';
+				img.src = "data:" + value['content_type'] + ";base64," + data;
+
+				tr = document.createElement('tr');
+				table_element.appendChild(tr);
+
+				td = document.createElement('td');
+				td.appendChild(img);
+				tr.appendChild(td);
+
+				$(container).removeClass('progress');
+			},
+
+			statusCode: {
+				404: function(data, status, message) {
+					container.textContent = message + ' (' + data.status + ')';
+					$(container).addClass('error');
+					$(container).removeClass('progress');
+				},
+				500: function(data, status, message) {
+					container.textContent = message + ' (' + data.status + ')';
+					$(container).addClass('error');
+					$(container).removeClass('progress');
+				},
+			},
+
+		});
+
+	}
+
+	// Function to regenerate multiple graphs
+	function _regenerate_multiple_graph(plotter_selector)
+	{
+		var data = JSON.parse(JSON.stringify(value));
+		data.content_type = 'image/png';
+		data.base64 = true;
+
+		// create the containers
+		if (replace_container_content)
+		{
+			while (container.firstChild)
+				container.removeChild(container.firstChild);
+		}
+
+		if (table_element !== null)
+			$(table_element).remove();
+
+		// adds table that will have the two elements
+		table_element = document.createElement('table');
+		table_element.className = 'plotter';
+		container.appendChild(table_element);
+
+		var tr = document.createElement('tr');
+		table_element.appendChild(tr);
+
+		var td = document.createElement('td');
+		td.appendChild(plotter_selector);
+		tr.appendChild(td);
+
+		$.ajaxSetup({
+			beforeSend: function(xhr, settings) {
+				var csrftoken = $.cookie('csrftoken');
+				xhr.setRequestHeader('X-CSRFToken', csrftoken);
+			}
+		});
+
+		function getPlot(_data)
+		{
+			$.ajax({
+				type: "POST",
+				url: prefix + '/plotters/plot/',
+				data: _data,
+
+				success: function(data) {
+
+					if(_data.experiment.length == 1)
+					{
+						var tr_title = document.createElement('tr');
+						table_element.appendChild(tr_title);
+
+						var td_title = document.createElement('td');
+						td_title.className = "td_title"
+						td_title.textContent = _data.legend;
+						tr_title.appendChild(td_title);
+					}
+					// adds image place holder
+					var img = document.createElement('img');
+					img.id = container.id + '_graph';
+					//img.width = value['width'];
+					//img.height = value['height'];
+					img.className = 'img-responsive  chart';
+					img.src = "data:" + value['content_type'] + ";base64," + data;
+
+					tr = document.createElement('tr');
+					table_element.appendChild(tr);
+
+					td = document.createElement('td');
+					td.appendChild(img);
+					tr.appendChild(td);
+
+					$(container).removeClass('progress');
+				},
+
+				statusCode: {
+					404: function(data, status, message) {
+						container.textContent = message + ' (' + data.status + ')';
+						$(container).addClass('error');
+						$(container).removeClass('progress');
+					},
+					500: function(data, status, message) {
+						container.textContent = message + ' (' + data.status + ')';
+						$(container).addClass('error');
+						$(container).removeClass('progress');
+					},
+				},
+
+			});
+		}
+
+		if(!state_merged)
+		{
+			for(var i = 0; i < data.experiment.length; ++i)
+			{
+				// This step is required to copy elements from object in javascript!
+				// if we do a basic copy using "=" we have a reference to the object (pointer)
+				var data_per_experiment = JSON.parse(JSON.stringify(data));
+
+				data_per_experiment.experiment = [data.experiment[i]];
+				if(data.legend != undefined)
+				{
+					data_per_experiment.legend = data.legend.split("&")[i];
+				}
+				else
+				{
+					data_per_experiment.legend = data.experiment[i];
+				}
+
+				getPlot(data_per_experiment);
+			}
+		}
+		else
+		{
+			getPlot(data);
+		}
+
+	}
+
+
+	// Creates a selector box for chart plotters and parameters
+	function _create_selector(what, selected, options, other_what, other_selected, other_options)
+	{
+		var span = document.createElement('span');
+
+		if (options.length > 1)
+		{
+			var title = document.createElement('span');
+			//title.innerHTML = 'Plotter: ';
+			title.innerHTML = 'Plotter: ';
+			span.appendChild(title);
+
+			var selector = document.createElement('select');
+			selector.id = container.id + '_settings_select';
+			selector.className = 'settings_select';
+			span.appendChild(selector);
+
+			for (var i = 0; i < options.length; ++i)
+			{
+				var opt = document.createElement('option');
+				opt.value = options[i];
+				opt.innerHTML = options[i];
+
+				if (options[i] === selected)
+					opt.selected = true;
+
+				selector.appendChild(opt);
+			}
+
+			$(selector).change(function() {
+				value[what] = $(selector).val();
+
+				var plotter_selector = _create_selector(what, value[what], available_plotters, other_what,
+					value[other_what], available_plotter_parameter);
+				_regenerate_graph(plotter_selector);
+
+				if (callback)
+					callback($(selector).val(), value[other_what]);
+			});
+		}
+
+		if (other_options.length > 1)
+		{
+			var title = document.createElement('span');
+			//title.innerHTML = 'Plotter: ';
+			title.innerHTML = ' Plotter parameter: ';
+			span.appendChild(title);
+
+			var selector_plotterparameter = document.createElement('select');
+			selector_plotterparameter.id = container.id + '_settings_select_plotterparameter';
+			selector_plotterparameter.className = 'settings_select_plotterparameter';
+			span.appendChild(selector_plotterparameter);
+
+			for (var i = 0; i < other_options.length; ++i)
+			{
+				var opt = document.createElement('option');
+				opt.value = other_options[i];
+				opt.innerHTML = other_options[i];
+
+				if (other_options[i] === other_selected)
+				{
+					opt.selected = true;
+				}
+
+				selector_plotterparameter.appendChild(opt);
+			}
+
+			$(selector_plotterparameter).change(function() {
+				value[other_what] = $(selector_plotterparameter).val();
+
+				var plotter_selector = _create_selector(what, value[what], available_plotters, other_what,
+					value[other_what], available_plotter_parameter);
+				_regenerate_graph(plotter_selector);
+
+				if (callback)
+					callback(value[what], $(selector_plotterparameter).val());
+			});
+		}
+
+		return span;
+	}
+
+	// Creates a selector box for chart plotters and parameters
+	function _create_selector_with_merge_button(what, selected, options, other_what, other_selected, other_options, multiple_experiments_flag, expand_plots)
+	{
+		var span = document.createElement('span');
+
+		if (options.length > 1)
+		{
+			var title = document.createElement('span');
+			//title.innerHTML = 'Plotter: ';
+			title.innerHTML = 'Plotter: ';
+			span.appendChild(title);
+
+			var selector = document.createElement('select');
+			selector.id = container.id + '_settings_select';
+			selector.className = 'settings_select';
+			span.appendChild(selector);
+
+			for (var i = 0; i < options.length; ++i)
+			{
+				var opt = document.createElement('option');
+				opt.value = options[i];
+				opt.innerHTML = options[i];
+
+				if (options[i] === selected)
+					opt.selected = true;
+
+				selector.appendChild(opt);
+			}
+
+			$(selector).change(function() {
+				value[what] = $(selector).val();
+
+				var plotter_selector = null;
+				if(!multiple_experiments_flag)
+					plotter_selector = _create_selector_with_merge_button(what, value[what], available_plotters, other_what,
+						value[other_what], available_plotter_parameter, false);
+				else
+					plotter_selector = _create_selector_with_merge_button(what, value[what], available_plotters, other_what,
+						value[other_what], available_plotter_parameter, true);
+				_regenerate_multiple_graph(plotter_selector);
+
+				if (callback)
+					callback($(selector).val(), value[other_what]);
+			});
+		}
+
+		if (other_options.length > 1)
+		{
+			var title = document.createElement('span');
+			//title.innerHTML = 'Plotter: ';
+			title.innerHTML = ' Plotter parameter: ';
+			span.appendChild(title);
+
+			var selector_plotterparameter = document.createElement('select');
+			selector_plotterparameter.id = container.id + '_settings_select_plotterparameter';
+			selector_plotterparameter.className = 'settings_select_plotterparameter';
+			span.appendChild(selector_plotterparameter);
+
+			for (var i = 0; i < other_options.length; ++i)
+			{
+				var opt = document.createElement('option');
+				opt.value = other_options[i];
+				opt.innerHTML = other_options[i];
+
+				if (other_options[i] === other_selected)
+				{
+					opt.selected = true;
+				}
+
+				selector_plotterparameter.appendChild(opt);
+			}
+
+			$(selector_plotterparameter).change(function() {
+				value[other_what] = $(selector_plotterparameter).val();
+
+				var plotter_selector = null;
+				if(!multiple_experiments_flag)
+					plotter_selector = _create_selector_with_merge_button(what, value[what], available_plotters, other_what,
+						value[other_what], available_plotter_parameter, false);
+				else
+					plotter_selector = _create_selector_with_merge_button(what, value[what], available_plotters, other_what,
+						value[other_what], available_plotter_parameter, true, !value["merged"]);
+				_regenerate_multiple_graph(plotter_selector);
+
+				if (callback)
+					callback(value[what], $(selector_plotterparameter).val(), value["merged"]);
+			});
+
+			var button_merge = document.createElement('a');
+			button_merge.id = container.id + '_button_merge_a';
+
+			if(expand_plots)
+			{
+				button_merge.className = 'btn btn-xs btn-primary merge_unmerge expand';
+				button_merge.innerHTML = "<i class='fa fa-compress fa-lg'></i> Merge";
+			}
+			else
+			{
+				button_merge.className = 'btn btn-xs btn-primary merge_unmerge merge';
+				button_merge.innerHTML = "<i class='fa fa-expand fa-lg'></i> Expand";
+			}
+			span.appendChild(button_merge);
+
+			$(button_merge).click(function() {
+				if(! $(button_merge).hasClass("expand"))
+				{
+					$(button_merge).addClass("expand");
+					$(button_merge).removeClass("merge");
+					$(button_merge).children().removeClass("fa-compress");
+					$(button_merge).children().addClass("fa-expand");
+					var plotter_selector = _create_selector_with_merge_button("plotter", value["plotter"], available_plotters, "parameter", value["parameter"], available_plotter_parameter, true, true);
+					state_merged = false;
+				}
+				else
+				{
+					$(button_merge).addClass("merge");
+					$(button_merge).removeClass("expand");
+					$(button_merge).children().removeClass("fa-expand");
+					$(button_merge).children().addClass("fa-compress");
+					var plotter_selector = _create_selector_with_merge_button("plotter", value["plotter"], available_plotters, "parameter", value["parameter"], available_plotter_parameter, true, false);
+					state_merged = true;
+				}
+				value.merged = state_merged;
+				_regenerate_multiple_graph(plotter_selector);
+				if (callback)
+					callback(value["plotter"], value["parameter"], value["merged"]);
+				//callback(value["plotter"], $(plotter_selector).val());
+
+			});
+
+
+		}
+
+		return span;
+	}
+
+	if(Array.isArray(value.experiment) && value.experiment.length > 1)
+	{
+		//creates button merge/unmerge and plotter/plotter_parameter selector
+		var plotter_selector = _create_selector_with_merge_button("plotter", value["plotter"], available_plotters, "parameter", value["parameter"], available_plotter_parameter, true, !state_merged);
+		_regenerate_multiple_graph(plotter_selector);
+	}
+	else
+	{
+		// creates plotter/plotter_parameter selector
+		var plotter_selector = _create_selector("plotter", value["plotter"], available_plotters, "parameter", value["parameter"], available_plotter_parameter);
+		_regenerate_graph(plotter_selector);
+
+	}
 
 }
 
@@ -500,89 +500,89 @@ beat.experiments.utils.displayPlot = function(prefix, container, value, availabl
 
 
 beat.experiments.utils.getPlotData = function(prefix, value, available_plotters,
-                                              available_plotter_parameter, content_type, callback)
+	available_plotter_parameter, content_type, callback)
 {
-    var selected_content_type = "";
-    if(content_type == 'PNG')
-    {
-        selected_content_type = 'image/png';
-    }
-    else if(content_type == 'JPEG')
-    {
-        selected_content_type = 'image/jpeg';
-    }
-    else if(content_type == 'PDF')
-    {
-        selected_content_type = 'application/pdf';
-    }
-
-    var state_merged = value.merged;
-    if(state_merged == undefined)
-        state_merged = true;
-
-
-    var data = JSON.parse(JSON.stringify(value));
-    data.content_type = selected_content_type;
-    data.base64 = true;
-
-    // Function to regenerate graph
-    function _fetch_graph(_data)
-    {
-        $.ajaxSetup({
-            beforeSend: function(xhr, settings) {
-                var csrftoken = $.cookie('csrftoken');
-                xhr.setRequestHeader('X-CSRFToken', csrftoken);
-            }
-        });
-
-        var content_type = selected_content_type;
-        $.ajax({
-            type: "GET",
-            url: prefix + '/plotters/plot/',
-            data: _data,
-
-            success: function(data) {
-
-                var returned_data = "data:" + content_type + ";base64," + data;
-                if (callback)
-                    callback(returned_data, content_type);
-
-            },
-
-            statusCode: {
-                404: function(data, status, message) {
-                    container.textContent = message + ' (' + data.status + ')';
-                    $(container).addClass('error');
-                    $(container).removeClass('progress');
-                },
-                500: function(data, status, message) {
-                    container.textContent = message + ' (' + data.status + ')';
-                    $(container).addClass('error');
-                    $(container).removeClass('progress');
-                },
-            },
-
-        });
-    }
-
-    if(!state_merged)
-    {
-        for(var i = 0; i < data.experiment.length; ++i)
-        {
-            // This step is required to copy elements from object in javascript!
-            // if we do a basic copy using "=" we have a reference to the object (pointer)
-            var data_per_experiment = JSON.parse(JSON.stringify(data));
-
-            data_per_experiment.experiment = [data.experiment[i]];
-            data_per_experiment.legend = data.legend.split("&")[i];
-
-            _fetch_graph(data_per_experiment);
-        }
-    }
-    else
-    {
-        _fetch_graph(data);
-    }
+	var selected_content_type = "";
+	if(content_type == 'PNG')
+	{
+		selected_content_type = 'image/png';
+	}
+	else if(content_type == 'JPEG')
+	{
+		selected_content_type = 'image/jpeg';
+	}
+	else if(content_type == 'PDF')
+	{
+		selected_content_type = 'application/pdf';
+	}
+
+	var state_merged = value.merged;
+	if(state_merged == undefined)
+		state_merged = true;
+
+
+	var data = JSON.parse(JSON.stringify(value));
+	data.content_type = selected_content_type;
+	data.base64 = true;
+
+	// Function to regenerate graph
+	function _fetch_graph(_data)
+	{
+		$.ajaxSetup({
+			beforeSend: function(xhr, settings) {
+				var csrftoken = $.cookie('csrftoken');
+				xhr.setRequestHeader('X-CSRFToken', csrftoken);
+			}
+		});
+
+		var content_type = selected_content_type;
+		$.ajax({
+			type: "GET",
+			url: prefix + '/plotters/plot/',
+			data: _data,
+
+			success: function(data) {
+
+				var returned_data = "data:" + content_type + ";base64," + data;
+				if (callback)
+					callback(returned_data, content_type);
+
+			},
+
+			statusCode: {
+				404: function(data, status, message) {
+					container.textContent = message + ' (' + data.status + ')';
+					$(container).addClass('error');
+					$(container).removeClass('progress');
+				},
+				500: function(data, status, message) {
+					container.textContent = message + ' (' + data.status + ')';
+					$(container).addClass('error');
+					$(container).removeClass('progress');
+				},
+			},
+
+		});
+	}
+
+	if(!state_merged)
+	{
+		for(var i = 0; i < data.experiment.length; ++i)
+		{
+			// This step is required to copy elements from object in javascript!
+			// if we do a basic copy using "=" we have a reference to the object (pointer)
+			var data_per_experiment = JSON.parse(JSON.stringify(data));
+
+			data_per_experiment.experiment = [data.experiment[i]];
+			data_per_experiment.legend = data.legend.split("&")[i];
+
+			_fetch_graph(data_per_experiment);
+		}
+	}
+	else
+	{
+		_fetch_graph(data);
+	}
 }
 
 
@@ -591,40 +591,40 @@ beat.experiments.utils.getPlotData = function(prefix, value, available_plotters,
 
 beat.experiments.utils.displayResult = function(element, value, type)
 {
-    if (value === null) return;
-
-    if (type == 'float32')
-    {
-        if ((value == '+inf') || (value == '-inf') || (value == 'NaN'))
-        {
-            $(element).text('' + value);
-        }
-        else
-        {
-            var n = parseFloat(value);
-            if (n <= 1.0)
-                $(element).text('' + parseFloat((parseFloat(value).toPrecision(3))));
-            else
-                $(element).text('' + parseFloat((parseFloat(value).toFixed(3))));
-        }
-    }
-    else if (type == 'int32')
-    {
-        $(element).text('' + value);
-    }
-    else if (type == 'bool')
-    {
-        $(element).text(value ? 'True' : 'False');
-    }
-    else if (type == 'string')
-    {
-        $(element).html(value.replace(/\n/g, '<br />'));
-    }
-    else
-    {
-        $(element).text('ERROR (invalid type)');
-        element.style.color = '#FF0000';
-    }
+	if (value === null) return;
+
+	if (type == 'float32')
+	{
+		if ((value == '+inf') || (value == '-inf') || (value == 'NaN'))
+		{
+			$(element).text('' + value);
+		}
+		else
+		{
+			var n = parseFloat(value);
+			if (n <= 1.0)
+				$(element).text('' + parseFloat((parseFloat(value).toPrecision(3))));
+			else
+				$(element).text('' + parseFloat((parseFloat(value).toFixed(3))));
+		}
+	}
+	else if (type == 'int32')
+	{
+		$(element).text('' + value);
+	}
+	else if (type == 'bool')
+	{
+		$(element).text(value ? 'True' : 'False');
+	}
+	else if (type == 'string')
+	{
+		$(element).html(value.replace(/\n/g, '<br />'));
+	}
+	else
+	{
+		$(element).text('ERROR (invalid type)');
+		element.style.color = '#FF0000';
+	}
 }
 
 
@@ -633,20 +633,20 @@ beat.experiments.utils.displayResult = function(element, value, type)
 
 beat.experiments.utils.getBlockInputSignature = function(block)
 {
-    var block_inputs_list = [];
+	var block_inputs_list = [];
 
-    for (var i = 0; i < block.nbInputs(); ++i)
-    {
-        var input = block.inputs[i];
+	for (var i = 0; i < block.nbInputs(); ++i)
+	{
+		var input = block.inputs[i];
 
-        block_inputs_list.push({
-            name: input.name,
-            dataformat: input.connections[0].output.dataformat,
-            channel: input.channel,
-        });
-    }
+		block_inputs_list.push({
+			name: input.name,
+			dataformat: input.connections[0].output.dataformat,
+			channel: input.channel,
+		});
+	}
 
-    return beat.experiments.utils.getEntryPointsSignature(block_inputs_list);
+	return beat.experiments.utils.getEntryPointsSignature(block_inputs_list);
 }
 
 
@@ -655,20 +655,20 @@ beat.experiments.utils.getBlockInputSignature = function(block)
 
 beat.experiments.utils.getBlockOutputSignature = function(block)
 {
-    var block_outputs_list = [];
+	var block_outputs_list = [];
 
-    for (var i = 0; i < block.nbOutputs(); ++i)
-    {
-        var output = block.outputs[i];
+	for (var i = 0; i < block.nbOutputs(); ++i)
+	{
+		var output = block.outputs[i];
 
-        block_outputs_list.push({
-            name: output.name,
-            dataformat: (output.connections.length > 0 ? output.connections[0].input.dataformat : null),
-            channel: output.channel,
-        })
-    }
+		block_outputs_list.push({
+			name: output.name,
+			dataformat: (output.connections.length > 0 ? output.connections[0].input.dataformat : null),
+			channel: output.channel,
+		})
+	}
 
-    return beat.experiments.utils.getEntryPointsSignature(block_outputs_list);
+	return beat.experiments.utils.getEntryPointsSignature(block_outputs_list);
 }
 
 
@@ -677,22 +677,22 @@ beat.experiments.utils.getBlockOutputSignature = function(block)
 
 beat.experiments.utils.getEntryPointsSignature = function(entry_points)
 {
-    var signature = {};
+	var signature = {};
 
-    for (var i = 0; i < entry_points.length; ++i)
-    {
-        var entry = entry_points[i];
+	for (var i = 0; i < entry_points.length; ++i)
+	{
+		var entry = entry_points[i];
 
-        if (signature[entry.channel] === undefined)
-            signature[entry.channel] = {};
+		if (signature[entry.channel] === undefined)
+			signature[entry.channel] = {};
 
-        if (signature[entry.channel][entry.dataformat] !== undefined)
-            signature[entry.channel][entry.dataformat] += 1;
-        else
-            signature[entry.channel][entry.dataformat] = 1;
-    }
+		if (signature[entry.channel][entry.dataformat] !== undefined)
+			signature[entry.channel][entry.dataformat] += 1;
+		else
+			signature[entry.channel][entry.dataformat] = 1;
+	}
 
-    return signature;
+	return signature;
 }
 
 
@@ -700,116 +700,116 @@ beat.experiments.utils.getEntryPointsSignature = function(entry_points)
 
 
 beat.experiments.utils.analyzeCompatibility = function(block_inputs_signature,
-                                                       block_outputs_signature,
-                                                       algorithm_inputs_signature,
-                                                       algorithm_outputs_signature,
-                                                       dataformats_list)
+	block_outputs_signature,
+	algorithm_inputs_signature,
+	algorithm_outputs_signature,
+	dataformats_list)
 {
-    function _getInfos(names, input_signatures, output_signatures)
-    {
-        var result = {};
-        for (var i = 0; i < names.length; ++i)
-        {
-            var name = names[i];
-
-            var entry = {
-                nb_inputs: 0,
-                nb_outputs: 0,
-            };
-
-            if (input_signatures[name] !== undefined)
-            {
-                var dataformats = Object.keys(input_signatures[name]);
-                for (var j = 0; j < dataformats.length; ++j)
-                    entry.nb_inputs += input_signatures[name][dataformats[j]];
-            }
-
-            if (output_signatures[name] !== undefined)
-            {
-                var dataformats = Object.keys(output_signatures[name]);
-                for (var j = 0; j < dataformats.length; ++j)
-                    entry.nb_outputs += output_signatures[name][dataformats[j]];
-            }
-
-            result[name] = entry;
-        }
-
-        return result;
-    }
-
-
-    var input_channels  = Object.keys(block_inputs_signature);
-    var output_channels = Object.keys(block_outputs_signature);
-
-    var input_groups  = Object.keys(algorithm_inputs_signature);
-    var output_groups = Object.keys(algorithm_outputs_signature);
-
-    // Check that the number of channels/groups match
-    if ((input_channels.length != input_groups.length) ||
-        (output_channels.length != output_groups.length))
-        return null;
-
-    // Find the compatible channel/group associations
-    var channel_names = input_channels.concat(output_channels).unique();
-    var group_names   = input_groups.concat(output_groups).unique();
-
-    var channels = _getInfos(channel_names, block_inputs_signature, block_outputs_signature);
-    var groups   = _getInfos(group_names, algorithm_inputs_signature, algorithm_outputs_signature);
-
-    var mapping = {};
-
-    for (var i = 0; i < channel_names.length; ++i)
-    {
-        var channel = channel_names[i];
-
-        var matches = [];
-        for (var j = 0; j < group_names.length; ++j)
-        {
-            var group = group_names[j];
-
-            if ((channels[channel].nb_inputs != groups[group].nb_inputs) ||
-                (channels[channel].nb_outputs != groups[group].nb_outputs))
-                continue;
-
-            if (channels[channel].nb_inputs > 0)
-            {
-                if (!beat.experiments.utils.compareSignatures(algorithm_inputs_signature[group],
-                                                              block_inputs_signature[channel],
-                                                              dataformats_list))
-                {
-                    continue;
-                }
-            }
-
-            if (channels[channel].nb_outputs > 0)
-            {
-                if (!beat.experiments.utils.compareSignatures(block_outputs_signature[channel],
-                                                              algorithm_outputs_signature[group],
-                                                              dataformats_list))
-                {
-                    continue;
-                }
-            }
-
-            matches.push(group);
-        }
-
-        if (matches.length == 0)
-            return null;
-
-        mapping[channel] = matches;
-    }
-
-    var smart_mapping = new beat.experiments.utils.SmartMapping(mapping,
-                                                                block_inputs_signature,
-                                                                block_outputs_signature,
-                                                                algorithm_inputs_signature,
-                                                                algorithm_outputs_signature);
-
-    if (smart_mapping.isValid())
-        return smart_mapping;
-
-    return null;
+	function _getInfos(names, input_signatures, output_signatures)
+	{
+		var result = {};
+		for (var i = 0; i < names.length; ++i)
+		{
+			var name = names[i];
+
+			var entry = {
+				nb_inputs: 0,
+				nb_outputs: 0,
+			};
+
+			if (input_signatures[name] !== undefined)
+			{
+				var dataformats = Object.keys(input_signatures[name]);
+				for (var j = 0; j < dataformats.length; ++j)
+					entry.nb_inputs += input_signatures[name][dataformats[j]];
+			}
+
+			if (output_signatures[name] !== undefined)
+			{
+				var dataformats = Object.keys(output_signatures[name]);
+				for (var j = 0; j < dataformats.length; ++j)
+					entry.nb_outputs += output_signatures[name][dataformats[j]];
+			}
+
+			result[name] = entry;
+		}
+
+		return result;
+	}
+
+
+	var input_channels  = Object.keys(block_inputs_signature);
+	var output_channels = Object.keys(block_outputs_signature);
+
+	var input_groups  = Object.keys(algorithm_inputs_signature);
+	var output_groups = Object.keys(algorithm_outputs_signature);
+
+	// Check that the number of channels/groups match
+	if ((input_channels.length != input_groups.length) ||
+		(output_channels.length != output_groups.length))
+		return null;
+
+	// Find the compatible channel/group associations
+	var channel_names = input_channels.concat(output_channels).unique();
+	var group_names   = input_groups.concat(output_groups).unique();
+
+	var channels = _getInfos(channel_names, block_inputs_signature, block_outputs_signature);
+	var groups   = _getInfos(group_names, algorithm_inputs_signature, algorithm_outputs_signature);
+
+	var mapping = {};
+
+	for (var i = 0; i < channel_names.length; ++i)
+	{
+		var channel = channel_names[i];
+
+		var matches = [];
+		for (var j = 0; j < group_names.length; ++j)
+		{
+			var group = group_names[j];
+
+			if ((channels[channel].nb_inputs != groups[group].nb_inputs) ||
+				(channels[channel].nb_outputs != groups[group].nb_outputs))
+				continue;
+
+			if (channels[channel].nb_inputs > 0)
+			{
+				if (!beat.experiments.utils.compareSignatures(algorithm_inputs_signature[group],
+					block_inputs_signature[channel],
+					dataformats_list))
+				{
+					continue;
+				}
+			}
+
+			if (channels[channel].nb_outputs > 0)
+			{
+				if (!beat.experiments.utils.compareSignatures(block_outputs_signature[channel],
+					algorithm_outputs_signature[group],
+					dataformats_list))
+				{
+					continue;
+				}
+			}
+
+			matches.push(group);
+		}
+
+		if (matches.length == 0)
+			return null;
+
+		mapping[channel] = matches;
+	}
+
+	var smart_mapping = new beat.experiments.utils.SmartMapping(mapping,
+		block_inputs_signature,
+		block_outputs_signature,
+		algorithm_inputs_signature,
+		algorithm_outputs_signature);
+
+	if (smart_mapping.isValid())
+		return smart_mapping;
+
+	return null;
 }
 
 
@@ -818,92 +818,92 @@ beat.experiments.utils.analyzeCompatibility = function(block_inputs_signature,
 
 beat.experiments.utils.compareSignatures = function(fixed, extendable, dataformats_list)
 {
-    // Clone the signatures to have a modifiable version
-    fixed      = JSON.parse(JSON.stringify(fixed));
-    extendable = JSON.parse(JSON.stringify(extendable));
-
-    // Compare the two signatures
-    while ((Object.keys(fixed).length != 0) && (Object.keys(extendable).length != 0))
-    {
-        var fixed_names      = Object.keys(fixed);
-        var extendable_names = Object.keys(extendable);
-
-        if ((fixed_names.length == 1) && (fixed_names[0] == 'null'))
-            break;
-
-        if ((extendable_names.length == 1) && (extendable_names[0] == 'null'))
-            break;
-
-        for (var k = 0; k < extendable_names.length; ++k)
-        {
-            var dataformat_name = extendable_names[k];
-
-            if (dataformat_name == 'null')
-                continue;
-
-            if (fixed_names.indexOf(dataformat_name) >= 0)
-            {
-                var nb = Math.min(fixed[dataformat_name], extendable[dataformat_name]);
-
-                fixed[dataformat_name] -= nb;
-                extendable[dataformat_name] -= nb;
-
-                if (fixed[dataformat_name] == 0)
-                    fixed[dataformat_name] = undefined;
-
-                if (extendable[dataformat_name] == 0)
-                    extendable[dataformat_name] = undefined;
-            }
-
-            if (extendable[dataformat_name] !== undefined)
-            {
-                var details = dataformats_list.get(dataformat_name);
-                if ((details !== null) && (details.extend !== null))
-                {
-                    if (extendable[details.extend] !== undefined)
-                        extendable[details.extend] += extendable[dataformat_name];
-                    else
-                        extendable[details.extend] = extendable[dataformat_name];
-
-                    extendable[dataformat_name] = undefined;
-                }
-                else
-                {
-                    return false;
-                }
-            }
-
-            fixed      = JSON.parse(JSON.stringify(fixed));
-            extendable = JSON.parse(JSON.stringify(extendable));
-        }
-    }
-
-    var fixed_names      = Object.keys(fixed);
-    var extendable_names = Object.keys(extendable);
-
-    var fixed_nulls = 0;
-    var fixed_not_nulls = 0;
-
-    for (var i = 0; i < fixed_names.length; ++i)
-    {
-        if (fixed_names[i] == 'null')
-            fixed_nulls += fixed[fixed_names[i]];
-        else
-            fixed_not_nulls += fixed[fixed_names[i]];
-    }
-
-    var extended_nulls = 0;
-    var extended_not_nulls = 0;
-
-    for (var i = 0; i < extendable_names.length; ++i)
-    {
-        if (extendable_names[i] == 'null')
-            extended_nulls += extendable[extendable_names[i]];
-        else
-            extended_not_nulls += extendable[extendable_names[i]];
-    }
-
-    return (fixed_nulls == extended_not_nulls) && (fixed_not_nulls == extended_nulls);
+	// Clone the signatures to have a modifiable version
+	fixed      = JSON.parse(JSON.stringify(fixed));
+	extendable = JSON.parse(JSON.stringify(extendable));
+
+	// Compare the two signatures
+	while ((Object.keys(fixed).length != 0) && (Object.keys(extendable).length != 0))
+	{
+		var fixed_names      = Object.keys(fixed);
+		var extendable_names = Object.keys(extendable);
+
+		if ((fixed_names.length == 1) && (fixed_names[0] == 'null'))
+			break;
+
+		if ((extendable_names.length == 1) && (extendable_names[0] == 'null'))
+			break;
+
+		for (var k = 0; k < extendable_names.length; ++k)
+		{
+			var dataformat_name = extendable_names[k];
+
+			if (dataformat_name == 'null')
+				continue;
+
+			if (fixed_names.indexOf(dataformat_name) >= 0)
+			{
+				var nb = Math.min(fixed[dataformat_name], extendable[dataformat_name]);
+
+				fixed[dataformat_name] -= nb;
+				extendable[dataformat_name] -= nb;
+
+				if (fixed[dataformat_name] == 0)
+					fixed[dataformat_name] = undefined;
+
+				if (extendable[dataformat_name] == 0)
+					extendable[dataformat_name] = undefined;
+			}
+
+			if (extendable[dataformat_name] !== undefined)
+			{
+				var details = dataformats_list.get(dataformat_name);
+				if ((details !== null) && (details.extend !== null))
+				{
+					if (extendable[details.extend] !== undefined)
+						extendable[details.extend] += extendable[dataformat_name];
+					else
+						extendable[details.extend] = extendable[dataformat_name];
+
+					extendable[dataformat_name] = undefined;
+				}
+				else
+				{
+					return false;
+				}
+			}
+
+			fixed      = JSON.parse(JSON.stringify(fixed));
+			extendable = JSON.parse(JSON.stringify(extendable));
+		}
+	}
+
+	var fixed_names      = Object.keys(fixed);
+	var extendable_names = Object.keys(extendable);
+
+	var fixed_nulls = 0;
+	var fixed_not_nulls = 0;
+
+	for (var i = 0; i < fixed_names.length; ++i)
+	{
+		if (fixed_names[i] == 'null')
+			fixed_nulls += fixed[fixed_names[i]];
+		else
+			fixed_not_nulls += fixed[fixed_names[i]];
+	}
+
+	var extended_nulls = 0;
+	var extended_not_nulls = 0;
+
+	for (var i = 0; i < extendable_names.length; ++i)
+	{
+		if (extendable_names[i] == 'null')
+			extended_nulls += extendable[extendable_names[i]];
+		else
+			extended_not_nulls += extendable[extendable_names[i]];
+	}
+
+	return (fixed_nulls == extended_not_nulls) && (fixed_not_nulls == extended_nulls);
 }
 
 
@@ -915,8 +915,8 @@ beat.experiments.utils.compareSignatures = function(fixed, extendable, dataforma
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList = function()
 {
-    // Attributes
-    this.databases = {};
+	// Attributes
+	this.databases = {};
 }
 
 
@@ -924,29 +924,29 @@ beat.experiments.utils.SmartDatasetList = function()
 // Add an entry into the list
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype.add = function(database, protocol, block,
-                                                                 dataset, compatibility_infos)
+	dataset, compatibility_infos)
 {
-    if (this.databases[database] === undefined)
-    {
-        this.databases[database] = {
-            usable: null,
-            protocols: {},
-        };
-    }
-
-    if (this.databases[database].protocols[protocol] === undefined)
-    {
-        this.databases[database].protocols[protocol] = {
-            usable: null,
-            blocks: {},
-        };
-    }
-
-    if (this.databases[database].protocols[protocol].blocks[block] === undefined)
-        this.databases[database].protocols[protocol].blocks[block] = [];
-
-    compatibility_infos.set = dataset;
-    this.databases[database].protocols[protocol].blocks[block].push(compatibility_infos);
+	if (this.databases[database] === undefined)
+	{
+		this.databases[database] = {
+			usable: null,
+			protocols: {},
+		};
+	}
+
+	if (this.databases[database].protocols[protocol] === undefined)
+	{
+		this.databases[database].protocols[protocol] = {
+			usable: null,
+			blocks: {},
+		};
+	}
+
+	if (this.databases[database].protocols[protocol].blocks[block] === undefined)
+		this.databases[database].protocols[protocol].blocks[block] = [];
+
+	compatibility_infos.set = dataset;
+	this.databases[database].protocols[protocol].blocks[block].push(compatibility_infos);
 }
 
 
@@ -956,83 +956,83 @@ beat.experiments.utils.SmartDatasetList.prototype.add = function(database, proto
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype.finish = function(nb_dataset_blocks)
 {
-    var database_names = Object.keys(this.databases);
-    for (var i = 0; i < database_names.length; ++i)
-    {
-        var database = this.databases[database_names[i]];
-
-        var protocol_names = Object.keys(database.protocols);
-        for (var j = 0; j < protocol_names.length; ++j)
-        {
-            var protocol = database.protocols[protocol_names[j]];
-
-            // Remove protocols not covering all the dataset blocks
-            var block_names = Object.keys(protocol.blocks);
-            if (block_names.length != nb_dataset_blocks)
-            {
-                delete database.protocols[protocol_names[j]];
-                continue;
-            }
-
-            // Sort the compatible datasets for each block
-            for (var k = 0; k < block_names.length; ++k)
-            {
-                protocol.blocks[block_names[k]].sort(function(a, b) {
-                    if (a.perfect)
-                    {
-                        if (!b.perfect)
-                            return -1.0;
-                        else
-                            return (a.distance - b.distance);
-                    }
-                    else
-                    {
-                        if (b.perfect)
-                            return +1.0;
-                        else
-                            return (a.distance - b.distance);
-                    }
-                });
-            }
-
-            // First process all the dataset blocks for which only one dataset is
-            // compatible
-            block_names = this._processSingleMatches(block_names, protocol);
-            if (block_names === null)
-            {
-                delete database.protocols[protocol_names[j]];
-                continue;
-            }
-
-            // Next process all the dataset blocks for which one perfect dataset was
-            // found
-            if (block_names.length > 0)
-            {
-                block_names = this._processPerfectMatches(block_names, protocol);
-                if (block_names === null)
-                {
-                    delete database.protocols[protocol_names[j]];
-                    continue;
-                }
-            }
-
-            // Next process the best remaining match for each dataset block
-            if (block_names.length > 0)
-            {
-                block_names = this._processBestMatches(block_names, protocol);
-                if (block_names === null)
-                {
-                    delete database.protocols[protocol_names[j]];
-                    continue;
-                }
-            }
-        }
-
-        if (Object.keys(database.protocols) == 0)
-            delete this.databases[database_names[i]];
-    }
-
-    return (Object.keys(this.databases).length > 0);
+	var database_names = Object.keys(this.databases);
+	for (var i = 0; i < database_names.length; ++i)
+	{
+		var database = this.databases[database_names[i]];
+
+		var protocol_names = Object.keys(database.protocols);
+		for (var j = 0; j < protocol_names.length; ++j)
+		{
+			var protocol = database.protocols[protocol_names[j]];
+
+			// Remove protocols not covering all the dataset blocks
+			var block_names = Object.keys(protocol.blocks);
+			if (block_names.length != nb_dataset_blocks)
+			{
+				delete database.protocols[protocol_names[j]];
+				continue;
+			}
+
+			// Sort the compatible datasets for each block
+			for (var k = 0; k < block_names.length; ++k)
+			{
+				protocol.blocks[block_names[k]].sort(function(a, b) {
+					if (a.perfect)
+					{
+						if (!b.perfect)
+							return -1.0;
+						else
+							return (a.distance - b.distance);
+					}
+					else
+					{
+						if (b.perfect)
+							return +1.0;
+						else
+							return (a.distance - b.distance);
+					}
+				});
+			}
+
+			// First process all the dataset blocks for which only one dataset is
+			// compatible
+			block_names = this._processSingleMatches(block_names, protocol);
+			if (block_names === null)
+			{
+				delete database.protocols[protocol_names[j]];
+				continue;
+			}
+
+			// Next process all the dataset blocks for which one perfect dataset was
+			// found
+			if (block_names.length > 0)
+			{
+				block_names = this._processPerfectMatches(block_names, protocol);
+				if (block_names === null)
+				{
+					delete database.protocols[protocol_names[j]];
+					continue;
+				}
+			}
+
+			// Next process the best remaining match for each dataset block
+			if (block_names.length > 0)
+			{
+				block_names = this._processBestMatches(block_names, protocol);
+				if (block_names === null)
+				{
+					delete database.protocols[protocol_names[j]];
+					continue;
+				}
+			}
+		}
+
+		if (Object.keys(database.protocols) == 0)
+			delete this.databases[database_names[i]];
+	}
+
+	return (Object.keys(this.databases).length > 0);
 }
 
 
@@ -1041,7 +1041,7 @@ beat.experiments.utils.SmartDatasetList.prototype.finish = function(nb_dataset_b
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype.databaseNames = function()
 {
-    return Object.keys(this.databases);
+	return Object.keys(this.databases);
 }
 
 
@@ -1050,10 +1050,10 @@ beat.experiments.utils.SmartDatasetList.prototype.databaseNames = function()
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype.protocolNames = function(database)
 {
-    if (this.databases[database] === undefined)
-        return [];
+	if (this.databases[database] === undefined)
+		return [];
 
-    return Object.keys(this.databases[database].protocols);
+	return Object.keys(this.databases[database].protocols);
 }
 
 
@@ -1062,13 +1062,13 @@ beat.experiments.utils.SmartDatasetList.prototype.protocolNames = function(datab
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype.protocol = function(database, name)
 {
-    if (this.databases[database] === undefined)
-        return null;
+	if (this.databases[database] === undefined)
+		return null;
 
-    if (this.databases[database].protocols[name] === undefined)
-        return null;
+	if (this.databases[database].protocols[name] === undefined)
+		return null;
 
-    return this.databases[database].protocols[name];
+	return this.databases[database].protocols[name];
 }
 
 
@@ -1076,38 +1076,38 @@ beat.experiments.utils.SmartDatasetList.prototype.protocol = function(database,
 // Update the usability status of one block/dataset combination
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype.update = function(database_name, protocol_name,
-                                                                    block_name, dataset, usable)
+	block_name, dataset, usable)
 {
-    var database = this.databases[database_name];
-    if (database === undefined)
-        return;
-
-    var protocol = database.protocols[protocol_name];
-    if (protocol === undefined)
-        return;
-
-    var block = protocol.blocks[block_name];
-    if (block === undefined)
-        return;
-
-    if (block.set != dataset)
-        return;
-
-    if (usable != block.usable)
-    {
-        block.usable = usable;
-
-        if (usable)
-        {
-            protocol.usable = null;
-            database.usable = null;
-        }
-        else
-        {
-            protocol.usable = false;
-            database.usable = null;
-        }
-    }
+	var database = this.databases[database_name];
+	if (database === undefined)
+		return;
+
+	var protocol = database.protocols[protocol_name];
+	if (protocol === undefined)
+		return;
+
+	var block = protocol.blocks[block_name];
+	if (block === undefined)
+		return;
+
+	if (block.set != dataset)
+		return;
+
+	if (usable != block.usable)
+	{
+		block.usable = usable;
+
+		if (usable)
+		{
+			protocol.usable = null;
+			database.usable = null;
+		}
+		else
+		{
+			protocol.usable = false;
+			database.usable = null;
+		}
+	}
 }
 
 
@@ -1115,32 +1115,32 @@ beat.experiments.utils.SmartDatasetList.prototype.update = function(database_nam
 // Indicates if a protocol is usable (given the current configuration)
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype.isProtocolUsable = function(database_name,
-                                                                              protocol_name)
+	protocol_name)
 {
-    var database = this.databases[database_name];
-    if (database === undefined)
-        return false;
-
-    var protocol = database.protocols[protocol_name];
-    if (protocol === undefined)
-        return false;
-
-    if (protocol.usable === null)
-    {
-        protocol.usable = true;
-
-        var block_names = Object.keys(protocol.blocks);
-        for (var i = 0; i < block_names.length; ++i)
-        {
-            if (!protocol.blocks[ block_names[i] ].usable)
-            {
-                protocol.usable = false;
-                break;
-            }
-        }
-    }
-
-    return protocol.usable;
+	var database = this.databases[database_name];
+	if (database === undefined)
+		return false;
+
+	var protocol = database.protocols[protocol_name];
+	if (protocol === undefined)
+		return false;
+
+	if (protocol.usable === null)
+	{
+		protocol.usable = true;
+
+		var block_names = Object.keys(protocol.blocks);
+		for (var i = 0; i < block_names.length; ++i)
+		{
+			if (!protocol.blocks[ block_names[i] ].usable)
+			{
+				protocol.usable = false;
+				break;
+			}
+		}
+	}
+
+	return protocol.usable;
 }
 
 
@@ -1149,26 +1149,26 @@ beat.experiments.utils.SmartDatasetList.prototype.isProtocolUsable = function(da
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype.isDatabaseUsable = function(database_name)
 {
-    var database = this.databases[database_name];
-    if (database === undefined)
-        return false;
-
-    if (database.usable === null)
-    {
-        database.usable = false;
-
-        var protocol_names = Object.keys(database.protocols);
-        for (var i = 0; i < protocol_names.length; ++i)
-        {
-            if (this.isProtocolUsable(database_name, protocol_names[i]))
-            {
-                database.usable = true;
-                break;
-            }
-        }
-    }
-
-    return database.usable;
+	var database = this.databases[database_name];
+	if (database === undefined)
+		return false;
+
+	if (database.usable === null)
+	{
+		database.usable = false;
+
+		var protocol_names = Object.keys(database.protocols);
+		for (var i = 0; i < protocol_names.length; ++i)
+		{
+			if (this.isProtocolUsable(database_name, protocol_names[i]))
+			{
+				database.usable = true;
+				break;
+			}
+		}
+	}
+
+	return database.usable;
 }
 
 
@@ -1176,27 +1176,27 @@ beat.experiments.utils.SmartDatasetList.prototype.isDatabaseUsable = function(da
 
 
 beat.experiments.utils.SmartDatasetList.prototype._removeDataset = function(
-                                                    block_names, protocol, set_to_remove)
+	block_names, protocol, set_to_remove)
 {
-    for (var i = 0; i < block_names.length; ++i)
-    {
-        var block_name = block_names[i];
+	for (var i = 0; i < block_names.length; ++i)
+	{
+		var block_name = block_names[i];
 
-        for (var j = 0; j < protocol.blocks[block_name].length; ++j)
-        {
-            if (protocol.blocks[block_name][j].set == set_to_remove)
-            {
-                protocol.blocks[block_name].splice(j, 1);
+		for (var j = 0; j < protocol.blocks[block_name].length; ++j)
+		{
+			if (protocol.blocks[block_name][j].set == set_to_remove)
+			{
+				protocol.blocks[block_name].splice(j, 1);
 
-                if (protocol.blocks[block_name].length == 0)
-                    return false;
+				if (protocol.blocks[block_name].length == 0)
+					return false;
 
-                break;
-            }
-        }
-    }
+				break;
+			}
+		}
+	}
 
-    return true;
+	return true;
 }
 
 
@@ -1204,31 +1204,31 @@ beat.experiments.utils.SmartDatasetList.prototype._removeDataset = function(
 // Process all the dataset blocks for which only one dataset is compatible
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype._processSingleMatches = function(
-                                                                    block_names, protocol)
+	block_names, protocol)
 {
-    var changed = true;
-    while (changed && (block_names.length > 0))
-    {
-        changed = false;
-        for (var i = 0; i < block_names.length; ++i)
-        {
-            var block_name = block_names[i];
-
-            if (protocol.blocks[block_name].length == 1)
-            {
-                protocol.blocks[block_name] = protocol.blocks[block_name][0];
-                block_names.splice(i, 1);
-
-                if (!this._removeDataset(block_names, protocol, protocol.blocks[block_name].set))
-                    return null;
-
-                changed = true;
-                break;
-            }
-        }
-    }
-
-    return block_names;
+	var changed = true;
+	while (changed && (block_names.length > 0))
+	{
+		changed = false;
+		for (var i = 0; i < block_names.length; ++i)
+		{
+			var block_name = block_names[i];
+
+			if (protocol.blocks[block_name].length == 1)
+			{
+				protocol.blocks[block_name] = protocol.blocks[block_name][0];
+				block_names.splice(i, 1);
+
+				if (!this._removeDataset(block_names, protocol, protocol.blocks[block_name].set))
+					return null;
+
+				changed = true;
+				break;
+			}
+		}
+	}
+
+	return block_names;
 }
 
 
@@ -1236,37 +1236,37 @@ beat.experiments.utils.SmartDatasetList.prototype._processSingleMatches = functi
 // Process all the dataset blocks for which one perfect dataset was found
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype._processPerfectMatches = function(
-                                                                    block_names, protocol)
+	block_names, protocol)
 {
-    var changed = true;
-    while (changed && (block_names.length > 0))
-    {
-        changed = false;
-        for (var i = 0; i < block_names.length; ++i)
-        {
-            var block_name = block_names[i];
-
-            for (var j = 0; j < protocol.blocks[block_name].length; ++j)
-            {
-                if (protocol.blocks[block_name][j].perfect)
-                {
-                    protocol.blocks[block_name] = protocol.blocks[block_name][j];
-                    block_names.splice(i, 1);
-
-                    if (!this._removeDataset(block_names, protocol, protocol.blocks[block_name].set))
-                        return null;
-
-                    changed = true;
-                    break;
-                }
-            }
-        }
-    }
-
-    if (block_names.length > 0)
-        block_names = this._processSingleMatches(block_names, protocol);
-
-    return block_names;
+	var changed = true;
+	while (changed && (block_names.length > 0))
+	{
+		changed = false;
+		for (var i = 0; i < block_names.length; ++i)
+		{
+			var block_name = block_names[i];
+
+			for (var j = 0; j < protocol.blocks[block_name].length; ++j)
+			{
+				if (protocol.blocks[block_name][j].perfect)
+				{
+					protocol.blocks[block_name] = protocol.blocks[block_name][j];
+					block_names.splice(i, 1);
+
+					if (!this._removeDataset(block_names, protocol, protocol.blocks[block_name].set))
+						return null;
+
+					changed = true;
+					break;
+				}
+			}
+		}
+	}
+
+	if (block_names.length > 0)
+		block_names = this._processSingleMatches(block_names, protocol);
+
+	return block_names;
 }
 
 
@@ -1274,50 +1274,50 @@ beat.experiments.utils.SmartDatasetList.prototype._processPerfectMatches = funct
 // Process the best match for each dataset block, when no ambiguity is possible
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype._processSingleBestMatches = function(
-                                                                    block_names, protocol)
+	block_names, protocol)
 {
-    var changed = true;
-    while (changed && (block_names.length > 0))
-    {
-        changed = false;
-        for (var i = 0; i < block_names.length; ++i)
-        {
-            var block_name = block_names[i];
-
-            var best_match = 0;
-            var nb_matches = 1;
-
-            for (var j = 1; j < protocol.blocks[block_name].length; ++j)
-            {
-                if (protocol.blocks[block_name][j].distance == protocol.blocks[block_name][best_match].distance)
-                {
-                    nb_matches += 1;
-                }
-                else if (protocol.blocks[block_name][j].distance < protocol.blocks[block_name][best_match].distance)
-                {
-                    best_match = j;
-                    nb_matches = 1;
-                }
-            }
-
-            if (nb_matches == 1)
-            {
-                protocol.blocks[block_name] = protocol.blocks[block_name][best_match];
-                block_names.splice(i, 1);
-
-                if (!this._removeDataset(block_names, protocol, protocol.blocks[block_name].set))
-                    return null;
-
-                changed = true;
-                break;
-            }
-        }
-    }
-
-    if (block_names.length > 0)
-        block_names = this._processSingleMatches(block_names, protocol);
-
-    return block_names;
+	var changed = true;
+	while (changed && (block_names.length > 0))
+	{
+		changed = false;
+		for (var i = 0; i < block_names.length; ++i)
+		{
+			var block_name = block_names[i];
+
+			var best_match = 0;
+			var nb_matches = 1;
+
+			for (var j = 1; j < protocol.blocks[block_name].length; ++j)
+			{
+				if (protocol.blocks[block_name][j].distance == protocol.blocks[block_name][best_match].distance)
+				{
+					nb_matches += 1;
+				}
+				else if (protocol.blocks[block_name][j].distance < protocol.blocks[block_name][best_match].distance)
+				{
+					best_match = j;
+					nb_matches = 1;
+				}
+			}
+
+			if (nb_matches == 1)
+			{
+				protocol.blocks[block_name] = protocol.blocks[block_name][best_match];
+				block_names.splice(i, 1);
+
+				if (!this._removeDataset(block_names, protocol, protocol.blocks[block_name].set))
+					return null;
+
+				changed = true;
+				break;
+			}
+		}
+	}
+
+	if (block_names.length > 0)
+		block_names = this._processSingleMatches(block_names, protocol);
+
+	return block_names;
 }
 
 
@@ -1325,78 +1325,78 @@ beat.experiments.utils.SmartDatasetList.prototype._processSingleBestMatches = fu
 // Process the best matches for each dataset block
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartDatasetList.prototype._processBestMatches = function(
-                                                                    block_names, protocol)
+	block_names, protocol)
 {
-    block_names = this._processSingleBestMatches(block_names, protocol);
-
-    while ((block_names !== null) && (block_names.length > 0))
-    {
-        var best_block = null;
-        var best_distance = null;
-        var matches = null;
-
-        for (var i = 0; i < block_names.length; ++i)
-        {
-            var block_name = block_names[i];
-
-            var block_best_distance = protocol.blocks[block_name][0].distance;
-            var block_matches = [ {
-                    set: protocol.blocks[block_name][0].set,
-                    index: 0,
-                }];
-
-            for (var j = 1; j < protocol.blocks[block_name].length; ++j)
-            {
-                if (protocol.blocks[block_name][j].distance == block_best_distance)
-                {
-                    block_matches.push({
-                        set: protocol.blocks[block_name][j].set,
-                        index: j,
-                    });
-                }
-                else if (protocol.blocks[block_name][j].distance < block_best_distance)
-                {
-                    block_best_distance = protocol.blocks[block_name][j].distance;
-                    block_matches.push({
-                        set: protocol.blocks[block_name][j].set,
-                        index: j,
-                    });
-                }
-            }
-
-            if ((best_block === null) || (block_best_distance < best_distance) ||
-                ((block_best_distance == best_distance) && (block_matches.length < matches.length)))
-            {
-                best_block = block_name;
-                best_distance = block_best_distance;
-                matches = block_matches;
-            }
-        }
-
-        // Select the dataset by fuzzy matching
-        var choice = null;
-        var score = -1.0;
-        for (var i = 0; i < matches.length; ++i)
-        {
-            var match_score = best_block.score(matches[i].set, 0.5);
-            if (match_score > score)
-            {
-                choice = matches[i];
-                score = match_score;
-            }
-        }
-
-        protocol.blocks[best_block] = protocol.blocks[best_block][choice.index];
-        block_names.splice(block_names.indexOf(best_block), 1);
-
-        if (!this._removeDataset(block_names, protocol, protocol.blocks[best_block].set))
-            return null;
-
-        if (block_names.length > 0)
-            block_names = this._processSingleMatches(block_names, protocol);
-    }
-
-    return block_names;
+	block_names = this._processSingleBestMatches(block_names, protocol);
+
+	while ((block_names !== null) && (block_names.length > 0))
+	{
+		var best_block = null;
+		var best_distance = null;
+		var matches = null;
+
+		for (var i = 0; i < block_names.length; ++i)
+		{
+			var block_name = block_names[i];
+
+			var block_best_distance = protocol.blocks[block_name][0].distance;
+			var block_matches = [ {
+				set: protocol.blocks[block_name][0].set,
+				index: 0,
+			}];
+
+			for (var j = 1; j < protocol.blocks[block_name].length; ++j)
+			{
+				if (protocol.blocks[block_name][j].distance == block_best_distance)
+				{
+					block_matches.push({
+						set: protocol.blocks[block_name][j].set,
+						index: j,
+					});
+				}
+				else if (protocol.blocks[block_name][j].distance < block_best_distance)
+				{
+					block_best_distance = protocol.blocks[block_name][j].distance;
+					block_matches.push({
+						set: protocol.blocks[block_name][j].set,
+						index: j,
+					});
+				}
+			}
+
+			if ((best_block === null) || (block_best_distance < best_distance) ||
+				((block_best_distance == best_distance) && (block_matches.length < matches.length)))
+			{
+				best_block = block_name;
+				best_distance = block_best_distance;
+				matches = block_matches;
+			}
+		}
+
+		// Select the dataset by fuzzy matching
+		var choice = null;
+		var score = -1.0;
+		for (var i = 0; i < matches.length; ++i)
+		{
+			var match_score = best_block.score(matches[i].set, 0.5);
+			if (match_score > score)
+			{
+				choice = matches[i];
+				score = match_score;
+			}
+		}
+
+		protocol.blocks[best_block] = protocol.blocks[best_block][choice.index];
+		block_names.splice(block_names.indexOf(best_block), 1);
+
+		if (!this._removeDataset(block_names, protocol, protocol.blocks[best_block].set))
+			return null;
+
+		if (block_names.length > 0)
+			block_names = this._processSingleMatches(block_names, protocol);
+	}
+
+	return block_names;
 }
 
 
@@ -1407,21 +1407,21 @@ beat.experiments.utils.SmartDatasetList.prototype._processBestMatches = function
 // Constructor
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartMapping = function(possibilities, block_inputs_signature,
-                                               block_outputs_signature,
-                                               algorithm_inputs_signature,
-                                               algorithm_outputs_signature)
+	block_outputs_signature,
+	algorithm_inputs_signature,
+	algorithm_outputs_signature)
 {
-    // Attributes
-    this.possibilities               = JSON.parse(JSON.stringify(possibilities));
-    this.block_inputs_signature      = block_inputs_signature;
-    this.block_outputs_signature     = block_outputs_signature;
-    this.algorithm_inputs_signature  = algorithm_inputs_signature;
-    this.algorithm_outputs_signature = algorithm_outputs_signature;
-    this.iteration_entries           = [];
-
-    // First process all the possibilities for which only one choice is possible
-    if (!this._automaticMapping())
-        this.possibilities = null;
+	// Attributes
+	this.possibilities               = JSON.parse(JSON.stringify(possibilities));
+	this.block_inputs_signature      = block_inputs_signature;
+	this.block_outputs_signature     = block_outputs_signature;
+	this.algorithm_inputs_signature  = algorithm_inputs_signature;
+	this.algorithm_outputs_signature = algorithm_outputs_signature;
+	this.iteration_entries           = [];
+
+	// First process all the possibilities for which only one choice is possible
+	if (!this._automaticMapping())
+		this.possibilities = null;
 }
 
 
@@ -1430,7 +1430,7 @@ beat.experiments.utils.SmartMapping = function(possibilities, block_inputs_signa
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartMapping.prototype.isValid = function()
 {
-    return (this.possibilities !== null);
+	return (this.possibilities !== null);
 }
 
 
@@ -1439,74 +1439,74 @@ beat.experiments.utils.SmartMapping.prototype.isValid = function()
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartMapping.prototype.startIteration = function()
 {
-    this.iteration_entries = [];
-
-    var channels_to_process = Object.keys(this.possibilities);
-    var processed_indexes = [];
-    var biggest = 0;
-
-    // First: add the single possibility combinations
-    for (var i = 0; i < channels_to_process.length; ++i)
-    {
-        var channel = channels_to_process[i];
-        if (this.possibilities[channel].length == 1)
-        {
-            this.iteration_entries.push({
-                channel: channel,
-                group: this.possibilities[channel][0].group,
-            });
-            processed_indexes.push(i);
-        }
-        else if (this.possibilities[channel].length > biggest)
-        {
-            biggest = this.possibilities[channel].length;
-        }
-    }
-
-    processed_indexes.reverse();
-    for (var i = 0; i < processed_indexes.length; ++i)
-        channels_to_process.splice(processed_indexes[i], 1);
-
-    // Next: add the best match of all the other combinations, beginning with the ones
-    // with the most possibilities
-    var unavailable_groups = [];
-    while (channels_to_process.length > 0)
-    {
-        processed_indexes = [];
-        var biggest2 = 0;
-
-        for (var i = 0; i < channels_to_process.length; ++i)
-        {
-            var channel = channels_to_process[i];
-
-            if (this.possibilities[channel].length == biggest)
-            {
-                var entries = this.possibilities[channel].filter(function(entry) {
-                    return (unavailable_groups.indexOf(entry.group) == -1);
-                });
-
-                this.iteration_entries.push({
-                    channel: channel,
-                    group: entries[0].group,
-                });
-
-                processed_indexes.push(i);
-                unavailable_groups.push(entries[0].group);
-            }
-            else if (this.possibilities[channel].length > biggest2)
-            {
-                biggest2 = this.possibilities[channel].length;
-            }
-        }
-
-        processed_indexes.reverse();
-        for (var i = 0; i < processed_indexes.length; ++i)
-            channels_to_process.splice(processed_indexes[i], 1);
-
-        biggest = biggest2;
-    }
-
-    this.iteration_entries.reverse();
+	this.iteration_entries = [];
+
+	var channels_to_process = Object.keys(this.possibilities);
+	var processed_indexes = [];
+	var biggest = 0;
+
+	// First: add the single possibility combinations
+	for (var i = 0; i < channels_to_process.length; ++i)
+	{
+		var channel = channels_to_process[i];
+		if (this.possibilities[channel].length == 1)
+		{
+			this.iteration_entries.push({
+				channel: channel,
+				group: this.possibilities[channel][0].group,
+			});
+			processed_indexes.push(i);
+		}
+		else if (this.possibilities[channel].length > biggest)
+		{
+			biggest = this.possibilities[channel].length;
+		}
+	}
+
+	processed_indexes.reverse();
+	for (var i = 0; i < processed_indexes.length; ++i)
+		channels_to_process.splice(processed_indexes[i], 1);
+
+	// Next: add the best match of all the other combinations, beginning with the ones
+	// with the most possibilities
+	var unavailable_groups = [];
+	while (channels_to_process.length > 0)
+	{
+		processed_indexes = [];
+		var biggest2 = 0;
+
+		for (var i = 0; i < channels_to_process.length; ++i)
+		{
+			var channel = channels_to_process[i];
+
+			if (this.possibilities[channel].length == biggest)
+			{
+				var entries = this.possibilities[channel].filter(function(entry) {
+					return (unavailable_groups.indexOf(entry.group) == -1);
+				});
+
+				this.iteration_entries.push({
+					channel: channel,
+					group: entries[0].group,
+				});
+
+				processed_indexes.push(i);
+				unavailable_groups.push(entries[0].group);
+			}
+			else if (this.possibilities[channel].length > biggest2)
+			{
+				biggest2 = this.possibilities[channel].length;
+			}
+		}
+
+		processed_indexes.reverse();
+		for (var i = 0; i < processed_indexes.length; ++i)
+			channels_to_process.splice(processed_indexes[i], 1);
+
+		biggest = biggest2;
+	}
+
+	this.iteration_entries.reverse();
 }
 
 
@@ -1515,7 +1515,7 @@ beat.experiments.utils.SmartMapping.prototype.startIteration = function()
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartMapping.prototype.isIterationDone = function()
 {
-    return (this.iteration_entries.length == 0);
+	return (this.iteration_entries.length == 0);
 }
 
 
@@ -1524,7 +1524,7 @@ beat.experiments.utils.SmartMapping.prototype.isIterationDone = function()
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartMapping.prototype.next = function()
 {
-    return this.iteration_entries.pop();
+	return this.iteration_entries.pop();
 }
 
 
@@ -1532,27 +1532,27 @@ beat.experiments.utils.SmartMapping.prototype.next = function()
 
 
 beat.experiments.utils.SmartMapping.prototype._removePossibility = function(
-                                                    channel_names, group_to_remove)
+	channel_names, group_to_remove)
 {
-    for (var i = 0; i < channel_names.length; ++i)
-    {
-        var channel = channel_names[i];
+	for (var i = 0; i < channel_names.length; ++i)
+	{
+		var channel = channel_names[i];
 
-        for (var j = 0; j < this.possibilities[channel].length; ++j)
-        {
-            if (this.possibilities[channel][j] == group_to_remove)
-            {
-                this.possibilities[channel].splice(j, 1);
+		for (var j = 0; j < this.possibilities[channel].length; ++j)
+		{
+			if (this.possibilities[channel][j] == group_to_remove)
+			{
+				this.possibilities[channel].splice(j, 1);
 
-                if (this.possibilities[channel].length == 0)
-                    return false;
+				if (this.possibilities[channel].length == 0)
+					return false;
 
-                break;
-            }
-        }
-    }
+				break;
+			}
+		}
+	}
 
-    return true;
+	return true;
 }
 
 
@@ -1561,94 +1561,94 @@ beat.experiments.utils.SmartMapping.prototype._removePossibility = function(
 //----------------------------------------------------------------------------------------
 beat.experiments.utils.SmartMapping.prototype._automaticMapping = function()
 {
-    var channel_names = Object.keys(this.possibilities);
-
-    // Process all the channels for which there is only one unique group
-    var changed = true;
-    while (changed && (channel_names.length > 0))
-    {
-        changed = false;
-        for (var i = 0; i < channel_names.length; ++i)
-        {
-            var channel = channel_names[i];
-
-            if (this.possibilities[channel].length == 1)
-            {
-                this.possibilities[channel] = [{
-                    group: this.possibilities[channel][0],
-                    score: 1.0,
-                }];
-
-                channel_names.splice(i, 1);
-
-                if (!this._removePossibility(channel_names, this.possibilities[channel][0].group))
-                    return false;
-
-                changed = true;
-                break;
-            }
-        }
-    }
-
-
-    // Process all the groups for which there is only one unique channel
-    var reverse_possibilities = {};
-    for (var i = 0; i < channel_names.length; ++i)
-    {
-        var channel = channel_names[i];
-
-        for (var j = 0; j < this.possibilities[channel].length; ++j)
-        {
-            var group = this.possibilities[channel][j];
-
-            if (reverse_possibilities[group] === undefined)
-                reverse_possibilities[group] = [];
-
-            reverse_possibilities[group].push(channel);
-        }
-    }
-
-    var group_names = Object.keys(reverse_possibilities);
-    for (var i = 0; i < group_names.length; ++i)
-    {
-        var group = group_names[i];
-
-        if (reverse_possibilities[group].length == 1)
-        {
-            this.possibilities[reverse_possibilities[group][0]] = [{
-                group: group,
-                score: 1.0,
-            }];
-
-            channel_names.splice(channel_names.indexOf(reverse_possibilities[group][0]), 1);
-
-            if (!this._removePossibility(channel_names, group))
-                return false;
-        }
-    }
-
-
-    // Process all the remaining channels
-    for (var i = 0; i < channel_names.length; ++i)
-    {
-        var channel = channel_names[i];
-
-        var result = [];
-        for (var j = 0; j < this.possibilities[channel].length; ++j)
-        {
-            var entry = {
-                group: this.possibilities[channel][j],
-                score: channel.score(this.possibilities[channel][j], 0.5),
-            };
-
-            result.push(entry);
-        }
-
-        result.sort(function(a, b) { return a.score < b.score; });
-        this.possibilities[channel] = result;
-    }
-
-    return true;
+	var channel_names = Object.keys(this.possibilities);
+
+	// Process all the channels for which there is only one unique group
+	var changed = true;
+	while (changed && (channel_names.length > 0))
+	{
+		changed = false;
+		for (var i = 0; i < channel_names.length; ++i)
+		{
+			var channel = channel_names[i];
+
+			if (this.possibilities[channel].length == 1)
+			{
+				this.possibilities[channel] = [{
+					group: this.possibilities[channel][0],
+					score: 1.0,
+				}];
+
+				channel_names.splice(i, 1);
+
+				if (!this._removePossibility(channel_names, this.possibilities[channel][0].group))
+					return false;
+
+				changed = true;
+				break;
+			}
+		}
+	}
+
+
+	// Process all the groups for which there is only one unique channel
+	var reverse_possibilities = {};
+	for (var i = 0; i < channel_names.length; ++i)
+	{
+		var channel = channel_names[i];
+
+		for (var j = 0; j < this.possibilities[channel].length; ++j)
+		{
+			var group = this.possibilities[channel][j];
+
+			if (reverse_possibilities[group] === undefined)
+				reverse_possibilities[group] = [];
+
+			reverse_possibilities[group].push(channel);
+		}
+	}
+
+	var group_names = Object.keys(reverse_possibilities);
+	for (var i = 0; i < group_names.length; ++i)
+	{
+		var group = group_names[i];
+
+		if (reverse_possibilities[group].length == 1)
+		{
+			this.possibilities[reverse_possibilities[group][0]] = [{
+				group: group,
+				score: 1.0,
+			}];
+
+			channel_names.splice(channel_names.indexOf(reverse_possibilities[group][0]), 1);
+
+			if (!this._removePossibility(channel_names, group))
+				return false;
+		}
+	}
+
+
+	// Process all the remaining channels
+	for (var i = 0; i < channel_names.length; ++i)
+	{
+		var channel = channel_names[i];
+
+		var result = [];
+		for (var j = 0; j < this.possibilities[channel].length; ++j)
+		{
+			var entry = {
+				group: this.possibilities[channel][j],
+				score: channel.score(this.possibilities[channel][j], 0.5),
+			};
+
+			result.push(entry);
+		}
+
+		result.sort(function(a, b) { return a.score < b.score; });
+		this.possibilities[channel] = result;
+	}
+
+	return true;
 }
 
 
@@ -1669,55 +1669,55 @@ beat.experiments.utils.SmartMapping.prototype._automaticMapping = function()
  **/
 beat.experiments.utils.modal_attest = function (name, url, redirect) {
 
-  var dialog = new BootstrapDialog({
-    title: '<i class="fa fa-certificate"></i> Attestation for "' + name + '"',
-    message: '<p>When you attest an experiment, the platform guarantees it is reproducible, therefore all components related to this experiment (including the toolchain, algorithms, libraries and dataformats) will be frozen. This procedure is <strong>not</strong> irreversible. You can always delete locked (i.e. unpublished) attestations. This procedure also does not stop you from forking or creating new revisions of objects used in this experiment.</p>',
-    type: BootstrapDialog.TYPE_PRIMARY,
-    size: BootstrapDialog.SIZE_WIDE,
-    buttons: [
-    {
-      label: 'Cancel',
-      cssClass: 'btn-default',
-      action: function(the_dialog) {
-        the_dialog.close();
-      }
-    },
-    {
-      label: 'Attest',
-      cssClass: 'btn-primary',
-      action: function(the_dialog) {
-
-        $.ajaxSetup({
-          beforeSend: function(xhr, settings) {
-            var csrftoken = $.cookie('csrftoken');
-            xhr.setRequestHeader('X-CSRFToken', csrftoken);
-          }
-        });
-
-        var d = $.ajax({
-          type: "POST",
-          url: url,
-          data: JSON.stringify({experiment: name}),
-          contentType: "application/json; charset=utf-8",
-          dataType: "json",
-        });
-
-        d.done(function(data) {
-          the_dialog.close();
-          window.location.href = redirect;
-        });
-
-        d.fail(function(data, text_status) {
-          the_dialog.close();
-          process_error(data, text_status);
-        });
-
-      }
-    },
-    ],
-  });
-  dialog.realize();
-  dialog.open();
+	var dialog = new BootstrapDialog({
+		title: '<i class="fa fa-certificate"></i> Attestation for "' + name + '"',
+		message: '<p>When you attest an experiment, the platform guarantees it is reproducible, therefore all components related to this experiment (including the toolchain, algorithms, libraries and dataformats) will be frozen. This procedure is <strong>not</strong> irreversible. You can always delete locked (i.e. unpublished) attestations. This procedure also does not stop you from forking or creating new revisions of objects used in this experiment.</p>',
+		type: BootstrapDialog.TYPE_PRIMARY,
+		size: BootstrapDialog.SIZE_WIDE,
+		buttons: [
+			{
+				label: 'Cancel',
+				cssClass: 'btn-default',
+				action: function(the_dialog) {
+					the_dialog.close();
+				}
+			},
+			{
+				label: 'Attest',
+				cssClass: 'btn-primary',
+				action: function(the_dialog) {
+
+					$.ajaxSetup({
+						beforeSend: function(xhr, settings) {
+							var csrftoken = $.cookie('csrftoken');
+							xhr.setRequestHeader('X-CSRFToken', csrftoken);
+						}
+					});
+
+					var d = $.ajax({
+						type: "POST",
+						url: url,
+						data: JSON.stringify({experiment: name}),
+						contentType: "application/json; charset=utf-8",
+						dataType: "json",
+					});
+
+					d.done(function(data) {
+						the_dialog.close();
+						window.location.href = redirect;
+					});
+
+					d.fail(function(data, text_status) {
+						the_dialog.close();
+						process_error(data, text_status);
+					});
+
+				}
+			},
+		],
+	});
+	dialog.realize();
+	dialog.open();
 
 }
 
@@ -1738,51 +1738,51 @@ beat.experiments.utils.modal_attest = function (name, url, redirect) {
  **/
 beat.experiments.utils.modal_cancel = function (name, url, redirect) {
 
-  var dialog = new BootstrapDialog({
-    title: '<i class="fa fa-power-off"></i> Stopping experiment "' + name + '"',
-    message: '<p>Choose "Cancel" to halt the stop operation. Choose "Stop" to continue and cancel the experiment. Stopping the experiment execution does not erase cached information, so you can continue later from the point where you have stopped.</p>',
-    type: BootstrapDialog.TYPE_PRIMARY,
-    buttons: [
-    {
-      label: 'Cancel',
-      cssClass: 'btn-default',
-      action: function(the_dialog) {
-        the_dialog.close();
-      }
-    },
-    {
-      label: 'Stop',
-      cssClass: 'btn-primary',
-      action: function(the_dialog) {
-
-        $.ajaxSetup({
-          beforeSend: function(xhr, settings) {
-            var csrftoken = $.cookie('csrftoken');
-            xhr.setRequestHeader('X-CSRFToken', csrftoken);
-          }
-        });
-
-        var d = $.ajax({
-          type: "POST",
-          url: url,
-        });
-
-        d.done(function(data) {
-          the_dialog.close();
-          window.location.href = redirect;
-        });
-
-        d.fail(function(data, text_status) {
-          the_dialog.close();
-          process_error(data, text_status);
-        });
-
-      }
-    },
-    ],
-  });
-  dialog.realize();
-  dialog.open();
+	var dialog = new BootstrapDialog({
+		title: '<i class="fa fa-power-off"></i> Stopping experiment "' + name + '"',
+		message: '<p>Choose "Cancel" to halt the stop operation. Choose "Stop" to continue and cancel the experiment. Stopping the experiment execution does not erase cached information, so you can continue later from the point where you have stopped.</p>',
+		type: BootstrapDialog.TYPE_PRIMARY,
+		buttons: [
+			{
+				label: 'Cancel',
+				cssClass: 'btn-default',
+				action: function(the_dialog) {
+					the_dialog.close();
+				}
+			},
+			{
+				label: 'Stop',
+				cssClass: 'btn-primary',
+				action: function(the_dialog) {
+
+					$.ajaxSetup({
+						beforeSend: function(xhr, settings) {
+							var csrftoken = $.cookie('csrftoken');
+							xhr.setRequestHeader('X-CSRFToken', csrftoken);
+						}
+					});
+
+					var d = $.ajax({
+						type: "POST",
+						url: url,
+					});
+
+					d.done(function(data) {
+						the_dialog.close();
+						window.location.href = redirect;
+					});
+
+					d.fail(function(data, text_status) {
+						the_dialog.close();
+						process_error(data, text_status);
+					});
+
+				}
+			},
+		],
+	});
+	dialog.realize();
+	dialog.open();
 
 }
 
@@ -1800,201 +1800,201 @@ beat.experiments.utils.modal_cancel = function (name, url, redirect) {
  **/
 beat.experiments.utils.modal_add_to_report = function (names, report_list_url) {
 
-  if (!Array.isArray(names)) {
-    BootstrapDialog.alert('The input "names" must be an array');
-    return false;
-  }
-
-  if (names.length == 0) {
-    BootstrapDialog.alert('Select at least 1 experiment to add to a report');
-    return false;
-  }
-
-  $.ajaxSetup({
-    beforeSend: function(xhr, settings) {
-      var csrftoken = $.cookie('csrftoken');
-      xhr.setRequestHeader('X-CSRFToken', csrftoken);
-    }
-  });
-
-  //retrieve list of existing reports - if that succeeds, construct modal form
-  function addToReport(names) {
-    return $.ajax({
-
-      type: 'GET',
-      url: report_list_url + '?fields=name,short_description,add_url',
-
-    }).pipe(function(data) {
-
-      var message = $(document.createElement('div'));
-      message.append($(document.createElement('p')).text('By clicking Add, the experiment(s) will be added to the selected report (if possible). You can cancel the operation by clicking Cancel.'));
-      var form_group = $(document.createElement('div')).addClass('form-group');
-      message.append(form_group);
-      var select = $(document.createElement('select')).addClass('form-control');
-      form_group.append(select);
-      var first_option = $(document.createElement('option'));
-      select.append(first_option);
-      first_option.val('');
-      first_option.attr('disabled', true);
-      first_option.attr('selected', true);
-      first_option.text('Select a report...');
-
-      select.chosen({
-        disable_search_threshold: 5,
-        search_contains: true,
-        allow_single_deselect: true,
-      });
-
-      select.on('chosen:showing_dropdown', function(e, params) {
-        select.find('option:gt(0)').remove();
-        select.find('option:eq(0)').attr('selected', true);
-        data.forEach(function(i) {
-          var opt = $(document.createElement('option'));
-          select.append(opt);
-          opt.val(i.add_url);
-          var div = $(document.createElement('div'));
-          opt.append(div);
-          opt.data('name', i.name);
-          div.text(i.name);
-          if (i.short_description) {
-            var help = $(document.createElement('span')).addClass('help');
-            help.text(' (' + i.short_description + ')');
-                div.append(help);
-          }
-        });
-        select.trigger('chosen:updated');
-        select.trigger('chosen:open');
-      });
-
-      //fix options when selected
-      select.on('change', function(e, params) {
-        var selected = $(this).find('option:selected');
-        selected.text(selected.data('name'));
-        select.trigger('chosen:updated');
-      });
-
-      BootstrapDialog.show({
-        title: '<i class="fa fa-file-text-o fa-lg"></i> Select a report',
-        message: message,
-        type: BootstrapDialog.TYPE_PRIMARY,
-        buttons: [
-        {
-          label: 'Cancel',
-          cssClass: 'btn-default',
-          action: function(the_dialog) {
-            the_dialog.close();
-            return false;
-          },
-        },
-        {
-          label: 'Add',
-          cssClass: 'btn-primary',
-          action: function(the_dialog) {
-            if (!select.val()) {
-              BootstrapDialog.alert({
-                title: '<i class="fa fa-warning"></i> Error',
-                message: 'You must select a report to add experiments to',
-                type: BootstrapDialog.TYPE_WARNING,
-              });
-              return false;
-            }
-            the_dialog.close();
-
-            var post_info = {experiments:names};
-
-            var d = $.ajax({
-              type: "POST",
-              data: JSON.stringify(post_info),
-              url: select.val(),
-              contentType: "application/json; charset=utf-8",
-              dataType: 'json',
-            });
-
-            d.done(function(data, status) {
-
-              var message = $(document.createElement('div'));
-              message.addClass('report-results');
-
-              var sent = post_info.experiments.length;
-              var successful = sent;
-
-              var description = $(document.createElement('h5'));
-              message.append(description);
-
-              if (data !== undefined) {
-                // some experiments have failed
-
-                // adds information about failed/incompatible experiments
-                function _add_list(message, objects, title) {
-                  if (objects === undefined) return;
-                  var length = objects.length;
-                  if (length > 0) {
-                    var _title = $(document.createElement('h5'));
-                    _title.text(title);
-                    message.append(_title);
-                    var ul = $(document.createElement('ul'));
-                    for (var i = 0; i < length; ++i) {
-                      var li = $(document.createElement('li'));
-                      li.text(objects[i]);
-                      ul.append(li);
-                    }
-                    message.append(ul);
-                  }
-                  return length;
-                }
-                if (data.inaccessible_experiments !== undefined)
-                  successful -= _add_list(message, data.inaccessible_experiments,
-                      'These experiments have failed (and cannot be added):');
-                if (data.incompatible_experiments !== undefined)
-                  successful -= _add_list(message, data.incompatible_experiments,
-                      'These experiments have different analyzers (and cannot be added):');
-              }
-
-              var size = BootstrapDialog.SIZE_NORMAL;
-              var type = BootstrapDialog.TYPE_PRIMARY;
-              var title = '<i class="fa fa-check"></i> Report changes';
-              var btn_type = 'btn-primary';
-              if (successful == sent) {
-                description.text('Successfully added ' + sent + ' experiment(s) to report');
-              }
-              else {
-                description.text('Added ' + successful + ' (out of ' + sent + ' in total) experiment(s) to report');
-                size = BootstrapDialog.SIZE_WIDE;
-                type = BootstrapDialog.TYPE_WARNING;
-                btn_type = 'btn-warning';
-                title = '<i class="fa fa-warning"></i> Report changes';
-              }
-
-              BootstrapDialog.show({
-                title: title,
-                message: message,
-                size: size,
-                type: type,
-                buttons: [{
-                  label: 'OK',
-                  cssClass: btn_type,
-                  action: function(dialog){dialog.close();},
-                }]
-              });
-
-              return true;
-            });
-
-            d.fail(function(data, status) {
-              process_error(data, status);
-            });
-
-          },
-        },
-        ],
-      });
-    });
-  }
-
-  addToReport(names).fail(function(data, text_status) {
-    process_error(data, text_status);
-    return false;
-  });
+	if (!Array.isArray(names)) {
+		BootstrapDialog.alert('The input "names" must be an array');
+		return false;
+	}
+
+	if (names.length == 0) {
+		BootstrapDialog.alert('Select at least 1 experiment to add to a report');
+		return false;
+	}
+
+	$.ajaxSetup({
+		beforeSend: function(xhr, settings) {
+			var csrftoken = $.cookie('csrftoken');
+			xhr.setRequestHeader('X-CSRFToken', csrftoken);
+		}
+	});
+
+	//retrieve list of existing reports - if that succeeds, construct modal form
+	function addToReport(names) {
+		return $.ajax({
+
+			type: 'GET',
+			url: report_list_url + '?fields=name,short_description,add_url',
+
+		}).pipe(function(data) {
+			console.log(data);
+			var message = $(document.createElement('div'));
+			message.append($(document.createElement('p')).text('By clicking Add, the experiment(s) will be added to the selected report (if possible). You can cancel the operation by clicking Cancel.'));
+			var form_group = $(document.createElement('div')).addClass('form-group');
+			message.append(form_group);
+			var select = $(document.createElement('select')).addClass('form-control');
+			form_group.append(select);
+			var first_option = $(document.createElement('option'));
+			select.append(first_option);
+			first_option.val('');
+			first_option.attr('disabled', true);
+			first_option.attr('selected', true);
+			first_option.text('Select a report...');
+
+			select.chosen({
+				disable_search_threshold: 5,
+				search_contains: true,
+				allow_single_deselect: true,
+			});
+
+			select.on('chosen:showing_dropdown', function(e, params) {
+				select.find('option:gt(0)').remove();
+				select.find('option:eq(0)').attr('selected', true);
+				data.forEach(function(i) {
+					var opt = $(document.createElement('option'));
+					select.append(opt);
+					opt.val(i.add_url);
+					var div = $(document.createElement('div'));
+					opt.append(div);
+					opt.data('name', i.name);
+					div.text(i.name);
+					if (i.short_description) {
+						var help = $(document.createElement('span')).addClass('help');
+						help.text(' (' + i.short_description + ')');
+						div.append(help);
+					}
+				});
+				select.trigger('chosen:updated');
+				select.trigger('chosen:open');
+			});
+
+			//fix options when selected
+			select.on('change', function(e, params) {
+				var selected = $(this).find('option:selected');
+				selected.text(selected.data('name'));
+				select.trigger('chosen:updated');
+			});
+
+			BootstrapDialog.show({
+				title: '<i class="fa fa-file-text-o fa-lg"></i> Select a report',
+				message: message,
+				type: BootstrapDialog.TYPE_PRIMARY,
+				buttons: [
+					{
+						label: 'Cancel',
+						cssClass: 'btn-default',
+						action: function(the_dialog) {
+							the_dialog.close();
+							return false;
+						},
+					},
+					{
+						label: 'Add',
+						cssClass: 'btn-primary',
+						action: function(the_dialog) {
+							if (!select.val()) {
+								BootstrapDialog.alert({
+									title: '<i class="fa fa-warning"></i> Error',
+									message: 'You must select a report to add experiments to',
+									type: BootstrapDialog.TYPE_WARNING,
+								});
+								return false;
+							}
+							the_dialog.close();
+
+							var post_info = {experiments:names};
+
+							var d = $.ajax({
+								type: "POST",
+								data: JSON.stringify(post_info),
+								url: select.val(),
+								contentType: "application/json; charset=utf-8",
+								dataType: 'json',
+							});
+
+							d.done(function(data, status) {
+
+								var message = $(document.createElement('div'));
+								message.addClass('report-results');
+
+								var sent = post_info.experiments.length;
+								var successful = sent;
+
+								var description = $(document.createElement('h5'));
+								message.append(description);
+
+								if (data !== undefined) {
+									// some experiments have failed
+
+									// adds information about failed/incompatible experiments
+									function _add_list(message, objects, title) {
+										if (objects === undefined) return;
+										var length = objects.length;
+										if (length > 0) {
+											var _title = $(document.createElement('h5'));
+											_title.text(title);
+											message.append(_title);
+											var ul = $(document.createElement('ul'));
+											for (var i = 0; i < length; ++i) {
+												var li = $(document.createElement('li'));
+												li.text(objects[i]);
+												ul.append(li);
+											}
+											message.append(ul);
+										}
+										return length;
+									}
+									if (data.inaccessible_experiments !== undefined)
+										successful -= _add_list(message, data.inaccessible_experiments,
+											'These experiments have failed (and cannot be added):');
+									if (data.incompatible_experiments !== undefined)
+										successful -= _add_list(message, data.incompatible_experiments,
+											'These experiments have different analyzers (and cannot be added):');
+								}
+
+								var size = BootstrapDialog.SIZE_NORMAL;
+								var type = BootstrapDialog.TYPE_PRIMARY;
+								var title = '<i class="fa fa-check"></i> Report changes';
+								var btn_type = 'btn-primary';
+								if (successful == sent) {
+									description.text('Successfully added ' + sent + ' experiment(s) to report');
+								}
+								else {
+									description.text('Added ' + successful + ' (out of ' + sent + ' in total) experiment(s) to report');
+									size = BootstrapDialog.SIZE_WIDE;
+									type = BootstrapDialog.TYPE_WARNING;
+									btn_type = 'btn-warning';
+									title = '<i class="fa fa-warning"></i> Report changes';
+								}
+
+								BootstrapDialog.show({
+									title: title,
+									message: message,
+									size: size,
+									type: type,
+									buttons: [{
+										label: 'OK',
+										cssClass: btn_type,
+										action: function(dialog){dialog.close();},
+									}]
+								});
+
+								return true;
+							});
+
+							d.fail(function(data, status) {
+								process_error(data, status);
+							});
+
+						},
+					},
+				],
+			});
+		});
+	}
+
+	addToReport(names).fail(function(data, text_status) {
+		process_error(data, text_status);
+		return false;
+	});
 
 }
 
@@ -2011,108 +2011,108 @@ beat.experiments.utils.modal_add_to_report = function (names, report_list_url) {
  **/
 beat.experiments.utils.modal_new_experiment = function (toolchain_list_url) {
 
-  $.ajaxSetup({
-    beforeSend: function(xhr, settings) {
-      var csrftoken = $.cookie('csrftoken');
-      xhr.setRequestHeader('X-CSRFToken', csrftoken);
-    }
-  });
-
-  //retrieve list of existing toolchains - if that succeeds, builds modal form
-  var d = $.ajax({
-    type: 'GET',
-    url: toolchain_list_url + '?fields=name,short_description,new_experiment_url',
-  });
-
-  d.fail(function(data, text_status) {
-    process_error(data, text_status);
-    return false;
-  });
-
-  d.done(function(data) {
-
-    var message = $(document.createElement('div'));
-    message.append($(document.createElement('p')).text('Choose a toolchain to create a new experiment.'));
-    var form_group = $(document.createElement('div')).addClass('form-group');
-    message.append(form_group);
-    var select = $(document.createElement('select')).addClass('form-control');
-    form_group.append(select);
-    var first_option = $(document.createElement('option'));
-    select.append(first_option);
-    first_option.val('');
-    first_option.attr('disabled', true);
-    first_option.attr('selected', true);
-    first_option.text('Select a toolchain...');
-
-    select.chosen({
-      disable_search_threshold: 5,
-      search_contains: true,
-      allow_single_deselect: true,
-    });
-
-    select.on('chosen:showing_dropdown', function(e, params) {
-      select.find('option:gt(0)').remove();
-      select.find('option:eq(0)').attr('selected', true);
-      data.forEach(function(i) {
-        var opt = $(document.createElement('option'));
-        select.append(opt);
-        opt.val(i.new_experiment_url);
-        var div = $(document.createElement('div'));
-        opt.append(div);
-        opt.data('name', i.name);
-        div.text(i.name);
-        if (i.short_description) {
-          var help = $(document.createElement('span')).addClass('help');
-          help.text(' (' + i.short_description + ')');
-          div.append(help);
-        }
-      });
-      select.trigger('chosen:updated');
-      select.trigger('chosen:open');
-    });
-
-    //fix options when selected
-    select.on('change', function(e, params) {
-      var selected = $(this).find('option:selected');
-      selected.text(selected.data('name'));
-      select.trigger('chosen:updated');
-    });
-
-    BootstrapDialog.show({
-      title: '<i class="fa fa-cog fa-lg"> Select a toolchain',
-      message: message,
-      type: BootstrapDialog.TYPE_PRIMARY,
-      onshown: function(the_dialog) {
-        select.trigger('chosen:activate');
-      },
-      buttons: [
-      {
-        label: 'Cancel',
-        cssClass: 'btn-default',
-        action: function(the_dialog) {
-          the_dialog.close();
-          return false;
-        },
-      },
-      {
-        label: 'Create',
-        cssClass: 'btn-primary',
-        action: function(the_dialog) {
-          if (!select.val()) {
-            BootstrapDialog.alert({
-              title: '<i class="fa fa-warning"></i> Error',
-              message: 'You must select a toolchain to continue',
-              type: BootstrapDialog.TYPE_WARNING,
-            });
-            return false;
-          }
-          the_dialog.close();
-          window.location.href = select.val();
-        },
-      },
-      ],
-    });
-  });
+	$.ajaxSetup({
+		beforeSend: function(xhr, settings) {
+			var csrftoken = $.cookie('csrftoken');
+			xhr.setRequestHeader('X-CSRFToken', csrftoken);
+		}
+	});
+
+	//retrieve list of existing toolchains - if that succeeds, builds modal form
+	var d = $.ajax({
+		type: 'GET',
+		url: toolchain_list_url + '?fields=name,short_description,new_experiment_url',
+	});
+
+	d.fail(function(data, text_status) {
+		process_error(data, text_status);
+		return false;
+	});
+
+	d.done(function(data) {
+
+		var message = $(document.createElement('div'));
+		message.append($(document.createElement('p')).text('Choose a toolchain to create a new experiment.'));
+		var form_group = $(document.createElement('div')).addClass('form-group');
+		message.append(form_group);
+		var select = $(document.createElement('select')).addClass('form-control');
+		form_group.append(select);
+		var first_option = $(document.createElement('option'));
+		select.append(first_option);
+		first_option.val('');
+		first_option.attr('disabled', true);
+		first_option.attr('selected', true);
+		first_option.text('Select a toolchain...');
+
+		select.chosen({
+			disable_search_threshold: 5,
+			search_contains: true,
+			allow_single_deselect: true,
+		});
+
+		select.on('chosen:showing_dropdown', function(e, params) {
+			select.find('option:gt(0)').remove();
+			select.find('option:eq(0)').attr('selected', true);
+			data.forEach(function(i) {
+				var opt = $(document.createElement('option'));
+				select.append(opt);
+				opt.val(i.new_experiment_url);
+				var div = $(document.createElement('div'));
+				opt.append(div);
+				opt.data('name', i.name);
+				div.text(i.name);
+				if (i.short_description) {
+					var help = $(document.createElement('span')).addClass('help');
+					help.text(' (' + i.short_description + ')');
+					div.append(help);
+				}
+			});
+			select.trigger('chosen:updated');
+			select.trigger('chosen:open');
+		});
+
+		//fix options when selected
+		select.on('change', function(e, params) {
+			var selected = $(this).find('option:selected');
+			selected.text(selected.data('name'));
+			select.trigger('chosen:updated');
+		});
+
+		BootstrapDialog.show({
+			title: '<i class="fa fa-cog fa-lg"> Select a toolchain',
+			message: message,
+			type: BootstrapDialog.TYPE_PRIMARY,
+			onshown: function(the_dialog) {
+				select.trigger('chosen:activate');
+			},
+			buttons: [
+				{
+					label: 'Cancel',
+					cssClass: 'btn-default',
+					action: function(the_dialog) {
+						the_dialog.close();
+						return false;
+					},
+				},
+				{
+					label: 'Create',
+					cssClass: 'btn-primary',
+					action: function(the_dialog) {
+						if (!select.val()) {
+							BootstrapDialog.alert({
+								title: '<i class="fa fa-warning"></i> Error',
+								message: 'You must select a toolchain to continue',
+								type: BootstrapDialog.TYPE_WARNING,
+							});
+							return false;
+						}
+						the_dialog.close();
+						window.location.href = select.val();
+					},
+				},
+			],
+		});
+	});
 }
 
 
@@ -2130,81 +2130,81 @@ beat.experiments.utils.modal_new_experiment = function (toolchain_list_url) {
  **/
 beat.experiments.utils.modal_rename = function (userid, current_name, list_url, update_url) {
 
-  $.ajaxSetup({
-    beforeSend: function(xhr, settings) {
-      var csrftoken = $.cookie('csrftoken');
-      xhr.setRequestHeader('X-CSRFToken', csrftoken);
-    }
-  });
-
-  //retrieve list of existing experiments - if that succeeds, builds modal form
-  var d = $.ajax({
-    type: 'GET',
-    url: list_url + '?fields=short_name,author',
-  });
-
-  d.fail(function(data, text_status) {
-    process_error(data, text_status);
-    return false;
-  });
-
-  d.done(function(data) {
-
-    //filter returned names to only keep author experiments' names
-    data = data.filter(function(e) {
-      return (e.author == userid && e.short_name != current_name);
-    }).map(function(e){ return e.short_name; });
-
-    var message = $(document.createElement('div'));
-    //message copied from templates/experiments/setup.html
-    message.append($('<span class="help">Enter a meaningful name to help you recognize this experiment. Auto-completion will help you in keeping your naming conventions tide. If a chosen name is <span class="text-danger">highlighted in red</span>, it is because it is already being used. In this case, choose another name.</span>'));
-    message.append($('<div class="form-group has-feedback"><label class="control-label" for="settings_name">Name:</label><input class="form-control input-sm" id="modal-rename" type="text" class="label" data-placeholder="Start typing a name..." autocomplete="off" autocorrect="off" autocapitalize="off" value="' + current_name + '"></input><span class="glyphicon glyphicon-remove form-control-feedback" aria-hidden="true"></span>'));
-
-    beat.experiments.dialogs.name_typeahead(message.find('input'), data);
-
-    BootstrapDialog.show({
-      title: '<i class="fa fa-tag fa-lg"></i> Rename experiment',
-      message: message,
-      type: BootstrapDialog.TYPE_PRIMARY,
-      buttons: [
-      {
-        label: 'Cancel',
-        cssClass: 'btn-default',
-        action: function(the_dialog) {
-          the_dialog.close();
-          return false;
-        },
-      },
-      {
-        label: 'Rename',
-        cssClass: 'btn-primary',
-        action: function(the_dialog) {
-          the_dialog.close();
-          var new_name = message.find('input').val().trim();
-          if (new_name === current_name) {
-            the_dialog.close();
-            return false;
-          }
-          var d2 = $.ajax({
-            type: "PUT",
-            url: update_url,
-            data: JSON.stringify({name: new_name}),
-            contentType: "application/json; charset=utf-8",
-            dataType: "json"
-          });
-          d2.fail(function(data, text_status) {
-            process_error(data, text_status);
-            return false;
-          });
-          d2.done(function(data) {
-            window.location.href = data.view_url;
-            return true;
-          });
-        },
-      },
-      ],
-    });
-  });
+	$.ajaxSetup({
+		beforeSend: function(xhr, settings) {
+			var csrftoken = $.cookie('csrftoken');
+			xhr.setRequestHeader('X-CSRFToken', csrftoken);
+		}
+	});
+
+	//retrieve list of existing experiments - if that succeeds, builds modal form
+	var d = $.ajax({
+		type: 'GET',
+		url: list_url + '?fields=short_name,author',
+	});
+
+	d.fail(function(data, text_status) {
+		process_error(data, text_status);
+		return false;
+	});
+
+	d.done(function(data) {
+
+		//filter returned names to only keep author experiments' names
+		data = data.filter(function(e) {
+			return (e.author == userid && e.short_name != current_name);
+		}).map(function(e){ return e.short_name; });
+
+		var message = $(document.createElement('div'));
+		//message copied from templates/experiments/setup.html
+		message.append($('<span class="help">Enter a meaningful name to help you recognize this experiment. Auto-completion will help you in keeping your naming conventions tide. If a chosen name is <span class="text-danger">highlighted in red</span>, it is because it is already being used. In this case, choose another name.</span>'));
+		message.append($('<div class="form-group has-feedback"><label class="control-label" for="settings_name">Name:</label><input class="form-control input-sm" id="modal-rename" type="text" class="label" data-placeholder="Start typing a name..." autocomplete="off" autocorrect="off" autocapitalize="off" value="' + current_name + '"></input><span class="glyphicon glyphicon-remove form-control-feedback" aria-hidden="true"></span>'));
+
+		beat.experiments.dialogs.name_typeahead(message.find('input'), data);
+
+		BootstrapDialog.show({
+			title: '<i class="fa fa-tag fa-lg"></i> Rename experiment',
+			message: message,
+			type: BootstrapDialog.TYPE_PRIMARY,
+			buttons: [
+				{
+					label: 'Cancel',
+					cssClass: 'btn-default',
+					action: function(the_dialog) {
+						the_dialog.close();
+						return false;
+					},
+				},
+				{
+					label: 'Rename',
+					cssClass: 'btn-primary',
+					action: function(the_dialog) {
+						the_dialog.close();
+						var new_name = message.find('input').val().trim();
+						if (new_name === current_name) {
+							the_dialog.close();
+							return false;
+						}
+						var d2 = $.ajax({
+							type: "PUT",
+							url: update_url,
+							data: JSON.stringify({name: new_name}),
+							contentType: "application/json; charset=utf-8",
+							dataType: "json"
+						});
+						d2.fail(function(data, text_status) {
+							process_error(data, text_status);
+							return false;
+						});
+						d2.done(function(data) {
+							window.location.href = data.view_url;
+							return true;
+						});
+					},
+				},
+			],
+		});
+	});
 }
 
 
@@ -2223,16 +2223,16 @@ beat.experiments.utils.modal_rename = function (userid, current_name, list_url,
  *     where the current block state is stored.
  */
 beat.experiments.utils.update_viewer = function (viewer, objects, dt_block_name, dt_block_status) {
-  //gather block information
-  block_status = {};
-  objects.each(function(idx) {
-    var bk_name = $(this).data(dt_block_name);
-    if (!bk_name) return;
-    var bk_status = $(this).data(dt_block_status);
-    if (bk_status === 'cached') bk_status = 'generated'; //< viewer quirk
-    block_status[bk_name] = bk_status;
-  });
-  viewer.updateBlocksStatus(block_status);
+	//gather block information
+	block_status = {};
+	objects.each(function(idx) {
+		var bk_name = $(this).data(dt_block_name);
+		if (!bk_name) return;
+		var bk_status = $(this).data(dt_block_status);
+		if (bk_status === 'cached') bk_status = 'generated'; //< viewer quirk
+		block_status[bk_name] = bk_status;
+	});
+	viewer.updateBlocksStatus(block_status);
 }
 
 
@@ -2263,57 +2263,57 @@ beat.experiments.utils.update_viewer = function (viewer, objects, dt_block_name,
  */
 beat.experiments.utils.update_blocks = function (url, st, dt, unveil, objects, dt_block_name, viewer, interval) {
 
-  var _status = $(st).data(dt);
-
-  if (_status === 'Failed') {
-    beat.experiments.utils.update_viewer(viewer, $(objects), dt_block_name, dt);
-    return;
-  }
-
-  //only updates if in one of the "interesting" states
-  var interesting_states = ['Scheduled', 'Running', 'Canceling'];
-  if (interesting_states.indexOf(_status) <= -1) return;
-
-  function _do_update() {
-    var _status = $(st).data(dt);
-
-    if (interesting_states.indexOf(_status) <= -1) {
-      //experiment changed status - should reload
-      $(unveil).show();
-      if (viewer.running) viewer.onExperimentDone();
-      if (_status === 'Failed') {
-        beat.experiments.utils.update_viewer(viewer, $(objects), dt_block_name,
-            dt);
-      }
-      return;
-    }
-
-    var d = $.get(url);
-
-    d.done(function(data) {
-      var parsed = $($.parseHTML(data));
-      $(objects).each(function(idx) {
-        var _self = $(this);
-        var r = parsed.find('#' + _self.attr('id'));
-        //only replaces if it changed
-        var old_status = _self.data(dt);
-        var new_status = r.data(dt);
-        if (r && old_status !== new_status) _self.replaceWith(r);
-      });
-
-      if (!viewer.running) viewer.onExperimentStarted();
-      beat.experiments.utils.update_viewer(viewer, $(objects), dt_block_name, dt);
-    });
-  }
-
-  //if we get to this point, we install the interval function
-  $.ajaxSetup({
-    beforeSend: function(xhr, settings) {
-      var csrftoken = $.cookie('csrftoken');
-      xhr.setRequestHeader('X-CSRFToken', csrftoken);
-    }
-  });
-
-  var timeout_id = window.setInterval(_do_update, interval);
+	var _status = $(st).data(dt);
+
+	if (_status === 'Failed') {
+		beat.experiments.utils.update_viewer(viewer, $(objects), dt_block_name, dt);
+		return;
+	}
+
+	//only updates if in one of the "interesting" states
+	var interesting_states = ['Scheduled', 'Running', 'Canceling'];
+	if (interesting_states.indexOf(_status) <= -1) return;
+
+	function _do_update() {
+		var _status = $(st).data(dt);
+
+		if (interesting_states.indexOf(_status) <= -1) {
+			//experiment changed status - should reload
+			$(unveil).show();
+			if (viewer.running) viewer.onExperimentDone();
+			if (_status === 'Failed') {
+				beat.experiments.utils.update_viewer(viewer, $(objects), dt_block_name,
+					dt);
+			}
+			return;
+		}
+
+		var d = $.get(url);
+
+		d.done(function(data) {
+			var parsed = $($.parseHTML(data));
+			$(objects).each(function(idx) {
+				var _self = $(this);
+				var r = parsed.find('#' + _self.attr('id'));
+				//only replaces if it changed
+				var old_status = _self.data(dt);
+				var new_status = r.data(dt);
+				if (r && old_status !== new_status) _self.replaceWith(r);
+			});
+
+			if (!viewer.running) viewer.onExperimentStarted();
+			beat.experiments.utils.update_viewer(viewer, $(objects), dt_block_name, dt);
+		});
+	}
+
+	//if we get to this point, we install the interval function
+	$.ajaxSetup({
+		beforeSend: function(xhr, settings) {
+			var csrftoken = $.cookie('csrftoken');
+			xhr.setRequestHeader('X-CSRFToken', csrftoken);
+		}
+	});
+
+	var timeout_id = window.setInterval(_do_update, interval);
 
 }
-- 
GitLab