diff --git a/beat/web/reports/migrations/0004_auto_20170410_1121.py b/beat/web/reports/migrations/0004_auto_20170410_1121.py
new file mode 100644
index 0000000000000000000000000000000000000000..aae102538b57fe7639effcecd329e7809d7b2515
--- /dev/null
+++ b/beat/web/reports/migrations/0004_auto_20170410_1121.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.5 on 2017-04-10 11:21
+from __future__ import unicode_literals
+
+from django.db import migrations
+import json
+import re
+
+def parse_table(table, precision):
+    conv_table = {
+            'fields': ['Experiment'],
+            'precision': precision
+            }
+
+    for row in table:
+        name = row['name']
+        name = re.sub(r'\[.*\]$', '', name)
+
+        if name == 'experiment':
+            continue
+
+        if name == 'experiment.execution_time' or name == 'execution_time':
+            name = 'total_execution_time'
+        elif re.match(r'^execution_time\.', name):
+            name = re.sub(r'execution_time', 'linear_execution_time', name)
+
+        if name.startswith('experiment.'):
+            name = re.sub(r'experiment\.', '', name)
+
+        if '.' in name:
+            segs = name.split('.')
+            name = segs[1] + '.' + segs[0]
+        conv_table['fields'].append(name)
+
+    return conv_table
+
+def parse_plot(plot):
+    conv_plot = {
+        'name': plot['data']['output'][0],
+        'type': plot['required_plotter'][0]
+            }
+    return conv_plot
+
+def experiment_fullname(exp):
+    return '%s/%s/%s/%s/%s' % (exp.author.username, exp.toolchain.author.username, exp.toolchain.name, exp.toolchain.version, exp.name)
+
+def analyzer_fullname(report):
+    return '%s/%s/%s' % (report.analyzer.author.username, report.analyzer.name, report.analyzer.version)
+
+def move_content_to_groups_format(apps, schema_editor):
+    Report = apps.get_model('reports', 'Report')
+
+    for report in Report.objects.all():
+        report_content = json.loads(report.content)
+        # convert to groups format
+        if 'groups' not in report_content:
+            exps = report.experiments.all()
+            # groups: {
+            # 	group1 : {
+            # 		experiments: [],
+            # 		reportItems: [],
+            # 		analyzer: '',
+            # 		aliases: {},
+            # 		idx: 1
+            # 		},
+            # 	...
+            # }
+            obj = {}
+            groups = {}
+            group1 = {
+                    'experiments': [ experiment_fullname(e) for e in exps ],
+                    'reportItems': [],
+                    'analyzer': analyzer_fullname(report) if report.analyzer else '',
+                    'aliases': {},
+                    'idx': 1
+                    }
+            for e in exps:
+                group1['aliases'][experiment_fullname(e)] = e.name
+
+            count_tables = 0
+            count_plots = 0
+            for item_name in report_content:
+                if item_name == 'floating_point_precision' or item_name == 'alias_experiments':
+                    continue
+
+                item = report_content[item_name]
+                item_type = 'table' if item_name.startswith('table') else 'plot'
+                fpp = report_content['floating_point_precision'] if 'floating_point_precision' in report_content else 10
+
+                converted_content = parse_table(item, fpp) if item_type == 'table' else parse_plot(item)
+
+                converted_id = ''
+                if item_type == 'table':
+                    converted_id = 'table_' + str(count_tables)
+                    count_tables += 1
+                else:
+                    converted_id = 'plot_' + str(count_plots)
+                    count_plots += 1
+
+                converted_item = {
+                        'id': converted_id,
+                        'content': converted_content
+                        }
+
+                group1['reportItems'].append(converted_item)
+
+            groups['group1'] = group1
+            obj['groups'] = groups
+            report.content = json.dumps(obj)
+            report.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('reports', '0003_report_last_edited_date'),
+    ]
+
+    operations = [
+            migrations.RunPython(move_content_to_groups_format)
+    ]
diff --git a/beat/web/reports/static/reports/app/controllers/groupsController.js b/beat/web/reports/static/reports/app/controllers/groupsController.js
index 7a0c4bdd0e71ce9cd511512a8809adaced430b0e..e7432840f60395a92caea6fe40a4c80c753b2a7d 100644
--- a/beat/web/reports/static/reports/app/controllers/groupsController.js
+++ b/beat/web/reports/static/reports/app/controllers/groupsController.js
@@ -23,9 +23,9 @@
 /*
  * GroupsController
  * 	provides access to the groups data to Django templates,
- * 	mostly used for handling the removal of experiments from the report
+ * 	used for handling the removal of experiments from the report
  */
-angular.module('reportApp').controller('GroupsController', ['$scope', '$http', 'GroupsService', 'UrlService', function ($scope, $http, GroupsService, UrlService){
+angular.module('reportApp').controller('GroupsController', ['$http', 'UrlService', function ($http, UrlService){
 	let vm = this;
 
 	vm.expNamesToRemove = [];
diff --git a/beat/web/reports/static/reports/app/controllers/reportController.js b/beat/web/reports/static/reports/app/controllers/reportController.js
index bd6a268937dca172534f8827a572649cc877a0ec..2c4700588265357f3fc37a4666a76d2c0445e3c3 100644
--- a/beat/web/reports/static/reports/app/controllers/reportController.js
+++ b/beat/web/reports/static/reports/app/controllers/reportController.js
@@ -19,10 +19,15 @@
  * 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/.
  */
-//This controller retrieves data from the reportFactory/experimentFactory through the REST API and associates it with the $scope
-//The $scope is ultimately bound to the report view
-angular.module('reportApp').controller('reportController',['$scope', 'reportFactory', 'experimentFactory', 'plotterFactory', 'dataFactory', '$q', 'GroupsService', 'ExperimentsService', function ($scope, reportFactory, experimentFactory, plotterFactory, dataFactory, $q, GroupsService, ExperimentsService){
-	$scope.q = $q;
+
+/* reportController
+ *
+ * This controller retrieves data from the reportFactory/experimentFactory through the REST API and associates it with the $scope.
+ * The $scope is ultimately bound to the report view.
+ *
+ * This controller is "deprecated" - it needs to be completely removed, and should not be expanded/built upon.
+ */
+angular.module('reportApp').controller('reportController',['$scope', 'reportFactory', 'ReportService', function ($scope, reportFactory, ReportService){
 	$scope.user;
 	$scope.report_id;
 	$scope.url_prefix;
@@ -35,375 +40,14 @@ angular.module('reportApp').controller('reportController',['$scope', 'reportFact
 	$scope.table_item_content = [];
 	$scope.selectedObject = {};
 
-	$scope.tables_details = {};
-	$scope.plots_details = {};
-	$scope.report_experiments_blocks = {};
-	$scope.report_experiments_blocks_merged_blocks = [];
-	$scope.report_experiments_blocks_computation_times = {};
-
-	$scope.report_experiments_alias = {};
-	$scope.report_experiments_alias_from_content = {};
-	$scope.floating_point_precision = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-	$scope.floating_point_precision.selected = 10;
-	$scope.report_algorithm_parameters = {};
-	$scope.report_algorithm_parameters_experiment = {};
-	$scope.sorted_tables = [];   //tables that were sorted
-	$scope.sorted_experiments_keys_tables = {};
-	$scope.sorted_experiments_alias_keys_tables = {};
-	$scope.sorted_experiments_keys_reverse = {};
-	$scope.sorted_experiments_keys_tables_sortkey = {};
-
-	$scope.init = function(user, report_id, url_prefix, data_itemcontent_file, data_table_itemcontent_file){
+	$scope.init = function(user, report_id, url_prefix){
 		$scope.user   = user;
 		$scope.report_id = report_id;
 		$scope.url_prefix = url_prefix;
-		getReportData($scope.user, $scope.report_id);
-		$scope.item_content = dataFactory.getData(data_itemcontent_file);
-		$scope.table_item_content = dataFactory.getData(data_table_itemcontent_file);
 	};
 
-	$scope.initWithReportNumber = function(report_number, url_prefix, data_itemcontent_file, data_table_itemcontent_file){
+	$scope.initWithReportNumber = function(report_number, url_prefix){
 		$scope.report_number = report_number;
 		$scope.url_prefix = url_prefix;
-		getReportDataFromNumber($scope.report_number);
-		$scope.item_content = dataFactory.getData(data_itemcontent_file);
-		$scope.table_item_content = dataFactory.getData(data_table_itemcontent_file);
 	};
-
-	// saves a data object to an different object by iterating through
-	// the data obj's entries and assigning them one-by-one.
-	// Why? JS is pass-by-value, but complex datatypes (Objects, arrays, funcs) are pointers.
-	// Angular depends on these pointers in two-way bindings of complex datatypes.
-	// If one assigns a different obj, the two-way binding is lost!
-	function mutateSave(saveObj, dataObj){
-		let t = typeof(dataObj);
-
-		if (t === 'object' && saveObj) {
-			Object.entries(dataObj)
-			.forEach(([key, val]) => {
-				saveObj[key] = val;
-			});
-		} else if (t === 'array' && typeof(saveObj) === 'array') {
-			saveObj.length = 0;
-			dataObj.forEach(e => saveObj.push(e));
-		}
-	}
-
-	// saves the report data from the API to the $scope
-	function saveReportData(reportData){
-		mutateSave($scope.report, reportData);
-
-		$scope.report.url_prefix = $scope.url_prefix;
-
-		if($scope.report.content.alias_experiments != undefined){
-			mutateSave($scope.report_experiments_alias, $scope.report.content.alias_experiments);
-		}
-
-		// save groups data to the model
-		GroupsService.loadGroups($scope.report.content.groups);
-		const isEditable = $scope.report.is_owner && $scope.report.status === 'editable';
-		GroupsService.setEditable(isEditable);
-	}
-
-	function getReportData(user, report_id){
-		reportFactory.getReportInformation(user, report_id, $scope.url_prefix)
-		.success(function (reportData){
-			saveReportData(reportData);
-			let promises = [];
-
-			function lastTask(){
-				getPlotters();
-			}
-
-			let experiments = $scope.report.experiments;
-			if(experiments != undefined){
-				promises.push(getExperimentDataForAuthor(experiments));
-				$scope.q.all(promises).then(lastTask);
-			}
-		})
-		.error(function (error){
-			$scope.status = 'Unable to load report data: ' + error.message;
-		});
-
-		$('#tabs_progress').hide();
-	}
-
-	function getReportDataFromNumber(report_number, $q){
-		reportFactory.getReportInformationFromNumber(report_number, $scope.url_prefix)
-		.success(function (reportData){
-			saveReportData(reportData);
-			let promises = [];
-
-			function lastTask(){
-				getPlotters();
-			}
-
-			let experiments = $scope.report.experiments;
-			if(experiments != undefined){
-				promises.push(getExperimentDataReportNumber(report_number));
-				$scope.q.all(promises).then(lastTask);
-			}
-		})
-		.error(function (error){
-			$scope.status = 'Unable to load report data: ' + error.message;
-		});
-
-		$('#tabs_progress').hide();
-	}
-
-	function getExperimentDataReportNumber(report_number){
-		ExperimentsService.loadExperiments($scope.url_prefix, report_number);
-		experimentFactory.getAllExperimentResults($scope.url_prefix, report_number)
-		.then(function (experiments){
-			mutateSave($scope.report_experiments, experiments);
-
-			$scope.report.all_experiments = experiments;
-			let experiment_aliases = Object.keys(experiments);
-			for(let i = 0; i < experiment_aliases.length; i++){
-				let experiment_alias = experiment_aliases[i];
-
-				if($scope.report.content.alias_experiments != undefined){
-					if($scope.report.content.alias_experiments[experiment_alias] != undefined){
-						setExperimentAliasFromContent(experiment_alias, $scope.report.content.alias_experiments[experiment_alias]);
-					}
-					else{
-						setExperimentAlias(experiment_alias);
-					}
-				}
-				else{
-					setExperimentAlias(experiment_alias);
-				}
-				intersectBlocks(experiment_alias);
-				algorithmParameters(experiment_alias);
-			}
-			//sort experiments in case it's sorted
-			if(!(jQuery.isEmptyObject($scope.report.content.sorted_tables_experiments)) &&
-				!(jQuery.isEmptyObject($scope.report.content.sorted_tables_alias_experiments)) &&
-				!(jQuery.isEmptyObject($scope.report.content.sorted_tables_keys_reverse)) &&
-				!(jQuery.isEmptyObject($scope.report.content.sorted_tables_sortkey))){
-				$scope.sorted_experiments_keys_tables = $scope.report.content.sorted_tables_experiments;
-				if($scope.report.status == "locked"){
-					//special case for locked reports
-					$scope.sorted_experiments_keys_tables = $scope.report.content.sorted_tables_alias_experiments;
-				}
-				$scope.sorted_experiments_alias_keys_tables = $scope.report.content.sorted_tables_alias_experiments;
-				$scope.sorted_experiments_keys_reverse = $scope.report.content.sorted_tables_keys_reverse;
-				$scope.sorted_experiments_keys_tables_sortkey = $scope.report.content.sorted_tables_sortkey;
-			}
-			for(let i = 0; i < Object.keys($scope.sorted_experiments_keys_tables_sortkey).length; i++){
-				$scope.sorted_tables.push(Object.keys($scope.sorted_experiments_keys_tables_sortkey)[i]);
-			}
-		});
-	}
-
-	function getExperimentDataForAuthor(all_experiments){
-		ExperimentsService.loadExperiments($scope.user, $scope.report_id, $scope.url_prefix);
-		experimentFactory.getAllExperimentResultsForAuthor($scope.user, $scope.report_id, $scope.url_prefix)
-		.then(function (experiments){
-			mutateSave($scope.report_experiments, experiments);
-
-			$scope.report.all_experiments = experiments;
-			let experiment_aliases = Object.keys(experiments);
-			for(let i = 0; i < experiment_aliases.length; i++){
-				let experiment_alias = experiment_aliases[i];
-
-				if($scope.report.content.alias_experiments != undefined){
-					if($scope.report.content.alias_experiments[experiment_alias] != undefined){
-						setExperimentAliasFromContent(experiment_alias, $scope.report.content.alias_experiments[experiment_alias]);
-					}
-					else{
-						setExperimentAlias(experiment_alias);
-					}
-				}
-				else{
-					setExperimentAlias(experiment_alias);
-				}
-				intersectBlocks(experiment_alias);
-				algorithmParameters(experiment_alias);
-			}
-
-			//sort experiments in case it's sorted
-			if(!(jQuery.isEmptyObject($scope.report.content.sorted_tables_experiments)) &&
-				!(jQuery.isEmptyObject($scope.report.content.sorted_tables_alias_experiments)) &&
-				!(jQuery.isEmptyObject($scope.report.content.sorted_tables_keys_reverse)) &&
-				!(jQuery.isEmptyObject($scope.report.content.sorted_tables_sortkey))){
-				$scope.sorted_experiments_keys_tables = $scope.report.content.sorted_tables_experiments;
-				if($scope.report.status == "locked"){
-					//special case for locked reports
-					$scope.sorted_experiments_keys_tables = $scope.report.content.sorted_tables_alias_experiments;
-				}
-				$scope.sorted_experiments_alias_keys_tables = $scope.report.content.sorted_tables_alias_experiments;
-				$scope.sorted_experiments_keys_reverse = $scope.report.content.sorted_tables_keys_reverse;
-				$scope.sorted_experiments_keys_tables_sortkey = $scope.report.content.sorted_tables_sortkey;
-			}
-			for(let i = 0; i < Object.keys($scope.sorted_experiments_keys_tables_sortkey).length; i++){
-				$scope.sorted_tables.push(Object.keys($scope.sorted_experiments_keys_tables_sortkey)[i]);
-			}
-
-		});
-	}
-
-	function getExperimentData(experiment_id){
-		experimentFactory.getExperimentInformation($scope.url_prefix, experiment_id)
-		.then(function (experimentData){
-			let experiments = $scope.report_experiments;
-			experiments[experiment_id] = experimentData;
-			mutateSave($scope.report_experiments, experiments);
-			$scope.report.all_experiments = experiments;
-
-			if($scope.report.content.alias_experiments != undefined){
-				if($scope.report.content.alias_experiments[experiment_id] != undefined){
-					setExperimentAliasFromContent(experiment_id, $scope.report.content.alias_experiments[experiment_id]);
-				}
-				else{
-					setExperimentAlias(experiment_id);
-				}
-			}
-			else{
-				setExperimentAlias(experiment_id);
-			}
-
-			intersectBlocks(experiment_id);
-			algorithmParameters(experiment_id);
-		});
-	}
-
-	function getPlotters(){
-		plotterFactory.getPlotters($scope.url_prefix)
-		.success(function (plottersData){
-			$scope.report.plotters = plottersData;
-			getPlotterParameterData();
-		})
-		.error(function (error){
-			$scope.status = 'Unable to load plotters data - ' + ' : ' + error.message;
-		});
-	}
-
-	function getDefaultPlotters(){
-		plotterFactory.getDefaultPlotters($scope.url_prefix)
-		.success(function (defaultPlottersData){
-			$scope.report.defaultplotters = defaultPlottersData;
-			checkContent();
-		})
-		.error(function (error){
-			$scope.status = 'Unable to load default plotters parameter data - ' + ' : ' + error.message;
-		});
-	}
-
-	function getPlotterParameterData(){
-		plotterFactory.getPlotterParameter($scope.url_prefix)
-		.success(function (plotterParameterData){
-			$scope.report.plotterparameter = plotterParameterData;
-			//$scope.report.plotterparameter.push({"name":"Default"});
-			getDefaultPlotters();
-		})
-		.error(function (error){
-			$scope.status = 'Unable to load plotter parameter data - ' + ' : ' + error.message;
-		});
-	}
-
-	function checkContent(){
-		if($scope.report.content != undefined){
-
-			let orderkeys = [];
-			angular.forEach($scope.report.content, function(value, key){
-				orderkeys.push(key);
-			});
-
-			orderkeys.sort(function(a,b){
-				a = a.split("_");
-				b = b.split("_");
-				a_num = parseInt(a[a.length-1]) || -1;
-				b_num = parseInt(b[b.length-1]) || -1;
-
-				return a_num - b_num;
-			});
-			$scope.report.orderedcontent = orderkeys;
-
-			if($scope.report.content.floating_point_precision != undefined){
-				$scope.floating_point_precision.selected = $scope.report.content.floating_point_precision;
-			}
-
-			for(let i = 0; i < $scope.report.orderedcontent.length; i++){
-				let id_content = $scope.report.orderedcontent[i];
-				addElementToReport(id_content);
-			}
-		}
-	}
-
-	function addElementToReport(id_content){
-		let type = id_content.split("_")[0];
-		$scope.$broadcast("addSavedElement", id_content, type);
-	}
-
-	function intersectBlocks(experiment_id){
-		$scope.report_experiments_blocks[experiment_id] = Object.keys($scope.report_experiments[experiment_id].blocks_status);
-		let values = $.map($scope.report_experiments_blocks[experiment_id], function(value, key) { return value; });
-		$scope.report_experiments_blocks_merged_blocks.push(values);
-	}
-
-	function algorithmParameters(experiment_id){
-		//go through globals
-		angular.forEach($scope.report_experiments[experiment_id].declaration.globals, function(value_algorithm, key_algorithm){
-			//only get algorithms
-			if(key_algorithm.indexOf("/") != -1){
-				$scope.report_algorithm_parameters[key_algorithm] = value_algorithm;
-				let experiment_algorithm_parameter = {};
-
-				if($scope.report_algorithm_parameters_experiment[experiment_id] == undefined){
-					$scope.report_algorithm_parameters_experiment[experiment_id] = {};
-				}
-
-				if($scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm] == undefined){
-					$scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm] = {};
-				}
-
-				//go through each blocks of the experiment and save parameters for given algorithm
-				angular.forEach($scope.report_experiments[experiment_id].declaration.blocks, function(value_block, key_block){
-					//check blocks that matches globals
-					if($scope.report_experiments[experiment_id].declaration.blocks[key_block]['algorithm'] == key_algorithm){
-						if($scope.report_experiments[experiment_id].declaration.blocks[key_block]['parameters'] != undefined){
-							//override globals parameters value if block contains 'parameters' key
-							angular.forEach($scope.report_experiments[experiment_id].declaration.blocks[key_block]['parameters'], function(value_block_parameter, key_block_parameter){
-								if($scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter] == undefined){
-									$scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter] = [];
-								}
-								$scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter].push(value_block_parameter);
-
-								$scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter] = unique($scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter]);
-							});
-						}
-						else{
-							//get globals parameters value if block doesn't contain 'parameters' key
-							angular.forEach($scope.report_experiments[experiment_id].declaration.globals[key_algorithm], function(value_block_parameter, key_block_parameter){
-								if($scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter] == undefined){
-									$scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter] = [];
-								}
-								$scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter].push(value_block_parameter);
-								$scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter] = unique($scope.report_algorithm_parameters_experiment[experiment_id][key_algorithm][key_block_parameter]);
-							});
-						}
-					}
-				});
-			}
-		});
-	}
-
-	function setExperimentAlias(experiment_id){
-		$scope.report_experiments_alias[experiment_id] = experiment_id.split("/").pop();
-		$scope.report_experiments_alias_from_content[experiment_id] = experiment_id.split("/").pop();
-	}
-
-	function setExperimentAliasFromContent(experiment_id, experiment_alias){
-		$scope.report_experiments_alias[experiment_id] = experiment_alias;
-		$scope.report_experiments_alias_from_content[experiment_id] = experiment_alias;
-	}
-
-	function unique(list){
-		let result = [];
-		$.each(list, function(i, e) {
-			if ($.inArray(e, result) == -1) result.push(e);
-		});
-		return result;
-	}
 }]);
diff --git a/beat/web/reports/static/reports/app/data/itemcontent.json b/beat/web/reports/static/reports/app/data/itemcontent.json
deleted file mode 100644
index 297831665fdde6e7bf49f9756e1f9e6c697617a8..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/data/itemcontent.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
-    {
-       "identifier": "table",
-       "name": "Table",
-       "description": "Compare elements in a table"
-    },
-    {
-       "identifier": "plot",
-       "name": "Figure",
-       "description": "Compare graphical elements (ROC, bar plots, etc.)"
-    }
-]
diff --git a/beat/web/reports/static/reports/app/data/table_itemcontent.json b/beat/web/reports/static/reports/app/data/table_itemcontent.json
deleted file mode 100644
index 13e37560096b4947b4c0a316a0bb9f71a7698260..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/data/table_itemcontent.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[
-    {
-       "identifier": "tableresults",
-       "name": "Results",
-       "description": "Only compare experiments results"
-    }
-]
diff --git a/beat/web/reports/static/reports/app/directives/addReportItem.js b/beat/web/reports/static/reports/app/directives/addReportItem.js
deleted file mode 100644
index 4e57c6a54b4c1d4bb51618ae2fc87b5ff10e7ec7..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/addReportItem.js
+++ /dev/null
@@ -1,778 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-//Directive for opening smart_selector list on "Add a report item" button click that displays options
-
-angular.module('reportApp').directive("addreportitem", function($compile){
-	return function(scope, element, attrs){
-		scope.$on("addSavedElement", function(event, id_content, type){
-			let num_tables_in_content = 0;
-			let num_figures_in_content = 0;
-			for(let i = 0; i < Object.keys(scope.report.content).length; i++){
-				if(Object.keys(scope.report.content)[i].indexOf("table_") == 0){
-					num_tables_in_content++;
-				}
-				else if(Object.keys(scope.report.content)[i].indexOf("chart_") == 0){
-					num_figures_in_content++;
-				}
-			}
-
-			if(type == "table"){
-				let table_id = id_content;
-				let table_details = undefined;
-
-				//take the one with more elements
-				if(Object.keys(scope.tables_details).length > num_tables_in_content){
-					table_details = scope.tables_details[id_content];
-				}
-				else{
-					table_details = scope.report.content[id_content];
-				}
-
-				let name_to_remove = "experiment";
-				for(let i = 0; i < table_details.length; i++){
-					if(table_details[i].name == "experiment"){
-						table_details.splice(i,1);
-						break;
-					}
-				}
-
-				scope.tables_details[table_id] = table_details;
-
-				let accessnumber = "no_number";
-				if(scope.report.number == scope.report_number){
-					accessnumber = "number";
-				}
-				let html_div_code = '<div class="panel panel-default" id="' + table_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + table_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-table"> Table</i></a></h4></div><div id="collapse-' + table_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div class="panel-body" table-dynamic monster=' + scope.num_report_items + " tableid=" + table_id + " reportstatus=" + scope.report.status + " accessnumber=" + accessnumber + " urlprefix=" + scope.url_prefix + '></div></div></div>';
-
-				angular.element(document.getElementById('space-for-report-items')).append($compile(html_div_code)(scope));
-				if(parseInt(table_id.split("_").pop(-1)) >= scope.max_num){
-					scope.max_num = parseInt(table_id.split("_").pop(-1));
-					scope.num_report_items = scope.max_num;
-				}
-
-				scope.num_report_items++;
-
-
-			}
-			else if(type == "chart"){
-
-				let content_detail = {};
-				let chart_id = id_content;
-
-				let plot_details = undefined;
-
-				//take the one with more elements
-				if(Object.keys(scope.plots_details).length > num_figures_in_content){
-					plot_details = scope.plots_details[chart_id];
-				}
-				else{
-					plot_details = scope.report.content[chart_id];
-				}
-
-
-
-				content_detail["name"] = plot_details.data.output[0];
-				//content_detail["description"] = scope.report.content[chart_id].data.plotter;
-				if(plot_details.data.plotter != undefined){
-					content_detail["selected_plotter"] = plot_details.data.plotter;
-				}
-				if(plot_details.selected_template != undefined){
-					content_detail["selected_template"] = plot_details.selected_template;
-				}
-				if(plot_details.data.merged != undefined){
-					content_detail["merged"] = plot_details.data.merged;
-				}
-				if(plot_details.merged != undefined){
-					content_detail["merged"] = plot_details.merged;
-				}
-
-				let html_div_code = '<div class="panel panel-default" id="' + chart_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + chart_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-area-chart"> ' + content_detail.name + '</i></a></h4></div><div id="collapse-' + chart_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div id="'+chart_id+'" class="panel-body chart {$ report.status $}"></div></div></div>';
-				generate_element(scope, "plot", html_div_code);
-				let element = document.getElementById(chart_id);
-				let label_element  = $(document.createElement('div'));
-				label_element.addClass('row');
-
-				let accessnumber = "no_number";
-				if(scope.report.number == scope.report_number){
-					accessnumber = "number";
-				}
-
-				let prepend_buttons = '<div class="col-sm-12"><div class="action-buttons report-buttons pull-left">';
-				let append_buttons = '</div></div>';
-
-				let a_export  = '<div class="btn-group"><button type="button" class="btn btn-primary btn-sm dropdown-toggle item_export ' + scope.report.status + ' ' + accessnumber + '" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-download fa-lg"></i> Export Figure <span class="caret"></span></button><ul class="dropdown-menu"><li id="PNG" buttonexportitem><a>PNG</a></li><li id="JPEG" buttonexportitem><a>JPEG</a></li><li id="PDF" buttonexportitem><a>PDF</a></li></ul></div>';
-				if(scope.report.status == "editable" && scope.report.number != scope.report_number) {
-					let a_element = '<button class="btn btn-danger btn-sm item_delete ' + scope.report.status + ' ' + accessnumber + '" buttondeleteitem><i class="fa fa-times fa-lg"></i> Delete</button>';
-					label_element.html(prepend_buttons + a_element + a_export + append_buttons);
-				}
-				else {
-					label_element.html(prepend_buttons + a_export + append_buttons);
-				}
-				;
-				$(element).find('.panel-body').append(label_element);
-				angular.element(document.getElementById('space-for-report-items')).append($compile(element)(scope));
-				//let html_dropdown  = "<chartdropdown id='selector_" + chart_id +"'></chartdropdown>";
-				//angular.element(document.getElementById(chart_id)).append($compile(html_dropdown)(scope));
-
-				_retrieve_chart(scope, content_detail, element);
-
-				if(parseInt(chart_id.split("_").pop(-1)) >= scope.max_num){
-					scope.max_num = parseInt(chart_id.split("_").pop(-1));
-					scope.num_report_items = scope.max_num;
-				}
-
-				scope.num_report_items++;
-
-			}
-
-		}
-		);
-
-		scope.$on("redrawGraphElement", function(event, id_content, type){
-			let content_detail = {};
-			let chart_id = id_content;
-			content_detail["name"] = scope.report.content[chart_id].data.output[0];
-			//content_detail["description"] = scope.report.content[chart_id].data.plotter;
-			if(scope.report.content[chart_id].data.plotter != undefined){
-				content_detail["selected_plotter"] = scope.report.content[chart_id].data.plotter;
-			}
-			if(scope.report.content[chart_id].data.parameter != undefined){
-				content_detail["selected_template"] = scope.report.content[chart_id].data.parameter;
-			}
-			if(scope.report.content[chart_id].data.merged != undefined){
-				content_detail["merged"] = scope.report.content[chart_id].data.merged;
-			}
-
-			let html_div_code = '<div class="panel panel-default" id="' + chart_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + chart_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-area-chart"> ' + content_detail.name + '</i></a></h4></div><div id="collapse-' + chart_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div id="'+chart_id+'" class="panel-body chart"></div></div></div>';
-			generate_element(scope, "plot", html_div_code);
-			let element = document.getElementById(chart_id);
-			let label_element  = $(document.createElement('div'));
-			label_element.addClass('row');
-
-			let accessnumber = "no_number";
-			if(scope.report.number == scope.report_number){
-				accessnumber = "number";
-			}
-
-			let prepend_buttons = '<div class="col-sm-12"><div class="action-buttons report-buttons pull-left">';
-			let append_buttons = '</div></div>';
-
-			let a_export  = '<div class="btn-group"><button type="button" class="btn btn-primary btn-sm dropdown-toggle item_export ' + scope.report.status + ' ' + accessnumber + '" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-download fa-lg"></i> Export Figure <span class="caret"></span></button><ul class="dropdown-menu"><li id="PNG" buttonexportitem><a>PNG</a></li><li id="JPEG" buttonexportitem><a>JPEG</a></li><li id="PDF" buttonexportitem><a>PDF</a></li></ul></div>';
-			if(scope.report.status == "editable" && scope.report.number != scope.report_number) {
-				let a_element = '<button class="btn btn-danger btn-sm item_delete ' + scope.report.status + ' ' + accessnumber + '" buttondeleteitem><i class="fa fa-times fa-lg"></i> Delete</button>';
-				label_element.html(prepend_buttons + a_element + a_export + append_buttons);
-			}
-			else {
-				label_element.html(prepend_buttons + a_export + append_buttons);
-			}
-			;
-			$(element).find('.panel-body').append(label_element);
-			angular.element(document.getElementById('space-for-report-items')).append($compile(element)(scope));
-			//let html_dropdown  = "<chartdropdown id='selector_" + chart_id +"'></chartdropdown>";
-			//angular.element(document.getElementById(chart_id)).html($compile(html_dropdown)(scope));
-
-			_retrieve_chart(scope, content_detail, element);
-
-		});
-
-		//add new report item
-		element.bind("click", function(){
-			let left = $('.add_item').offset().left - $('.add_item').width() + 400;
-			let top = $('.add_item').offset().top;
-			smart_selector.display(scope.item_content.content, left, top);
-
-			let allblocks = [];
-			for(let i = 0; i < scope.report_experiments_blocks_merged_blocks.length; i++){
-				allblocks.push(scope.report_experiments_blocks_merged_blocks[i]);
-			}
-			scope.report.common_blocks = arraysInCommon(allblocks);
-		});
-
-		//handles first selector selection (table/plot)
-		if(smart_selector != undefined){
-			smart_selector.onEntrySelected = function(item_selected){
-
-				let left = $('.add_item').offset().left - $('.add_item').width() + 400;
-				let top = $('.add_item').offset().top;
-
-				let next_items_for_selector = prepareContent(scope, smart_selector.entries[smart_selector.selected_entry].identifier);
-				smart_selector_detail.display(next_items_for_selector, left, top);
-			};
-		}
-
-		//handles next selector detail selection
-		if(smart_selector_detail != undefined){
-			smart_selector_detail.onEntrySelected = function(item_selected){
-				let left = $('.add_item').offset().left - $('.add_item').width() + 400;
-				let top = $('.add_item').offset().top;
-
-
-				let result_next_items = nextDetailContent(scope, smart_selector.entries[smart_selector.selected_entry].identifier, smart_selector_detail.entries[smart_selector_detail.selected_entry]);
-
-				if(!result_next_items[0]){
-					multiple_selector.display(result_next_items[1], left, top);
-				}
-			};
-		}
-
-		//handles first creation of table based on options
-		if(multiple_selector != undefined){
-			multiple_selector.onEntrySelected = function(item_selected, data){
-				for(let i = 0; i < multiple_selector.entries.length; i++){
-					if(multiple_selector.entries[i].name == item_selected){
-						multiple_selector.entries[i].selected = true;
-					}
-				}
-			};
-
-			multiple_selector.onEntryDeselected = function(item_selected, data){
-				for(let i = 0; i < multiple_selector.entries.length; i++){
-					if(multiple_selector.entries[i].name == item_selected){
-						multiple_selector.entries[i].selected = false;
-					}
-				}
-			};
-
-			multiple_selector.onClose = function(){
-				//check if at least one item is selected
-				let checkOneSelected = false;
-				angular.forEach(multiple_selector.entries, function(value, key){
-					if(value.selected)
-						checkOneSelected = true;
-				});
-
-				if(checkOneSelected){
-					let table_id = 'table' + '_' + scope.num_report_items;
-
-					scope.tables_details[table_id] = multiple_selector.entries;
-
-					let html_div_code = '<div class="panel panel-default" id="' + table_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + table_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-table"> Table</i></a></h4></div><div id="collapse-' + table_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div class="panel-body" table-dynamic monster=' + scope.num_report_items + " tableid=" + table_id + " reportstatus=" + scope.report.status + " urlprefix=" + scope.url_prefix + '></div></div></div>';
-
-					angular.element(document.getElementById('space-for-report-items')).append($compile(html_div_code)(scope));
-
-					scope.report.orderedcontent.push(table_id);
-					scope.num_report_items++;
-				}
-			};
-		}
-
-		//handles single table updates via settings option
-		if(multiple_selector_updater != undefined){
-			multiple_selector_updater.onEntrySelected = function(item_selected, data){
-				for(let i = 0; i < multiple_selector_updater.entries.length; i++){
-					if(multiple_selector_updater.entries[i].name == item_selected){
-						multiple_selector_updater.entries[i].selected = true;
-					}
-				}
-			};
-
-			multiple_selector_updater.onEntryDeselected = function(item_selected, data){
-				for(let i = 0; i < multiple_selector_updater.entries.length; i++){
-					if(multiple_selector_updater.entries[i].name == item_selected){
-						multiple_selector_updater.entries[i].selected = false;
-					}
-				}
-			};
-
-			multiple_selector_updater.onClose = function(){
-
-				//check if at least one item is selected
-				let checkOneSelected = false;
-				angular.forEach(multiple_selector_updater.entries, function(value, key){
-					if(value.selected)
-						checkOneSelected = true;
-				});
-
-				if(checkOneSelected){
-					let element = document.getElementById(multiple_selector_updater.current_table);
-
-					$(element).attr('id', null);
-					$compile(element)(scope);
-					$(element).attr('id', multiple_selector_updater.current_table);
-				}
-			};
-
-		};
-
-		//Prepare the content for selector initialization
-		function prepareContent(scope, content_identifier){
-			let next_content_items = [];
-
-			switch(content_identifier){
-				case 'table':
-					next_content_items =  scope.table_item_content.content;
-					break;
-				case 'plot':
-					angular.forEach(scope.report_experiments, function(value, key){
-						angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer){
-							if(value_analyzer.algorithm == scope.report.analyzer){
-								scope.report_experiments[key].analyzer_block = key_analyzer;
-							}
-						});
-
-						let plottable_result = [];
-						angular.forEach(value.results[scope.report_experiments[key].analyzer_block], function(value_result_item, key_result_item){
-							if(value_result_item.type.startsWith("plot/")){
-								plottable_result.push(key_result_item);
-							}
-
-						});
-
-						scope.report_experiments[key].plottable_blocks = plottable_result;
-
-					});
-
-					//Results block are the same for all experiments from same analyzer.
-					//So just grab information from one of them for smart_selector items
-					let single_experiment = scope.report.experiments[0];
-
-					//create smart_selector items
-					angular.forEach(scope.report_experiments[single_experiment].plottable_blocks, function(value_plottable, key_plottable){
-						let plot_type = scope.report_experiments[single_experiment].results[scope.report_experiments[single_experiment].analyzer_block][value_plottable].type;
-						let item_dict = {};
-						item_dict["identifier"] = value_plottable;
-						item_dict["name"] = value_plottable;
-						item_dict["description"] = plot_type;
-
-						next_content_items.push(item_dict);
-					});
-					break;
-			}
-
-			return next_content_items;
-		}
-
-		//Get the detailed content for next selector
-		function nextDetailContent(scope, content_identifier, sub_content){
-			let is_end = false;
-			let next_items = [];
-			let return_contents = [];
-			switch(content_identifier){
-				case 'table':
-					is_end = false;
-					angular.forEach(scope.report_experiments, function(value, key){
-						angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer){
-							if(value_analyzer.algorithm == scope.report.analyzer){
-								scope.report_experiments[key].analyzer_block = key_analyzer;
-							}
-						});
-
-						let table_result = [];
-						angular.forEach(value.results[scope.report_experiments[key].analyzer_block], function(value_result_item, key_result_item){
-							if(!(value_result_item.type.startsWith("plot/"))){
-								table_result.push(key_result_item);
-							}
-
-						});
-
-						scope.report_experiments[key].table_result_blocks = table_result;
-
-					});
-
-					//Results block are the same for all experiments from same analyzer.
-					//So just grab information from one of them for smart_selector items
-					let single_experiment = scope.report.experiments[0];
-
-					//create smart_selector items
-					angular.forEach(scope.report_experiments[single_experiment].table_result_blocks, function(value_table_result, key_table_result){
-
-						let table_item_data = scope.report_experiments[single_experiment].results[scope.report_experiments[single_experiment].analyzer_block][value_table_result];
-						let item_dict = {};
-						item_dict["identifier"] = value_table_result;
-						item_dict["name"] = value_table_result;
-						item_dict["description"] = table_item_data.type;
-						if(table_item_data.primary)
-							item_dict["selected"] = true;
-						else
-							item_dict["selected"] = false;
-						item_dict["data"] = table_item_data;
-
-						next_items.push(item_dict);
-					});
-
-					//adding execution time entry information for common blocks
-					angular.forEach(scope.report.common_blocks, function(value_table_result, key_table_result){
-						let item_dict = {};
-						item_dict["identifier"] = "execution_time." + value_table_result;
-						item_dict["name"] = "execution_time." + value_table_result;
-						item_dict["selected"] = false;
-						next_items.push(item_dict);
-
-					});
-
-					//adding total execution time entry information for experiment
-					let item_dict = {};
-					item_dict["identifier"] = "experiment.execution_time";
-					item_dict["name"] = "experiment.execution_time";
-					item_dict["selected"] = false;
-					next_items.push(item_dict);
-
-					//adding globals algorithms parameters
-					angular.forEach(scope.report_algorithm_parameters, function(value_algorithm_parameters, key_algorithm_parameters){
-						for(let i = 0; i < Object.keys(value_algorithm_parameters).length; i++){
-							let item_dict = {};
-							item_dict["identifier"] = "algorithm_parameter." + key_algorithm_parameters + "__" +Object.keys(value_algorithm_parameters)[i];
-							item_dict["name"] = "algorithm_parameter." + key_algorithm_parameters + "__" +Object.keys(value_algorithm_parameters)[i];
-							item_dict["selected"] = false;
-							next_items.push(item_dict);
-						}
-
-					});
-
-					break;
-				case 'plot':
-					is_end = true;
-					let chart_id = 'chart_' + sub_content.identifier + '_graph' + '_' + scope.num_report_items;
-					let html_div_code = '<div class="panel panel-default" id="' + chart_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + chart_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-area-chart"> ' + sub_content.name + '</i></a></h4></div><div id="collapse-' + chart_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div id="'+chart_id+'" class="panel-body chart {$ report.status $}"></div></div></div>';
-					generate_element(scope, content_identifier, html_div_code);
-					let element = document.getElementById(chart_id);
-					let label_element  = $(document.createElement('div'));
-					label_element.addClass('row');
-
-					let accessnumber = "no_number";
-					if(scope.report.number == scope.report_number){
-						accessnumber = "number";
-					}
-
-					let prepend_buttons = '<div class="col-sm-12"><div class="action-buttons report-buttons pull-left">';
-					let append_buttons = '</div></div>';
-
-					let a_export  = '<div class="btn-group"><button type="button" class="btn btn-primary btn-sm dropdown-toggle item_export ' + scope.report.status + ' ' + accessnumber + '" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-download fa-lg"></i> Export Figure <span class="caret"></span></button><ul class="dropdown-menu"><li id="PNG" buttonexportitem><a>PNG</a></li><li id="JPEG" buttonexportitem><a>JPEG</a></li><li id="PDF" buttonexportitem><a>PDF</a></li></ul></div>';
-					if(scope.report.status == "editable" && scope.report.number != scope.report_number) {
-						let a_element = '<button class="btn btn-danger btn-sm item_delete ' + scope.report.status + ' ' + accessnumber + '" buttondeleteitem><i class="fa fa-times fa-lg"></i> Delete</button>';
-						label_element.html(prepend_buttons + a_element + a_export + append_buttons);
-					}
-					else {
-						label_element.html(prepend_buttons + a_export + append_buttons);
-					}
-					$(element).find('.panel-body').append(label_element);
-
-					angular.element(document.getElementById('space-for-report-items')).append($compile(element)(scope));
-
-					_retrieve_chart(scope, sub_content, element);
-					scope.report.orderedcontent.push(chart_id);
-					break;
-			}
-
-			return_contents = [is_end, next_items];
-
-			return return_contents;
-
-		}
-
-		//Generate and compile DOM element
-		function generate_element(scope, content_identifier, html_div_code){
-			switch(content_identifier){
-				case 'table':
-					angular.element(document.getElementById('space-for-report-items')).append($compile(html_div_code)(scope));
-					scope.num_report_items++;
-					break;
-				case 'plot':
-					angular.element(document.getElementById('space-for-report-items')).append($compile(html_div_code)(scope));
-					scope.num_report_items++;
-					break;
-			}
-
-		}
-
-		//Retrieve chart from api and display on proper item
-		function _get_chart(scope, sub_content, content_type, chart_id){
-			let required_plotter = [];
-			let set_plotter = '';
-			if(sub_content.selected_plotter != undefined){
-				//required_plotter.push(sub_content.selected_plotter);
-				set_plotter = sub_content.selected_plotter;
-				let requested_dataformat = '';
-				//Get other plotters for required dataformat
-				for (let i = 0; i < scope.report.plotters.length; i++){
-					if(scope.report.plotters[i].name == sub_content.selected_plotter){
-						requested_dataformat = scope.report.plotters[i].dataformat;
-						break;
-					}
-				}
-
-				//Get default plotter for required dataformat
-				for (let i = 0; i < scope.report.defaultplotters.length; i++){
-					if(scope.report.defaultplotters[i].dataformat == requested_dataformat){
-						sub_content.defaultplotter = scope.report.defaultplotters[i];
-						break;
-					}
-				}
-
-				required_plotter.push(sub_content.defaultplotter.plotter);
-
-				//Get other plotters for required dataformat
-				for (let i = 0; i < scope.report.plotters.length; i++){
-					if(scope.report.plotters[i].dataformat == requested_dataformat && scope.report.plotters[i].name != sub_content.defaultplotter.plotter){
-						required_plotter.push(scope.report.plotters[i].name);
-					}
-				}
-			}
-			else{
-				//Get default plotter for required dataformat
-				for (let i = 0; i < scope.report.defaultplotters.length; i++){
-					if(scope.report.defaultplotters[i].dataformat == sub_content.description){
-						sub_content.defaultplotter = scope.report.defaultplotters[i];
-						break;
-					}
-				}
-
-				required_plotter.push(sub_content.defaultplotter.plotter);
-				set_plotter = sub_content.defaultplotter.plotter;
-
-				//Get other plotters for required dataformat
-				for (let i = 0; i < scope.report.plotters.length; i++){
-					if(scope.report.plotters[i].dataformat == sub_content.description && scope.report.plotters[i].name != sub_content.defaultplotter.plotter){
-						required_plotter.push(scope.report.plotters[i].name);
-					}
-				}
-			}
-
-			let plotterparameter = [];
-			//Get other plotterparameter
-			for (let i = 0; i < scope.report.plotterparameter.length; i++){
-				plotterparameter.push(scope.report.plotterparameter[i].name);
-			}
-
-			let chart_name = sub_content.name;
-
-			let legend_experiments = '';
-			let alias_experiments = [];
-			for(let i = 0; i < scope.report.experiments.length; i++){
-				if(i == 0)
-					legend_experiments = scope.report_experiments_alias[scope.report.experiments[i]];
-				else
-					legend_experiments = legend_experiments+ "&" +scope.report_experiments_alias[scope.report.experiments[i]];
-
-				alias_experiments.push(scope.report_experiments_alias[scope.report.experiments[i]]);
-			}
-
-			let request_data = {
-				experiment:   alias_experiments,
-				analyzer:     [scope.report.analyzer],
-				output:       [chart_name],
-				plotter:      set_plotter,
-				legend:       legend_experiments,
-				report_number:  scope.report.number,
-				//height:       300,
-				//width:        400,
-			};
-
-			base_url = scope.report.url_prefix;
-
-			let plot_detail = {};
-			plot_detail["required_plotter"] = required_plotter;
-			plot_detail["data"] =  {
-				analyzer:     [scope.report.analyzer],
-				output:       [chart_name],
-				plotter:      set_plotter,
-			};
-
-			if(sub_content.selected_template != undefined){// && sub_content.selected_template != "Default")
-				plot_detail["selected_template"] = sub_content.selected_template;
-				request_data.parameter = plot_detail["selected_template"];
-			}
-			else{
-				plot_detail["selected_template"] = sub_content.defaultplotter.parameter;
-				request_data.parameter = plot_detail["selected_template"];
-			}
-
-			if(sub_content.merged != undefined){// && sub_content.selected_template != "Default")
-				plot_detail["merged"] = sub_content.merged;
-				request_data.merged = plot_detail["merged"];
-			}
-			else{
-				plot_detail["merged"] = true;
-				request_data.merged = plot_detail["merged"];
-			}
-
-			beat.experiments.utils.getPlotData(base_url,
-				request_data, required_plotter, plotterparameter, content_type,
-				function(r_image_data, selected_content_type) {
-					download(r_image_data, chart_id + "." + content_type.toLowerCase(), selected_content_type);
-				}
-			);
-		}
-
-
-		//Retrieve chart from api and display on proper item
-		function _retrieve_chart(scope, sub_content, container){
-			let required_plotter = [];
-			let set_plotter = '';
-			if(sub_content.selected_plotter != undefined){
-				//required_plotter.push(sub_content.selected_plotter);
-				set_plotter = sub_content.selected_plotter;
-				let requested_dataformat = '';
-				//Get other plotters for required dataformat
-				for (let i = 0; i < scope.report.plotters.length; i++){
-					if(scope.report.plotters[i].name == sub_content.selected_plotter){
-						requested_dataformat = scope.report.plotters[i].dataformat;
-						break;
-					}
-				}
-
-				//Get default plotter for required dataformat
-				for (let i = 0; i < scope.report.defaultplotters.length; i++){
-					if(scope.report.defaultplotters[i].dataformat == requested_dataformat){
-						sub_content.defaultplotter = scope.report.defaultplotters[i];
-						break;
-					}
-				}
-
-				required_plotter.push(sub_content.defaultplotter.plotter);
-
-				//Get other plotters for required dataformat
-				for (let i = 0; i < scope.report.plotters.length; i++){
-					if(scope.report.plotters[i].dataformat == requested_dataformat && scope.report.plotters[i].name != sub_content.defaultplotter.plotter){
-						required_plotter.push(scope.report.plotters[i].name);
-					}
-				}
-			}
-			else{
-				//Get default plotter for required dataformat
-				for (let i = 0; i < scope.report.defaultplotters.length; i++){
-					if(scope.report.defaultplotters[i].dataformat == sub_content.description){
-						sub_content.defaultplotter = scope.report.defaultplotters[i];
-						break;
-					}
-				}
-
-				required_plotter.push(sub_content.defaultplotter.plotter);
-				set_plotter = sub_content.defaultplotter.plotter;
-
-				//Get other plotters for required dataformat
-				for (let i = 0; i < scope.report.plotters.length; i++){
-					if(scope.report.plotters[i].dataformat == sub_content.description && scope.report.plotters[i].name != sub_content.defaultplotter.plotter){
-						required_plotter.push(scope.report.plotters[i].name);
-					}
-				}
-			}
-
-			let plotterparameter = [];
-			//Get other plotterparameter
-			for (let i = 0; i < scope.report.plotterparameter.length; i++){
-				plotterparameter.push(scope.report.plotterparameter[i].name);
-			}
-
-			let chart_name = sub_content.name;
-
-			let legend_experiments = '';
-			let alias_experiments = [];
-
-			for(let i = 0; i < scope.report.experiments.length; i++){
-				if(i == 0)
-					legend_experiments = scope.report_experiments_alias[scope.report.experiments[i]];
-				else
-					legend_experiments = legend_experiments+ "&" +scope.report_experiments_alias[scope.report.experiments[i]];
-
-				if(Object.keys(scope.$parent.report_experiments_alias_from_content).length === 0)
-					alias_experiments.push(scope.report_experiments_alias[scope.report.experiments[i]]);
-				else
-					alias_experiments.push(scope.report_experiments_alias_from_content[scope.report.experiments[i]]);
-			}
-
-			let request_data = {
-				experiment:   alias_experiments,
-				analyzer:     [scope.report.analyzer],
-				output:       [chart_name],
-				plotter:      set_plotter,
-				legend:       legend_experiments,
-				report_number: scope.report.number,
-				//height:       300,
-				//width:        400,
-			};
-
-			base_url = scope.report.url_prefix;
-			//scope.plots_details[container.id];
-
-			let plot_detail = {};
-			plot_detail["required_plotter"] = required_plotter;
-			plot_detail["data"] =  {
-				analyzer:     [scope.report.analyzer],
-				output:       [chart_name],
-				plotter:      set_plotter,
-			};
-
-
-			if(sub_content.selected_template != undefined){// && sub_content.selected_template != "Default")
-				plot_detail["selected_template"] = sub_content.selected_template;
-				request_data.parameter = plot_detail["selected_template"];
-			}
-			else{
-				plot_detail["selected_template"] = sub_content.defaultplotter.parameter;
-				request_data.parameter = plot_detail["selected_template"];
-			}
-
-			if(sub_content.merged != undefined){// && sub_content.selected_template != "Default")
-				plot_detail["merged"] = sub_content.merged;
-				request_data.merged = plot_detail["merged"];
-			}
-			else{
-				plot_detail["merged"] = true;
-				request_data.merged = plot_detail["merged"];
-			}
-
-			scope.plots_details[container.id]= plot_detail;
-
-			if(scope.report.status == "editable" && scope.report.number != scope.report_number){
-				beat.experiments.utils.displayPlot(base_url, $(container).find('.panel-body')[0],
-					request_data, required_plotter, plotterparameter, false,
-
-					function(r_plotter, r_plotterparameter, r_merged) {
-						scope.plots_details[container.id]["data"]["plotter"] = r_plotter;
-						scope.plots_details[container.id]["selected_template"] = r_plotterparameter;
-						scope.plots_details[container.id]["merged"] = r_merged;
-					}
-				);
-			}
-			else{
-				beat.experiments.utils.displayPlot(base_url, $(container).find('.panel-body')[0],
-					request_data, [], [], false,
-
-					function(r_plotter, r_plotterparameter, r_merged) {
-					}
-				);
-			}
-		}
-
-		function arraysInCommon(arrays){
-			let i, common,
-				L= arrays.length, min= Infinity;
-			while(L){
-				if(arrays[--L].length<min){
-					min= arrays[L].length;
-					i= L;
-				}
-			}
-			common= arrays.splice(i, 1)[0];
-			return common.filter(function(itm, indx){
-				if(common.indexOf(itm)== indx){
-					return arrays.every(function(arr){
-						return arr.indexOf(itm)!= -1;
-					});
-				}
-			});
-		}
-
-	};
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/aliasExperiment.js b/beat/web/reports/static/reports/app/directives/aliasExperiment.js
deleted file mode 100644
index 5fda415e6ed13ff121bcace03aa0ddcdf93ff61a..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/aliasExperiment.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to handle table settings click
-angular.module('reportApp').directive("aliasexperiment", function($compile){
-	return {
-		link:function(scope, element, attrs){
-			let alias_name = scope.$parent.$parent.$parent.$parent.report_experiments_alias[scope.name];
-			createAlias(scope.name, alias_name);
-
-			element.bind("click", function(){
-				let input_element = document.getElementById("input_"+scope.name);
-				let icon_element = document.getElementById("icon_"+scope.name);
-				let button_element = document.getElementById("button_alias_"+scope.name);
-				if(! $(input_element).hasClass("input-disabled")){
-					if ($(input_element).val()){
-						$(input_element).addClass("input-disabled");
-						$(input_element).attr("disabled", true);
-						$(icon_element).removeClass("fa-unlock");
-						$(icon_element).addClass("fa-lock");
-						$(button_element).addClass("setalias");
-						let experiment_name = attrs.id.split("button_alias_")[1];
-						let alias_name = $(input_element).val();
-						$(input_element).val(alias_name);
-						createAlias(experiment_name, alias_name);
-					}
-					else{
-						alert("Alias for experiment" + scope.name + " can't be empty!");
-					}
-				}
-				else{
-					$(input_element).removeClass("input-disabled");
-					$(input_element).attr("disabled", false);
-					$(icon_element).removeClass("fa-lock");
-					$(icon_element).addClass("fa-unlock");
-					$(button_element).removeClass("setalias");
-
-				}
-			});
-
-			function createAlias(experiment_name, alias_name){
-				scope.report_experiments_alias[experiment_name] = alias_name;
-				scope.$parent.$parent.$parent.$parent.report_experiments_alias[experiment_name] = alias_name;
-
-				if(!scope.$$phase){
-					//$digest or $apply
-					let parent_scope = scope.$parent.$parent.$parent.$parent;
-					let table_details = parent_scope.tables_details;
-
-					for(let i = 0; i < parent_scope.report.orderedcontent.length; i++){
-						let element = document.getElementById(parent_scope.report.orderedcontent[i]);
-
-						$(element).remove();
-
-						let id_content = parent_scope.report.orderedcontent[i];
-						let type = id_content.split("_")[0];
-						parent_scope.$parent.$broadcast("addSavedElement", id_content, type);
-					}
-				}
-			}
-		}
-	};
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/buttonDeleteItem.js b/beat/web/reports/static/reports/app/directives/buttonDeleteItem.js
deleted file mode 100644
index d23011aea97b6a475a17b23b54f77e60887b008d..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/buttonDeleteItem.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to handle table settings click
-angular.module('reportApp').directive("buttondeleteitem", function(){
-	return {
-		link:function(scope, element, attrs){
-			element.bind("click", function(){
-				let elementToRemove = element.context.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
-				beat.ui.report.remove_item('report_remove_item', elementToRemove, scope);
-			});
-		}
-	};
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/buttonExportItem.js b/beat/web/reports/static/reports/app/directives/buttonExportItem.js
deleted file mode 100644
index 5fdd72eae6acfb0c1d76d417690bfbb548265bdd..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/buttonExportItem.js
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to export graph/tables on click
-angular.module('reportApp').directive("buttonexportitem", function(){
-	return {
-		link:function(scope, element, attrs){
-			element.bind("click", function(){
-				let the_element = element.context.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
-				if(the_element.id.indexOf("chart_") == 0){
-					let content_detail = {};
-					let chart_id = the_element.id;
-
-					content_detail["name"] = scope.plots_details[chart_id].data.output[0];
-
-					if(scope.plots_details[chart_id].data.plotter != undefined){
-						content_detail["selected_plotter"] = scope.plots_details[chart_id].data.plotter;
-					}
-					if(scope.plots_details[chart_id].data.parameter != undefined){
-						content_detail["selected_template"] = scope.plots_details[chart_id].data.parameter;
-					}
-					if(scope.plots_details[chart_id].data.merged != undefined){
-						content_detail["merged"] = scope.plots_details[chart_id].data.merged;
-					}
-
-					let accessnumber = "no_number";
-					if(scope.report.number == scope.report_number){
-						accessnumber = "number";
-					}
-					_get_chart(scope, content_detail, element[0].id, chart_id);
-				}
-				else if(the_element.id.indexOf("table_") == 0){
-					let separator = ",";
-					let csv_text = export_table_as_csv(scope, the_element.id, separator);
-					let output_filename = the_element.id + ".csv";
-					download(csv_text, output_filename, "text/plain");
-				}
-			});
-		}
-	};
-
-	function export_table_as_csv(scope, table_id, separator){
-		let report_experiments = scope.$parent.report_experiments;
-		let report_experiments_alias = scope.$parent.report_experiments_alias;
-		let floating_point_precision = scope.$parent.floating_point_precision;
-		let report = scope.report;
-		let final_table = [];
-		let table_headers = [];
-		angular.forEach(scope.tables_details[table_id], function(table_value, table_key){
-			if(table_value.selected){
-				table_headers.push(table_value.name);
-			}
-		});
-
-		final_table.push(table_headers);
-
-		angular.forEach(report_experiments, function(experiment_value, experiment_key){
-			let table_items = [];
-
-			angular.forEach(table_headers, function(table_value, table_key){
-				let experiment_name = experiment_key;
-				let analyzer_block = report_experiments[experiment_name].analyzer_block;
-				let report_algorithm_parameters_experiment = scope.$parent.report_algorithm_parameters_experiment;
-
-				if(table_value != "experiment"){
-					if(analyzer_block == undefined){
-						angular.forEach(report_experiments, function(value, key){
-							angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer){
-								if(value_analyzer.algorithm == report.analyzer && (key_analyzer in report_experiments[experiment_name].declaration.analyzers)){
-									report_experiments[experiment_name].analyzer_block = key_analyzer;
-									analyzer_block = report_experiments[experiment_name].analyzer_block;
-								}
-							});
-						});
-					}
-
-					if(table_value.indexOf("execution_time.") == 0){
-						//execution time information
-						let block_name = table_value.split("execution_time.")[1].split("[s]")[0];
-						if(Object.keys(report_experiments[experiment_name].execution_info).length === 0){
-							table_items.push("-");
-						}
-						else{
-							if(report_experiments[experiment_name].execution_info[block_name] == undefined){
-								table_items.push("-");
-							}
-							else{
-								table_items.push((report_experiments[experiment_name].execution_info[block_name].linear_execution_time).toFixed(floating_point_precision.selected));
-							}
-						}
-					}
-					else if(table_value.indexOf("experiment.") == 0){
-						//total execution time information
-						let block_name = table_value.split("execution_time.")[1];
-						let total_time = 0;
-						if(Object.keys(report_experiments[experiment_name].execution_info).length === 0){
-							total_time = "-";
-						}
-						else{
-							angular.forEach(report_experiments[experiment_name].execution_info, function(value, key){
-								total_time += value.linear_execution_time;
-							});
-						}
-
-						table_items.push(total_time.toFixed(floating_point_precision.selected));
-					}
-					else if(table_value.indexOf("algorithm_parameter.") == 0){
-						//total execution time information
-						let block_name = table_value.split("algorithm_parameter.")[1];
-						let algorithm_name = block_name.split("__")[0];
-						let parameter_name = block_name.split("__")[1];
-						if(report_algorithm_parameters_experiment[experiment_name][algorithm_name] != undefined){
-							let value = "";
-							for(let i = 0; i < report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name].length; i++){
-								if(i > 0 && i < report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name].length -1 ){
-									value += ",";
-								}
-								value = report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name][i];
-							}
-
-							table_items.push(value);
-						}
-						else{
-							table_items.push("-");
-						}
-					}
-					else{
-						//results information
-						table_items.push((report_experiments[experiment_name].results[analyzer_block][table_value].value).toFixed(floating_point_precision.selected));
-					}
-				}
-				else{
-					let experiment_alias = report_experiments_alias[experiment_name];
-					table_items.push(experiment_alias);
-				}
-
-			});
-
-			final_table.push(table_items);
-
-		});
-
-		let csv_text = "";
-		for(let i = 0; i < final_table.length; i++){
-			if(i != 0){
-				csv_text += "\n";
-			}
-
-			for(let j = 0; j < final_table[i].length; j++){
-				if( j != 0){
-					csv_text += separator;
-				}
-				csv_text += final_table[i][j];
-			}
-		}
-
-		let pre_base64_text = "data:text/plain;base64,";
-		let base_64_csv_text = pre_base64_text + Base64.encode(csv_text);
-		return base_64_csv_text;
-	}
-
-	//Retrieve chart from api and display on proper item
-	function _get_chart(scope, sub_content, content_type, chart_id){
-		let required_plotter = [];
-		let set_plotter = '';
-		if(sub_content.selected_plotter != undefined){
-			//required_plotter.push(sub_content.selected_plotter);
-			set_plotter = sub_content.selected_plotter;
-			let requested_dataformat = '';
-			//Get other plotters for required dataformat
-			for (let i = 0; i < scope.report.plotters.length; i++){
-				if(scope.report.plotters[i].name == sub_content.selected_plotter){
-					requested_dataformat = scope.report.plotters[i].dataformat;
-					break;
-				}
-			}
-
-			//Get default plotter for required dataformat
-			for (let i = 0; i < scope.report.defaultplotters.length; i++){
-				if(scope.report.defaultplotters[i].dataformat == requested_dataformat){
-					sub_content.defaultplotter = scope.report.defaultplotters[i];
-					break;
-				}
-			}
-
-			required_plotter.push(sub_content.defaultplotter.plotter);
-
-			//Get other plotters for required dataformat
-			for (let i = 0; i < scope.report.plotters.length; i++){
-				if(scope.report.plotters[i].dataformat == requested_dataformat && scope.report.plotters[i].name != sub_content.defaultplotter.plotter){
-					required_plotter.push(scope.report.plotters[i].name);
-				}
-			}
-		}
-		else{
-			//Get default plotter for required dataformat
-			for (let i = 0; i < scope.report.defaultplotters.length; i++){
-				if(scope.report.defaultplotters[i].dataformat == sub_content.description){
-					sub_content.defaultplotter = scope.report.defaultplotters[i];
-					break;
-				}
-			}
-
-			required_plotter.push(sub_content.defaultplotter.plotter);
-			set_plotter = sub_content.defaultplotter.plotter;
-
-			//Get other plotters for required dataformat
-			for (let i = 0; i < scope.report.plotters.length; i++){
-				if(scope.report.plotters[i].dataformat == sub_content.description && scope.report.plotters[i].name != sub_content.defaultplotter.plotter){
-					required_plotter.push(scope.report.plotters[i].name);
-				}
-			}
-		}
-
-		let plotterparameter = [];
-		//Get other plotterparameter
-		for (let i = 0; i < scope.report.plotterparameter.length; i++){
-			plotterparameter.push(scope.report.plotterparameter[i].name);
-		}
-
-		let chart_name = sub_content.name;
-
-		let legend_experiments = '';
-		for(let i = 0; i < scope.report.experiments.length; i++){
-			if(i == 0)
-				legend_experiments = scope.report_experiments_alias[scope.report.experiments[i]];
-			else
-				legend_experiments = legend_experiments+ "&" +scope.report_experiments_alias[scope.report.experiments[i]];
-		}
-
-		let request_data = {
-			experiment:   scope.report.experiments,
-			analyzer:     [scope.report.analyzer],
-			output:       [chart_name],
-			plotter:      set_plotter,
-			legend:       legend_experiments,
-			//height:       300,
-			//width:        400,
-		};
-
-		base_url = scope.report.url_prefix;
-
-		let plot_detail = {};
-		plot_detail["required_plotter"] = required_plotter;
-		plot_detail["data"] = request_data;
-		if(sub_content.selected_template != undefined){// && sub_content.selected_template != "Default")
-			plot_detail["selected_template"] = sub_content.selected_template;
-			request_data.parameter = plot_detail["selected_template"];
-		}
-		else{
-			plot_detail["selected_template"] = sub_content.defaultplotter.parameter;
-			request_data.parameter = plot_detail["selected_template"];
-		}
-
-		if(sub_content.merged != undefined){// && sub_content.selected_template != "Default")
-			plot_detail["merged"] = sub_content.merged;
-			request_data.merged = plot_detail["merged"];
-		}
-		else{
-			plot_detail["merged"] = true;
-			request_data.merged = plot_detail["merged"];
-		}
-
-		beat.experiments.utils.getPlotData(base_url,
-			request_data, required_plotter, plotterparameter, content_type,
-			function(r_image_data, selected_content_type) {
-				download(r_image_data, chart_id + "." + content_type.toLowerCase(), selected_content_type);
-			}
-		);
-	}
-
-
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/buttonSettings.js b/beat/web/reports/static/reports/app/directives/buttonSettings.js
deleted file mode 100644
index 06062c6b2638708e92543d4dbcea787e5c06d640..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/buttonSettings.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to handle table settings click
-angular.module('reportApp').directive("buttonsettings", function(){
-	return {
-		link:function(scope, element, attrs){
-			element.bind("click", function(){
-				let name_to_remove = "experiment";
-				let tables_details = scope.$parent.tables_details[scope.dattrs.tableid];
-				for(let i = 0; i < tables_details.length; i++){
-					if(tables_details[i].name == "experiment"){
-						tables_details.splice(i,1);
-						break;
-					}
-				}
-				multiple_selector_updater.current_table = scope.dattrs.tableid;
-				multiple_selector_updater.display(tables_details);
-			});
-		}
-	};
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/edit/addGroupMenu.js b/beat/web/reports/static/reports/app/directives/edit/addGroupMenu.js
index 40dcee254cf0f470b02e624a51f3fd740d1b6dad..dcefe7f83a0fedd4b9110c930596efdfe8f8e5bf 100644
--- a/beat/web/reports/static/reports/app/directives/edit/addGroupMenu.js
+++ b/beat/web/reports/static/reports/app/directives/edit/addGroupMenu.js
@@ -23,7 +23,7 @@
 /*
  * groupAddGroupMenu
  * Desc:
- * 	represents the menu for creating groups
+ * 	the menu & validating for creating a new group
  */
 angular.module('reportApp')
 .directive("groupAddGroupMenu", ['GroupsService', function(GroupsService){
@@ -32,12 +32,16 @@ angular.module('reportApp')
 		},
 		link: function(scope){
 			scope.newGroupName = { val: '' };
+			// validates the user input
 			scope.hasError = (val) => {
 				// if val is undefined, empty, or a dup, its an err
 				const isErr = !val || val.length === 0 || GroupsService.groups.find(g => g.name === val);
 				// cast to boolean
 				return !!isErr;
 			};
+
+			// creates a new group if the new group name is valid
+			// wipes the input on successful creation
 			scope.createGroup = () => {
 				if(scope.hasError(scope.newGroupName.val)){
 					return;
diff --git a/beat/web/reports/static/reports/app/directives/edit/addItemsMenu.js b/beat/web/reports/static/reports/app/directives/edit/addItemsMenu.js
index d19b96086f035070a8b8fdace3f78b5472b7d4c5..d4ee0c8a1db1d38f35a7c836dce4861702a4c4cc 100644
--- a/beat/web/reports/static/reports/app/directives/edit/addItemsMenu.js
+++ b/beat/web/reports/static/reports/app/directives/edit/addItemsMenu.js
@@ -23,7 +23,7 @@
 /*
  * groupAddItemsMenu
  * Desc:
- * 	represents the menu for adding report items to a group
+ * 	the button group for adding report items (plot, table, text) to a group
  */
 angular.module('reportApp')
 .directive("groupAddItemsMenu", ['ExperimentsService', 'GroupsService', function(ExperimentsService, GroupsService){
@@ -32,18 +32,22 @@ angular.module('reportApp')
 			group: '='
 		},
 		link: function(scope){
-			scope.isEditable = GroupsService.isEditable;
+			// finds the id for the next report item of
+			// the given type
+			// by looking at the existing items
 			const getNextItemId = (type) => {
 				let count = Object.values(scope.group.reportItems)
 				.filter(v => v.id.includes(type))
 				.length
 				;
+
 				let nextId = `${type}_${count}`;
 				return nextId;
 			};
 
 			scope.plottables = ExperimentsService.plottables;
 
+			// helper func for adding a table
 			scope.addNewTable = () => {
 				const id = getNextItemId('table');
 
@@ -63,31 +67,47 @@ angular.module('reportApp')
 				const defaultFields = Array.from(defaultFieldsSet);
 				console.log(defaultFields);
 
-				scope.group.addReportItem(id, {
+				// tables have an arr of selected fields
+				// and a float precision
+				let content = {
 					fields: defaultFields,
 					precision: 10
-				});
+				};
+
+				scope.group.addReportItem(id, content);
 			};
 
-			scope.addNewPlot = (content) => {
+			// helper func for adding a plot
+			scope.addNewPlot = (plot) => {
 				let id = getNextItemId('plot');
+
+				// plots have a given name (by analyzer)
+				// and a plot type
+				let content = {
+					name: plot.label,
+					type: plot.type
+				};
+
 				scope.group.addReportItem(id, content);
 			};
 
+			// helper func for adding a text block
 			scope.addNewText = () => {
 				let id = getNextItemId('text');
-				scope.group.addReportItem(id, '');
+				// text blocks just have raw RST
+				let content = '';
+				scope.group.addReportItem(id, content);
 			};
 		},
 		template: `
 <div class="btn-group" role="group">
-	<button ng-disabled='!isEditable' type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+	<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
 		Add Plot
 		<span class="caret"></span>
 	</button>
 	<ul class='dropdown-menu' ng-repeat='(expName, plots) in plottables' ng-if='expName === group.experiments[0]'>
 		<li ng-repeat='plot in plots'>
-			<a href='#' ng-click='addNewPlot(plot.label)'>{{ plot.label }} <i>({{ plot.type }})</i></a>
+			<a href='#' ng-click='addNewPlot(plot)'>{{ plot.label }} <i>({{ plot.type }})</i></a>
 		</li>
 	</ul>
 </div>
@@ -95,7 +115,7 @@ angular.module('reportApp')
 	Add Table
 </button>
 <div class="btn-group" role="group">
-	<button ng-disabled='!isEditable'
+	<button
 		ng-click='addNewText()'
 		type="button"
 		class="btn btn-default"
diff --git a/beat/web/reports/static/reports/app/directives/edit/itemContainer.js b/beat/web/reports/static/reports/app/directives/edit/itemContainer.js
index 3aed999c49d25f8dd93bd3b9059a387d610ffb22..1361c99e7b35d4ce483cb519e395fbd603f01226 100644
--- a/beat/web/reports/static/reports/app/directives/edit/itemContainer.js
+++ b/beat/web/reports/static/reports/app/directives/edit/itemContainer.js
@@ -23,7 +23,9 @@
 /*
  * groupItemContainer
  * Desc:
- * 	container for an item in the group
+ * 	container for an item in the group.
+ * 	depending of the type of the item (found in the item's id)
+ * 	it creates a table, plot, or text item
  */
 angular.module('reportApp')
 .directive("groupItemContainer", [function(){
@@ -34,6 +36,7 @@ angular.module('reportApp')
 		},
 		link: function(scope){
 			scope.item = scope.reportItem;
+			// report-wide-unique prefix for the item to use
 			scope.domId = `${scope.group.name}_${scope.id}`;
 		},
 		template: `
diff --git a/beat/web/reports/static/reports/app/directives/edit/layout.js b/beat/web/reports/static/reports/app/directives/edit/layout.js
index ad31140e5ff34616a979c639c035d1f54b553028..c2b40bfaeb64f523f420075467aa957335e025a9 100644
--- a/beat/web/reports/static/reports/app/directives/edit/layout.js
+++ b/beat/web/reports/static/reports/app/directives/edit/layout.js
@@ -23,23 +23,25 @@
 /*
  * groupsLayout
  * Desc:
- * 	controls the layout of the reports content, generating group panels
- * 	using the GroupsService data.
+ * 	controls the layout of the reports content,
+ * 	generating group panels using the GroupsService data,
+ * 	and holding the menu for adding a group
  */
 angular.module('reportApp').directive("groupsLayout", ['GroupsService', function(GroupsService){
 	return {
-		// isolate scope for modularity & less coupling
 		scope: {
 		},
 		link: function(scope, el, attr){
 			scope.groups = GroupsService.groups;
 			scope.GroupsService = GroupsService;
+
+			// drag handle CSS selector
 			scope.sortableOptions = {
 				handle: '.panel-heading > .panel-title > .action-buttons > .drag-handle'
 			};
 		},
 		template: `
-<div ng-if='GroupsService.isEditable' group-add-group-menu class='panel'></div>
+<div group-add-group-menu class='panel'></div>
 
 <div ui-sortable='sortableOptions' ng-model='GroupsService.groups' id='groupsLayout' class='panel-group'>
 	<div
diff --git a/beat/web/reports/static/reports/app/directives/edit/panelContent.js b/beat/web/reports/static/reports/app/directives/edit/panelContent.js
index f714b2efbcc9b9b518a6a38a1972fbc6dc8cfab3..bfb1fecc1982d933b1429ba8c029c920515428b7 100644
--- a/beat/web/reports/static/reports/app/directives/edit/panelContent.js
+++ b/beat/web/reports/static/reports/app/directives/edit/panelContent.js
@@ -23,7 +23,11 @@
 /*
  * groupPanelContent
  * Desc:
- * 	lays out the content of a group in a panel
+ * 	presents the group to the user in logical blocks:
+ * 	- a panel holds content
+ * 	- panel header contains an (editable) group name label
+ * 	- panel header also contains the buttons to add report items
+ * 	- two sub-panels are added: the experiments list, and the items list
  */
 angular.module('reportApp').directive("groupPanelContent", ['GroupsService', function(GroupsService){
 	return {
@@ -31,10 +35,9 @@ angular.module('reportApp').directive("groupPanelContent", ['GroupsService', fun
 			group: '='
 		},
 		link: function(scope){
-			scope.isEditable = GroupsService.isEditable;
 			scope.deleteGroup = GroupsService.deleteGroup;
 			scope.editingGroupName = false;
-			scope.change = () => {
+			scope.toggleEditingGroupName = () => {
 				scope.editingGroupName = !scope.editingGroupName;
 			};
 		},
@@ -50,7 +53,7 @@ angular.module('reportApp').directive("groupPanelContent", ['GroupsService', fun
 			aria-expanded="true"
 			aria-controls="collapse-{{group.name}}">
 		</a>
-		<span ng-if='!editingGroupName' ng-click='change()'>
+		<span ng-if='!editingGroupName' ng-click='toggleEditingGroupName()'>
 			<span>{{ group.name }}</span>
 			<span style='cursor: pointer;' class='glyphicon glyphicon-pencil'></span>
 		</span>
@@ -62,7 +65,7 @@ angular.module('reportApp').directive("groupPanelContent", ['GroupsService', fun
 					class='form-control'
 					placeholder='Group name...'
 					ng-model='group._name'/>
-				<span class='input-group-addon' ng-click='change()'>
+				<span class='input-group-addon' ng-click='toggleEditingGroupName()'>
 					Save
 				</span>
 			</span>
diff --git a/beat/web/reports/static/reports/app/directives/edit/panelExperiments.js b/beat/web/reports/static/reports/app/directives/edit/panelExperiments.js
index 2a0d9d3544fa9c8f6ad22a50b904a84ca267a50c..8913dcbd29e4e27d68af0d84a0b214acaca7f3d0 100644
--- a/beat/web/reports/static/reports/app/directives/edit/panelExperiments.js
+++ b/beat/web/reports/static/reports/app/directives/edit/panelExperiments.js
@@ -23,7 +23,9 @@
 /*
  * groupPanelExperiments
  * Desc:
- * 	displays the experiments panel of the group
+ * 	displays the experiments panel of the group -
+ * 	a table of experiments in the group, their databases/protocols, and aliases.
+ * 	Also has a menu for adding (compatible) experiments to the group.
  */
 angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService', 'ExperimentsService', 'UrlService', function(GroupsService, ExperimentsService, UrlService){
 	return {
@@ -31,9 +33,11 @@ angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService',
 			group: '='
 		},
 		link: function(scope){
-			scope.isEditable = GroupsService.isEditable;
 			scope.experiments = ExperimentsService.experiments;
 			scope.dropdownId = `${scope.group.name}_exp_add_dropdown`;
+
+			// find experiments that are not in the group but are
+			// compatible with the existing experiments (if any)
 			scope.expsNotInGroup = () => {
 				return ExperimentsService.experimentNames
 				// exps not in group
@@ -42,10 +46,14 @@ angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService',
 				.filter(e => scope.group.analyzer === '' || ExperimentsService.getAnalyzerFromExpName(e) === scope.group.analyzer)
 				;
 			};
+
+			// collects an array of formatted databases & protocols of an experiment
+			// format is "<database name>@<protocol name>"
 			scope.getExpDatabases = (expName) => {
 				let dbs = scope.experiments[expName].declaration.datasets;
 				return Array.from(new Set(Object.values(dbs).map(db => `${db.database}@${db.protocol}`)));
 			};
+
 			scope.getAnalyzerFromExpName = ExperimentsService.getAnalyzerFromExpName;
 			scope.getExpUrl = UrlService.getExperimentUrl;
 			scope.getBlockUrl = UrlService.getBlockUrl;
@@ -66,7 +74,7 @@ angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService',
 		</a>
 		<div class='btn-group'>
 			<div class="dropdown">
-				<button ng-disabled='!isEditable' class="btn btn-default dropdown-toggle" ng-class='{disabled: expsNotInGroup().length === 0}' type="button" id="{{ dropdownId }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+				<button class="btn btn-default dropdown-toggle" ng-class='{disabled: expsNotInGroup().length === 0}' type="button" id="{{ dropdownId }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
 					Add Experiment
 					<span class="caret"></span>
 				</button>
@@ -114,11 +122,11 @@ angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService',
 					<td><a href='{{ getExpUrl(expName) }}'>{{ expName }}</a></td>
 					<td>
 						<span ng-repeat='db in getExpDatabases(expName)'>
-							<a href='{{ getDatabaseUrl(db) }}'>{{ db }}</a>
+							<a href='{{ getDatabaseUrl(db.split("@")[0]) }}'>{{ db }}</a>
 							&nbsp;
 						</span>
 					</td>
-					<td><input ng-disabled='!isEditable' ng-model='group.aliases[expName]'></input></td>
+					<td><input ng-model='group.aliases[expName]'></input></td>
 				</tr>
 			</tbody>
 		</table>
diff --git a/beat/web/reports/static/reports/app/directives/edit/panelItems.js b/beat/web/reports/static/reports/app/directives/edit/panelItems.js
index 0f446ed829bc8431ef67ed490887bbce9c2cd53c..0c73d467701c8372a7cd9881484d6426f211f8bd 100644
--- a/beat/web/reports/static/reports/app/directives/edit/panelItems.js
+++ b/beat/web/reports/static/reports/app/directives/edit/panelItems.js
@@ -23,7 +23,8 @@
 /*
  * groupPanelItems
  * Desc:
- * 	displays the panel of report items of the group
+ * 	displays the panel of report items of the group,
+ * 	using the item container adaptor
  */
 angular.module('reportApp').directive("groupPanelItems", [function(){
 	return {
@@ -31,6 +32,9 @@ angular.module('reportApp').directive("groupPanelItems", [function(){
 			group: '='
 		},
 		link: function(scope){
+			// CSS selector for drag handles for the ui-sortable functionality
+			// TODO: this needs to be changed each time the HTML hierarchy changes.
+			// Make it hierarchy-independent
 			scope.sortableOptions = {
 				handle: '.panel-heading > .panel-title > .action-buttons > .drag-handle'
 			};
diff --git a/beat/web/reports/static/reports/app/directives/edit/plotItem.js b/beat/web/reports/static/reports/app/directives/edit/plotItem.js
index cdc7343f779c9264478e2406d473ce3a1b0fbfae..138d6b90fac8de0e6993aecfba801547915cb6e2 100644
--- a/beat/web/reports/static/reports/app/directives/edit/plotItem.js
+++ b/beat/web/reports/static/reports/app/directives/edit/plotItem.js
@@ -23,10 +23,10 @@
 /*
  * groupPlotItem
  * Desc:
- * 	displays a plot report item
+ * 	displays a plot report item (basically a container for the plots code to insert into)
  */
 angular.module('reportApp')
-.directive("groupPlotItem", ['ExperimentsService', function(ExperimentsService){
+.directive("groupPlotItem", ['ExperimentsService', 'PlotService', '$timeout', function(ExperimentsService, PlotService, $timeout){
 	return {
 		scope: {
 			group: '=',
@@ -34,7 +34,37 @@ angular.module('reportApp')
 			content: '='
 		},
 		link: function(scope){
+			const group = scope.group;
 			scope.domId = `${scope.group.name}_${scope.id}`;
+
+			// container for the plots applet to insert into
+			scope.renderDivId = `${scope.domId}-render`;
+
+			// wait until the container html element is rendered.
+			// angular will run these functions called with $timeout
+			// after everything has been rendered
+			$timeout(function() {
+				PlotService.addPlot(scope.group, scope.id, scope.renderDivId);
+			});
+
+			// if the group has exps added/removed, rerender the plot
+			scope.$watch(
+				// angular doesnt watch arrays properly (i.e. watching scope.group._experimentNames),
+				// as it doesnt register array changes.
+				// it also doesnt watch getters properly (i.e. watching scope.group.experiments),
+				// because it compares shallowly and getters commonly return a new obj each time.
+				// so, watch the getters length instead
+				() => scope.group.experiments.length,
+				(newExps, oldExps) => {
+					let el = document.querySelector(`#${scope.renderDivId}`);
+					// if the container is rendered and it already has had a render,
+					// redo the render
+					if(el && el.childNodes.length > 0){
+						el.innerHTML = '';
+						PlotService.addPlot(scope.group, scope.id, scope.renderDivId);
+					}
+				}
+			);
 		},
 		template: `
 <div id="{{domId}}-heading" class="panel-heading" role="tab">
@@ -68,8 +98,7 @@ angular.module('reportApp')
 	role="tabpanel"
 	aria-labelledby="{{domId}}-heading">
 	<div class='panel-body'>
-		<p>{{ id }} content</p>
-		<strong class='text-danger'>Plot items in reports are not implemented yet.</strong>
+		<div id='{{ renderDivId }}'></div>
 	</div>
 </div>
 `
diff --git a/beat/web/reports/static/reports/app/directives/edit/tableFieldSelector.js b/beat/web/reports/static/reports/app/directives/edit/tableFieldSelector.js
index 3305cc2c59cb9a71b16e88f218847604bb5916d8..f3ac84adc688d70cdb29cbc589c353bde7444a1b 100644
--- a/beat/web/reports/static/reports/app/directives/edit/tableFieldSelector.js
+++ b/beat/web/reports/static/reports/app/directives/edit/tableFieldSelector.js
@@ -31,13 +31,16 @@ angular.module('reportApp')
 		scope: {
 			id: '=',
 			group: '=',
+			// currently selected columns for the table
 			colsSelected: '=',
+			// function to execute when the user clicks the submit button
 			buttonAction: '&',
+			// title for the menu
 			title: '@',
+			// text  for the submit button
 			buttonText: '@'
 		},
 		link: function(scope){
-			scope.isEditable = GroupsService.isEditable;
 			// bootstrap's auto dropdown toggling is disabled for the table creation dropdown
 			// add a click handler for the table creation dropdown submit button to toggle
 			// manually
@@ -47,34 +50,52 @@ angular.module('reportApp')
 			};
 
 			scope.tableables = () => {
+				// start with the tableables generated in experimentsservice
 				const tableables = ExperimentsService.tableables;
 				const fieldArr = Object.entries(tableables)
+				// only look at fields that are from an experiment in the group
 				.filter(([e, fields]) => scope.group.experiments.includes(e))
+				// get the names of the fields
 				.map(([e, fields]) => Object.keys(fields))
+				// make one big array of all field names
 				.reduce((arr, fArr) => [...arr, ...fArr], [])
 				;
 
+				// converting to and from a Set is a simple way of
+				// removing dups
 				return Array.from(new Set(fieldArr));
 			};
 
+			// has this fieldName already been processed?
+			// need to look at the already-processed field names
 			scope.isUniqueTableable = (expName, fieldName) => {
 				const tableables = ExperimentsService.tableables;
 				const concatNames = (eName, fName) => `${eName}.${fName}`;
 
+				// see if this field is a repeat
 				const isRepeat = Object.entries(tableables)
 				.filter(([e, fields]) => {
+					// only look at tableables of exps that are in group
 					let isInGroup = scope.group.experiments.includes(e);
+					// and have already been looked at
 					let alreadyChecked = Object.keys(tableables).indexOf(e) < Object.keys(tableables).indexOf(expName);
 					return isInGroup && alreadyChecked;
 				})
+				// get field names
 				.map(([e, fields]) => Object.keys(fields))
+				// flatten
 				.reduce((arr, fArr) => [...arr, ...fArr], [])
+				// does this flattened array have this field name in it?
 				.includes(fieldName)
 				;
 
+				// if it isnt a repeat, its unique!
 				return !isRepeat;
 			};
 
+			// many tableable fields are fields from an analyzer, a block, or something else
+			// these fields have a field group name, a '.', and an actual field name
+			// find these group names and use them to subdivide the list of fields in the menu
 			scope.tableablesGroups = () => {
 				let groupNames = scope.tableables()
 				.filter(f => f.includes('.'))
@@ -83,8 +104,10 @@ angular.module('reportApp')
 				return Array.from(new Set(groupNames)).sort((a, b) => groupNames.indexOf(a) - groupNames.indexOf(b));
 			};
 
+			// finds the actual field name whether its in a field group or not
 			scope.subfieldName = (field) => field.includes('.') ? field.split('.').slice(1).join('.') : field;
 
+			// toggle the selection of a field
 			scope.toggleField = (fName) => {
 				let idx = scope.colsSelected.indexOf(fName);
 				if(idx > -1){
@@ -96,7 +119,7 @@ angular.module('reportApp')
 			};
 		},
 		template: `
-<button ng-disabled='!isEditable' id='{{ id }}' type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+<button id='{{ id }}' type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
 	{{ title }}
 	<span class="caret"></span>
 </button>
diff --git a/beat/web/reports/static/reports/app/directives/edit/tableItem.js b/beat/web/reports/static/reports/app/directives/edit/tableItem.js
index 69a4fa74ab732681416d5deae734124c7634d3e6..9fba43f28944e7eb73ef6d78ffa26a1077542509 100644
--- a/beat/web/reports/static/reports/app/directives/edit/tableItem.js
+++ b/beat/web/reports/static/reports/app/directives/edit/tableItem.js
@@ -23,7 +23,8 @@
 /*
  * groupTableItem
  * Desc:
- * 	displays a table report item
+ * 	displays a table report item and lets the user
+ * 	manage this table's selected cols and float precision
  */
 angular.module('reportApp')
 .directive("groupTableItem", ['GroupsService', 'ExperimentsService', function(GroupsService, ExperimentsService){
@@ -35,80 +36,27 @@ angular.module('reportApp')
 		},
 		link: function(scope){
 			// aliases
-			scope.isEditable = GroupsService.isEditable;
 			scope.fields = scope.content.fields;
 			// ids
 			scope.domId = `${scope.group.name}_${scope.id}`;
 			scope.colSelectorId = `${scope.domId}_columnSelector`;
 
-			// add 'expName' to the beginning of the fields to show in the table
-			// if it isnt already there
-			if(scope.fields.length === 0 || scope.fields[0] !== 'Experiment'){
-				scope.fields.unshift('Experiment');
-			}
-
 			// 1 - 10
+			// probably the most concise way of generating 1-10 computationally
+			// also vastly more complex than writing [1,2,3,4,5,6,7,9,10]
 			scope.floatingPointRange = [...(new Array(10)).keys()].map(i => i + 1);
 
-			// get possible table entries
-			scope.tableables = ExperimentsService.tableables || {};
-
-			// gets the field type (int, float, string, nothing)
-			scope.getFieldType = (field) => {
-				if(field === scope.fields[0]){
-					return 'string';
-				}
-
-				let hasFieldObj = Object.values(scope.tableables)
-				.find(o => o[field]);
-				let fVal = hasFieldObj ? hasFieldObj[field] : {};
-				let type;
-				if(fVal.type){
-					type = fVal.type;
-				} else if(Number.isSafeInteger(fVal)){
-					type = 'integer';
-				} else if(Number.isFinite(fVal)){
-					type = 'float';
-				} else if(typeof fVal === 'string'){
-					type = 'string';
-				} else {
-					type = undefined;
-				}
-
-				return type;
-			};
-			// gets the field val for the given exp
-			scope.getFieldVal = (expName, field) => {
-				let fVal = scope.tableables[expName] ?
-					scope.tableables[expName][field] : undefined;
-				let val;
-				if(field === scope.fields[0]){
-					val = scope.group.aliases[expName].length > 0 ? scope.group.aliases[expName] : expName;
-				} else if(!fVal){
-					val = '-';
-				} else {
-					let tmp;
-					if(fVal.value){
-						tmp = fVal.value;
-					} else {
-						tmp = fVal;
-					}
-
-					let type = scope.getFieldType(field);
-					if(type && type.startsWith('float')){
-						val = tmp.toFixed(parseInt(scope.content.precision));
-					} else {
-						val = tmp;
-					}
-				}
-
-				return val;
-			};
-
 			// init the chosen cols for the table with the saved cols
 			scope.chosenCols = Array.from(scope.fields);
+
 			// save new cols choice
+			// due to how angular handles functions passed as props to an element,
+			// it must return a function that does the actual work.
 			scope.saveChosenCols = () => () => {
+				// we want to keep the selected columns in order,
+				// so try to add new columns in their correct indices,
+				// and rm cols by mutating the array
+
 				const newCols = scope.chosenCols
 				.filter(c => !scope.fields.includes(c));
 
@@ -120,48 +68,6 @@ angular.module('reportApp')
 				newCols.forEach(nf => scope.fields.push(nf));
 			};
 
-			// need to nest actual value in an obj to get angular
-			// to watch it correctly
-			scope.sortField = { val: 'Experiment', isReversed: false };
-			// sort rows (one row per exp)
-			scope.sortFunc = (expName) => {
-				return scope.getFieldType(scope.sortField.val) ?
-					scope.getFieldVal(expName, scope.sortField.val) : expName;
-			};
-			// sets the new sort field and direction
-			scope.setSortField = (field) => {
-				if(scope.sortField.val === field){
-					scope.sortField.isReversed = !scope.sortField.isReversed;
-				} else {
-					scope.sortField.val = field;
-					scope.sortField.isReversed = false;
-				}
-			};
-
-			// a different view of the table
-			scope.getCSV = () => {
-				let fields = scope.fields;
-				let exps = scope.group.experiments
-				// clone arr
-				.map(e => `${e}`)
-				.sort((ea, eb) => (scope.sortField.isReversed ? -1 : 1) * (scope.sortFunc(ea) > scope.sortFunc(eb) ? -1 : 1))
-				;
-
-				let str = '';
-
-				let fieldsStr = fields
-				.map(f => `${f}(${scope.getFieldType(f)})`)
-				.join(',');
-
-				let expsStrs = exps
-				.map(e => fields.map(f => `${scope.getFieldVal(e, f)}`).join(','))
-				.join('\n');
-
-				str = `${fieldsStr}\n${expsStrs}`;
-
-				return str;
-			};
-
 			// toggle val for viewing CSV
 			scope.isViewingCSV = { val: false };
 			scope.toggleViewingCSV = () => {
@@ -205,7 +111,7 @@ angular.module('reportApp')
 				>
 			</div>
 			<div class='btn-group' role='group'>
-				<button ng-disabled='!isEditable' class='btn btn-default' id="{{domId}}-precision" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+				<button class='btn btn-default' id="{{domId}}-precision" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
 					Float Precision: {{ content.precision }}
 					<span class="caret"></span>
 				</button>
@@ -223,38 +129,7 @@ angular.module('reportApp')
 	class="panel-collapse collapse in"
 	role="tabpanel"
 	aria-labelledby="{{domId}}-heading">
-	<div ng-if='isViewingCSV.val' class='panel-body'>
-		<pre>{{ getCSV() }}</pre>
-	</div>
-	<div ng-if='!isViewingCSV.val' class='panel-body' style='height: 100%; overflow-x: auto;'>
-		<table class="table table-striped table-hover">
-			<thead>
-				<tr ui-sortable ng-model='fields'>
-					<th ng-repeat='field in fields'>
-						<span
-							ng-if="sortField.val == field"
-							class='glyphicon'
-							ng-class="{
-								'glyphicon-chevron-up': sortField.isReversed,
-								'glyphicon-chevron-down': !sortField.isReversed
-								}"
-							>
-						</span>
-						<a role='button' ng-click='setSortField(field)'>
-							{{ field }} <i>({{ getFieldType(field) }})</i>
-						</a>
-					</th>
-				</tr>
-			</thead>
-			<tbody>
-				<tr ng-repeat="exp in group.experiments | orderBy:sortFunc:sortField.isReversed">
-					<td ng-repeat='field in fields'>
-						{{ getFieldVal(exp, field) }}
-					</td>
-				</tr>
-			</tbody>
-		</table>
-	</div>
+	<div table-item group='group' content='content' is-viewing-csv='isViewingCSV'></div>
 </div>
 `
 	};
diff --git a/beat/web/reports/static/reports/app/directives/edit/textItem.js b/beat/web/reports/static/reports/app/directives/edit/textItem.js
index d55618a86927b3700795e23730e64a7921cd12ad..c38e9aafd6b39b5c24d4e99e7ff776f67e739955 100644
--- a/beat/web/reports/static/reports/app/directives/edit/textItem.js
+++ b/beat/web/reports/static/reports/app/directives/edit/textItem.js
@@ -23,7 +23,11 @@
 /*
  * groupTextItem
  * Desc:
- * 	displays a text report item
+ * 	displays a text report item:
+ * 	- sends the raw RST to the server to be compiled,
+ * 		and displays the returned HTML
+ * 	- the user can edit the RST using codemirror
+ * 	- the compile is async and doesnt require refreshing the page
  */
 angular.module('reportApp')
 .directive("groupTextItem", ['GroupsService', '$sce', 'UrlService', 'reportFactory', function(GroupsService, $sce, UrlService, reportFactory){
@@ -34,24 +38,18 @@ angular.module('reportApp')
 		},
 		link: function(scope){
 			// aliases
+			// angular requires that compiling raw html be sanitized
 			scope.trustAsHtml = $sce.trustAsHtml;
-			scope.isEditable = GroupsService.isEditable;
 			scope.item = scope.reportItem;
 			scope.domId = `${scope.group.name}_${scope.item.id}`;
 
 			// codemirror options
 			scope.uicmOptions = {
 				mode: 'rst',
-				readOnly: !GroupsService.isEditable
-			};
-
-			// readonly codemirror options
-			scope.srccmOptions = {
-				mode: 'rst',
-				readOnly: true
 			};
 
 			// handle compiling content
+			// holds the last response from the server
 			scope.compiledContent = { val: '' };
 			scope.compileContent = () => {
 				let url = UrlService.getCompileRstUrl();
@@ -59,18 +57,22 @@ angular.module('reportApp')
 
 				return reportFactory.compileRST(url, content)
 				.then(data => {
+					// when compiled, save the raw html
 					scope.compiledContent.val = data.data.html_str;
 				});
 			};
 
 			// handle edit/save/cancel buttons
 			scope.isEditing = { val: false };
+			// when editing, use a buffer to hold the raw text
 			scope.unsavedContent = { val: `${scope.item.content}` };
+			// save the buffer to the actual report item content
 			scope.saveAction = () => {
 				scope.item.content = scope.unsavedContent.val;
 				scope.compileContent();
 				scope.isEditing.val = false;
 			};
+			// discard buffer and use report item content
 			scope.cancelAction = () => {
 				scope.unsavedContent.val = `${scope.item.content}`;
 				scope.isEditing.val = false;
diff --git a/beat/web/reports/static/reports/app/directives/edit/viewSerialized.js b/beat/web/reports/static/reports/app/directives/edit/viewSerialized.js
deleted file mode 100644
index fa0821546d66f4be2fef1ad3a4e824194ac5c0ff..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/edit/viewSerialized.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-/*
- * groupViewSerialized
- * Desc:
- * 	renders a 'serialized' view of the report item using their html func:
- * 	- text: The HTML compiled on the server from the raw RST in the input
- * 	- table: A CSV-style, raw & easily copyable text block
- */
-angular.module('reportApp')
-.directive("groupViewSerialized", ['$sce', function($sce){
-	return {
-		scope: {
-			entity: '=',
-			serializeFuncObj: '='
-		},
-		link: function(scope, el){
-			scope.trustAsHtml = $sce.trustAsHtml;
-		},
-		template: `
-<div class='well'>
-	<div ng-bind-html='trustAsHtml(serializeFuncObj.val())'></div>
-</div>
-`
-	};
-}]);
diff --git a/beat/web/reports/static/reports/app/directives/item.js b/beat/web/reports/static/reports/app/directives/item.js
deleted file mode 100644
index 5e383a4747a3baa19f02e7f2a1fadf18858e32b4..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/item.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Dynamic tables items
-angular.module('reportApp').directive('item', function ($compile) {
-	function createTDElement(column) {
-		let table = angular.element('<table><tr><td class="tableitemspace" thecolumn="' + column + '"></td></tr></table>');
-		return table.find('td');
-	}
-
-	function render(element, scope) {
-		let column, html, i;
-		let columns = scope.$parent.tables_details[scope.$parent.dattrs.tableid];
-
-		for (i = 0; i < columns.length ; i++) {
-			if(i == 0 && columns.length > 0){
-				html = $compile(createTDElement("experiment"))(scope);
-
-			}
-			column = columns[i];
-			if (column.selected) {
-
-				html = $compile(createTDElement(column.name))(scope);
-				element.append(html);
-			}
-		}
-
-
-	}
-
-	return {
-		//   restrict: 'A',
-		scope: {
-			exp_name: "=",
-			item: "=",
-			columns: "="
-		},
-		compile: function () {
-			return function (scope, element) {
-				render(element, scope);
-			};
-
-		}
-	};
-
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/loadedContent.js b/beat/web/reports/static/reports/app/directives/loadedContent.js
deleted file mode 100644
index fa47b3d859d6b8fe949f58339f0ce265375d61f6..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/loadedContent.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
-*/
-
-//Directive that returns an element which adds buttons on click which adds items
-angular.module('reportApp').directive('loadedcontent', function(){
-    //return {
-    //    link: function (scope, elem, attrs, ctrl) {
-    //        angular.element(document).ready(function() {
-    //            console.info(scope.report.content);
-    //
-    //            });
-    //        //$(window).load(function() {
-    //        //
-    //        //    console.info(scope.report.content);
-    //        //
-    //        //    });
-    //    }
-    //}
-});
-
-
diff --git a/beat/web/reports/static/reports/app/directives/lockReport.js b/beat/web/reports/static/reports/app/directives/lockReport.js
index 31e0f9e67e903e86dda30c155c868f6c5fd1d652..9d02f4eebda3681be7489ab6b94357ee9901d2dc 100644
--- a/beat/web/reports/static/reports/app/directives/lockReport.js
+++ b/beat/web/reports/static/reports/app/directives/lockReport.js
@@ -25,13 +25,8 @@ angular.module('reportApp').directive("lockreport", function($compile){
 	return function(scope, element, attrs){
 		//add new report item
 		element.bind("click", function(){
-			lockReport();
-		});
-
-
-		function lockReport(){
 			beat.ui.report.lock_report('report_lock', scope);
-		}
+		});
 	};
 });
 
diff --git a/beat/web/reports/static/reports/app/directives/myReportInfo.js b/beat/web/reports/static/reports/app/directives/myReportInfo.js
deleted file mode 100644
index f8f0c4cb0b63d4a821c75b32aaa83b53749840a8..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/myReportInfo.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to generate the dynamic table loading partials
-angular.module('reportApp').directive("myreportinfo", function(){
-	return {
-		restrict: 'E',
-		scope: true,
-		replace: true,
-		 Â Ã‚ Ã‚ //templateUrl: "/reports/partials/reportTable/",
-		 Â Ã‚ Ã‚ templateUrl: function(scope, elem, attrs){
-			let prefix = elem['urlprefix'];
-			 Â Ã‚ Ã‚     let the_url = prefix + "/reports/partials/reportInfo/";
-			return the_url;
-		},
-		link: function(scope, elem, attrs){
-		},
-		controller: ['$scope', function ($scope) {
-		}],
-	};
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/publishReport.js b/beat/web/reports/static/reports/app/directives/publishReport.js
index 4d03afae59d25b11f72ccdc2d70ba3d32c333d73..2595699e5dd5d1c0e88624a39f73e3c265d89d2e 100644
--- a/beat/web/reports/static/reports/app/directives/publishReport.js
+++ b/beat/web/reports/static/reports/app/directives/publishReport.js
@@ -25,11 +25,6 @@ angular.module('reportApp').directive("publishreport", function($compile){
 	return function(scope, element, attrs){
 		//add new report item
 		element.bind("click", function(){
-			publishReport();
-		});
-
-
-		function publishReport(){
 			scope.reportFactory.publishReportAlgorithms(scope.user, scope.report_id, scope.url_prefix)
 			.success(function (reportData){
 				beat.ui.report.publish_report('report_publish', scope, reportData);
@@ -44,9 +39,7 @@ angular.module('reportApp').directive("publishreport", function($compile){
 				$("#report_publish .errors .errorslist").append(scope.status);
 				$("#button-report_publish-cancel").hide();
 			});
-
-
-		}
+		});
 	};
 });
 
diff --git a/beat/web/reports/static/reports/app/directives/removeExperiment.js b/beat/web/reports/static/reports/app/directives/removeExperiment.js
deleted file mode 100644
index b6a55a0c84d36e72bc96d014a181cd28ac6d8941..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/removeExperiment.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive for opening smart_selector list on "Add a report item" button click that displays options
-angular.module('reportApp').directive("removeexperiment", function($compile){
-	return function(scope, element, attrs){
-		//add new report item
-		element.bind("click", function(){
-			removeExperiment(attrs.id);
-		});
-
-
-		function removeExperiment(experiment_name){
-			beat.ui.report.remove_experiment('report_remove_experiment', experiment_name, scope);
-		}
-	};
-});
-
-
diff --git a/beat/web/reports/static/reports/app/directives/reportItemView.js b/beat/web/reports/static/reports/app/directives/reportItemView.js
deleted file mode 100644
index 68f67287153717e141d39bc1c87cd9e2c158df41..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/reportItemView.js
+++ /dev/null
@@ -1,2185 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- * 
- * This file is part of the beat.web module of the BEAT platform.
- * 
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- * 
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- * 
- * 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/.
-*/
-//Directive that returns an element which adds buttons on click which adds items
-var app = angular.module('reportApp');
-
-app.directive('loadedcontent', function()
-{
-    //return {
-    //    link: function (scope, elem, attrs, ctrl) {
-    //        angular.element(document).ready(function() {
-    //            console.info(scope.report.content);
-    //
-    //            });
-    //        //$(window).load(function() {
-    //        //
-    //        //    console.info(scope.report.content);
-    //        //
-    //        //    });
-    //    }
-    //}
-});
-
-//Directive for opening smart_selector list on "Add a report item" button click that displays options
-app.directive("removeexperiment", function($compile)
-{
-	return function(scope, element, attrs)
-    {
-        //add new report item
-		element.bind("click", function()
-        {
-            removeExperiment(attrs.id);
-		});
-
-
-        function removeExperiment(experiment_name)
-        {
-             beat.ui.report.remove_experiment('report_remove_experiment', experiment_name, scope);
-        }
-    }
-});
-
-//Directive for opening smart_selector list on "Add a report item" button click that displays options
-app.directive("savereportitems", function($compile)
-{
-	return function(scope, element, attrs)
-    {
-        //add new report item
-		element.bind("click", function()
-        {
-            //var savecontent = [];
-            var savecontent = {};
-            if(scope.plots_details != undefined)
-            {
-                angular.forEach(scope.plots_details, function(value, key)
-                {
-                    //savecontent.push(key, value);
-                    savecontent[key] = value;
-                });
-            }
-            if(scope.tables_details != undefined)
-            {
-                angular.forEach(scope.tables_details, function(value, key)
-                {
-                    savecontent[key] = value;
-                    //savecontent.push(key, value);
-                });
-
-                if(scope.floating_point_precision.selected != undefined)
-                {
-                    savecontent["floating_point_precision"] = scope.floating_point_precision.selected;
-                }
-            }
-            if(scope.report_experiments_alias != undefined)
-            {
-                var alias_experiments = {};
-                angular.forEach(scope.report_experiments_alias, function(value, key)
-                {
-                    alias_experiments[key] = value;
-                });
-                savecontent["alias_experiments"] = alias_experiments;
-            }
-
-            if(!(jQuery.isEmptyObject(scope.sorted_experiments_keys_tables)) && !(jQuery.isEmptyObject(scope.sorted_experiments_keys_reverse)) && !(jQuery.isEmptyObject(scope.sorted_experiments_keys_tables_sortkey)))
-            {
-                savecontent["sorted_tables_experiments"] = scope.sorted_experiments_keys_tables;
-                savecontent["sorted_tables_alias_experiments"] = scope.sorted_experiments_alias_keys_tables;
-                savecontent["sorted_tables_keys_reverse"] = scope.sorted_experiments_keys_reverse;
-                savecontent["sorted_tables_sortkey"] = scope.sorted_experiments_keys_tables_sortkey;
-            }
-            //call set report content from factory
-            var mydict = {};
-            mydict["experiments"] = scope.report.experiments;
-            mydict["content"] = savecontent;
-
-            for(var i = 0; i < scope.report.experiments.length; i++)
-            {
-                scope.report_experiments_alias_from_content[scope.report.experiments[i]] = scope.report_experiments_alias[scope.report.experiments[i]];
-            }
-
-            updateReport(mydict);
-		});
-
-
-        function updateReport(data)
-        {
-            scope.reportFactory.updateReport(scope.user, scope.report_id, data, scope.url_prefix)
-                .success(function (reportData)
-                {
-                    //alert("The report "+ scope.report_id +" has been saved.");
-
-                    beat.ui.report.report_saved('report_saved', scope);
-                })
-                .error(function (error)
-                {
-                    scope.status = 'Unable to update report data: ' + error.message;
-
-                });
-
-        }
-    }
-});
-
-//Directive for opening smart_selector list on "Add a report item" button click that displays options
-app.directive("lockreport", function($compile)
-{
-	return function(scope, element, attrs)
-    {
-        //add new report item
-		element.bind("click", function()
-        {
-            lockReport();
-		});
-
-
-        function lockReport()
-        {
-             beat.ui.report.lock_report('report_lock', scope);
-        }
-    }
-});
-
-//Directive for opening smart_selector list on "Add a report item" button click that displays options
-app.directive("publishreport", function($compile)
-{
-	return function(scope, element, attrs)
-    {
-        //add new report item
-		element.bind("click", function()
-        {
-            publishReport();
-		});
-
-
-        function publishReport()
-        {
-            scope.reportFactory.publishReportAlgorithms(scope.user, scope.report_id, scope.url_prefix)
-               .success(function (reportData)
-               {
-                    beat.ui.report.publish_report('report_publish', scope, reportData);
-               })
-               .error(function (error)
-               {
-                    scope.status = 'Unable to publish report data: ' + error;
-                    $(".explanation_text").hide();
-                    $("#report_publish .warnings").hide();
-                    $("#report_publish .errors").show();
-                    if(error.detail != undefined)
-                        scope.status = 'Unable to publish report data: ' + error.detail;
-                    $("#report_publish .errors .errorslist").append(scope.status);
-                    $("#button-report_publish-cancel").hide();
-               });
-
-
-        }
-    }
-});
-
-//Directive for opening smart_selector list on "Add a report item" button click that displays options
-app.directive("addreportitem", function($compile)
-{
-	return function(scope, element, attrs)
-    {
-        scope.$on("addSavedElement", function(event, id_content, type)
-        {
-            var num_tables_in_content = 0;
-            var num_figures_in_content = 0;
-            for(var i = 0; i < Object.keys(scope.report.content).length; i++)
-            {
-                if(Object.keys(scope.report.content)[i].indexOf("table_") == 0)
-                {
-                    num_tables_in_content++;
-                }
-                else if(Object.keys(scope.report.content)[i].indexOf("chart_") == 0)
-                {
-                    num_figures_in_content++;
-                }
-            }
-
-            if(type == "table")
-            {
-                var table_id = id_content;
-                var table_details = undefined;
-
-                //take the one with more elements
-                if(Object.keys(scope.tables_details).length > num_tables_in_content)
-                {
-                    table_details = scope.tables_details[id_content];
-                }
-                else
-                {
-                    table_details = scope.report.content[id_content];
-                }
-
-                var name_to_remove = "experiment";
-                for(var i = 0; i < table_details.length; i++)
-                {
-                    if(table_details[i].name == "experiment")
-                    {
-                        table_details.splice(i,1);
-                        break;
-                    }
-                }
-
-                scope.tables_details[table_id] = table_details;
-
-                var accessnumber = "no_number";
-                if(scope.report.number == scope.report_number)
-                {
-                    accessnumber = "number";
-                }
-                var html_div_code = '<div class="panel panel-default" id="' + table_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + table_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-table"> Table</i></a></h4></div><div id="collapse-' + table_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div class="panel-body" table-dynamic monster=' + scope.num_report_items + " tableid=" + table_id + " reportstatus=" + scope.report.status + " accessnumber=" + accessnumber + " urlprefix=" + scope.url_prefix + '></div></div></div>';
-
-                angular.element(document.getElementById('space-for-report-items')).append($compile(html_div_code)(scope));
-                if(parseInt(table_id.split("_").pop(-1)) >= scope.max_num)
-                {
-                    scope.max_num = parseInt(table_id.split("_").pop(-1));
-                    scope.num_report_items = scope.max_num;
-                }
-
-                scope.num_report_items++;
-
-
-            }
-            else if(type == "chart")
-            {
-
-                var content_detail = {};
-                var chart_id = id_content;
-
-                var plot_details = undefined;
-
-                //take the one with more elements
-                if(Object.keys(scope.plots_details).length > num_figures_in_content)
-                {
-                    plot_details = scope.plots_details[chart_id];
-                }
-                else
-                {
-                    plot_details = scope.report.content[chart_id];
-                }
-
-
-
-                content_detail["name"] = plot_details.data.output[0];
-                //content_detail["description"] = scope.report.content[chart_id].data.plotter;
-                if(plot_details.data.plotter != undefined)
-                {
-                    content_detail["selected_plotter"] = plot_details.data.plotter;
-                }
-                if(plot_details.selected_template != undefined)
-                {
-                    content_detail["selected_template"] = plot_details.selected_template;
-                }
-                if(plot_details.data.merged != undefined)
-                {
-                    content_detail["merged"] = plot_details.data.merged;
-                }
-                if(plot_details.merged != undefined)
-                {
-                    content_detail["merged"] = plot_details.merged;
-                }
-
-                var html_div_code = '<div class="panel panel-default" id="' + chart_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + chart_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-area-chart"> ' + content_detail.name + '</i></a></h4></div><div id="collapse-' + chart_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div id="'+chart_id+'" class="panel-body chart {$ report.status $}"></div></div></div>';
-                generate_element(scope, "plot", html_div_code);
-                var element = document.getElementById(chart_id);
-                var label_element  = $(document.createElement('div'));
-                label_element.addClass('row');
-
-                var accessnumber = "no_number";
-                if(scope.report.number == scope.report_number)
-                {
-                    accessnumber = "number";
-                }
-
-                var prepend_buttons = '<div class="col-sm-12"><div class="action-buttons report-buttons pull-left">';
-                var append_buttons = '</div></div>';
-
-                var a_export  = '<div class="btn-group"><button type="button" class="btn btn-primary btn-sm dropdown-toggle item_export ' + scope.report.status + ' ' + accessnumber + '" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-download fa-lg"></i> Export Figure <span class="caret"></span></button><ul class="dropdown-menu"><li id="PNG" buttonexportitem><a>PNG</a></li><li id="JPEG" buttonexportitem><a>JPEG</a></li><li id="PDF" buttonexportitem><a>PDF</a></li></ul></div>';
-                if(scope.report.status == "editable" && scope.report.number != scope.report_number) {
-                  var a_element = '<button class="btn btn-danger btn-sm item_delete ' + scope.report.status + ' ' + accessnumber + '" buttondeleteitem><i class="fa fa-times fa-lg"></i> Delete</button>';
-                  label_element.html(prepend_buttons + a_element + a_export + append_buttons);
-                }
-                else {
-                  label_element.html(prepend_buttons + a_export + append_buttons);
-                }
-;
-                $(element).find('.panel-body').append(label_element);
-                angular.element(document.getElementById('space-for-report-items')).append($compile(element)(scope));
-                //var html_dropdown  = "<chartdropdown id='selector_" + chart_id +"'></chartdropdown>";
-                //angular.element(document.getElementById(chart_id)).append($compile(html_dropdown)(scope));
-
-                _retrieve_chart(scope, content_detail, element);
-
-                if(parseInt(chart_id.split("_").pop(-1)) >= scope.max_num)
-                {
-                    scope.max_num = parseInt(chart_id.split("_").pop(-1));
-                    scope.num_report_items = scope.max_num;
-                }
-
-                scope.num_report_items++;
-
-            }
-
-        }
-        );
-
-        scope.$on("redrawGraphElement", function(event, id_content, type)
-        {
-            var content_detail = {};
-            var chart_id = id_content;
-            content_detail["name"] = scope.report.content[chart_id].data.output[0];
-            //content_detail["description"] = scope.report.content[chart_id].data.plotter;
-            if(scope.report.content[chart_id].data.plotter != undefined)
-            {
-                content_detail["selected_plotter"] = scope.report.content[chart_id].data.plotter;
-            }
-            if(scope.report.content[chart_id].data.parameter != undefined)
-            {
-                content_detail["selected_template"] = scope.report.content[chart_id].data.parameter;
-            }
-            if(scope.report.content[chart_id].data.merged != undefined)
-            {
-                content_detail["merged"] = scope.report.content[chart_id].data.merged;
-            }
-
-            var html_div_code = '<div class="panel panel-default" id="' + chart_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + chart_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-area-chart"> ' + content_detail.name + '</i></a></h4></div><div id="collapse-' + chart_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div id="'+chart_id+'" class="panel-body chart"></div></div></div>';
-            generate_element(scope, "plot", html_div_code);
-            var element = document.getElementById(chart_id);
-            var label_element  = $(document.createElement('div'));
-            label_element.addClass('row');
-
-            var accessnumber = "no_number";
-            if(scope.report.number == scope.report_number)
-            {
-                accessnumber = "number";
-            }
-
-            var prepend_buttons = '<div class="col-sm-12"><div class="action-buttons report-buttons pull-left">';
-            var append_buttons = '</div></div>';
-
-            var a_export  = '<div class="btn-group"><button type="button" class="btn btn-primary btn-sm dropdown-toggle item_export ' + scope.report.status + ' ' + accessnumber + '" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-download fa-lg"></i> Export Figure <span class="caret"></span></button><ul class="dropdown-menu"><li id="PNG" buttonexportitem><a>PNG</a></li><li id="JPEG" buttonexportitem><a>JPEG</a></li><li id="PDF" buttonexportitem><a>PDF</a></li></ul></div>';
-            if(scope.report.status == "editable" && scope.report.number != scope.report_number) {
-              var a_element = '<button class="btn btn-danger btn-sm item_delete ' + scope.report.status + ' ' + accessnumber + '" buttondeleteitem><i class="fa fa-times fa-lg"></i> Delete</button>';
-              label_element.html(prepend_buttons + a_element + a_export + append_buttons);
-            }
-            else {
-              label_element.html(prepend_buttons + a_export + append_buttons);
-            }
-;
-            $(element).find('.panel-body').append(label_element);
-            angular.element(document.getElementById('space-for-report-items')).append($compile(element)(scope));
-            //var html_dropdown  = "<chartdropdown id='selector_" + chart_id +"'></chartdropdown>";
-            //angular.element(document.getElementById(chart_id)).html($compile(html_dropdown)(scope));
-
-            _retrieve_chart(scope, content_detail, element);
-
-        });
-
-        //add new report item
-		element.bind("click", function()
-        {
-            var left = $('.add_item').offset().left - $('.add_item').width() + 400;
-            var top = $('.add_item').offset().top;
-            smart_selector.display(scope.item_content.content, left, top);
-
-            var allblocks = [];
-            for(var i = 0; i < scope.report_experiments_blocks_merged_blocks.length; i++)
-            {
-                allblocks.push(scope.report_experiments_blocks_merged_blocks[i]);
-            }
-            scope.report.common_blocks = arraysInCommon(allblocks);
-		});
-
-        //handles first selector selection (table/plot)
-        if(smart_selector != undefined)
-        {
-            smart_selector.onEntrySelected = function(item_selected)
-            {
-
-                var left = $('.add_item').offset().left - $('.add_item').width() + 400;
-                var top = $('.add_item').offset().top;
-
-                var next_items_for_selector = prepareContent(scope, smart_selector.entries[smart_selector.selected_entry].identifier);
-                smart_selector_detail.display(next_items_for_selector, left, top);
-            };
-        }
-
-        //handles next selector detail selection
-        if(smart_selector_detail != undefined)
-        {
-            smart_selector_detail.onEntrySelected = function(item_selected)
-            {
-                var left = $('.add_item').offset().left - $('.add_item').width() + 400;
-                var top = $('.add_item').offset().top;
-
-
-                var result_next_items = nextDetailContent(scope, smart_selector.entries[smart_selector.selected_entry].identifier, smart_selector_detail.entries[smart_selector_detail.selected_entry]);
-
-                if(!result_next_items[0])
-                {
-                    multiple_selector.display(result_next_items[1], left, top);
-                }
-            };
-        }
-
-        //handles first creation of table based on options
-        if(multiple_selector != undefined)
-        {
-            multiple_selector.onEntrySelected = function(item_selected, data)
-            {
-                for(var i = 0; i < multiple_selector.entries.length; i++)
-                {
-                    if(multiple_selector.entries[i].name == item_selected)
-                    {
-                        multiple_selector.entries[i].selected = true;
-                    }
-                }
-            };
-
-            multiple_selector.onEntryDeselected = function(item_selected, data)
-            {
-                for(var i = 0; i < multiple_selector.entries.length; i++)
-                {
-                    if(multiple_selector.entries[i].name == item_selected)
-                    {
-                        multiple_selector.entries[i].selected = false;
-                    }
-                }
-            };
-
-            multiple_selector.onClose = function()
-            {
-                //check if at least one item is selected
-                var checkOneSelected = false;
-                angular.forEach(multiple_selector.entries, function(value, key)
-                {
-                    if(value.selected)
-                        checkOneSelected = true
-                });
-
-                if(checkOneSelected)
-                {
-                    var table_id = 'table' + '_' + scope.num_report_items;
-
-                    scope.tables_details[table_id] = multiple_selector.entries;
-
-                    var html_div_code = '<div class="panel panel-default" id="' + table_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + table_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-table"> Table</i></a></h4></div><div id="collapse-' + table_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div class="panel-body" table-dynamic monster=' + scope.num_report_items + " tableid=" + table_id + " reportstatus=" + scope.report.status + " urlprefix=" + scope.url_prefix + '></div></div></div>';
-
-                    angular.element(document.getElementById('space-for-report-items')).append($compile(html_div_code)(scope));
-
-                    scope.report.orderedcontent.push(table_id);
-                    scope.num_report_items++;
-                }
-            }
-        }
-
-        //handles single table updates via settings option
-        if(multiple_selector_updater != undefined)
-        {
-            multiple_selector_updater.onEntrySelected = function(item_selected, data)
-            {
-                for(var i = 0; i < multiple_selector_updater.entries.length; i++)
-                {
-                    if(multiple_selector_updater.entries[i].name == item_selected)
-                    {
-                        multiple_selector_updater.entries[i].selected = true;
-                    }
-                }
-            };
-
-            multiple_selector_updater.onEntryDeselected = function(item_selected, data)
-            {
-                for(var i = 0; i < multiple_selector_updater.entries.length; i++)
-                {
-                    if(multiple_selector_updater.entries[i].name == item_selected)
-                    {
-                        multiple_selector_updater.entries[i].selected = false;
-                    }
-                }
-            };
-
-            multiple_selector_updater.onClose = function()
-            {
-
-                //check if at least one item is selected
-                var checkOneSelected = false;
-                angular.forEach(multiple_selector_updater.entries, function(value, key)
-                {
-                    if(value.selected)
-                        checkOneSelected = true
-                });
-
-                if(checkOneSelected)
-                {
-                    var element = document.getElementById(multiple_selector_updater.current_table);
-
-                    $(element).attr('id', null);
-                    $compile(element)(scope);
-                    $(element).attr('id', multiple_selector_updater.current_table);
-                }
-            }
-
-	};
-
-    //Prepare the content for selector initialization
-    function prepareContent(scope, content_identifier)
-    {
-        var next_content_items = [];
-
-        switch(content_identifier)
-        {
-            case 'table':
-                next_content_items =  scope.table_item_content.content;
-                break;
-            case 'plot':
-                angular.forEach(scope.report_experiments, function(value, key)
-                {
-                    angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer)
-                    {
-                        if(value_analyzer.algorithm == scope.report.analyzer)
-                        {
-                            scope.report_experiments[key].analyzer_block = key_analyzer;
-                        }
-                    });
-
-                    var plottable_result = [];
-                    angular.forEach(value.results[scope.report_experiments[key].analyzer_block], function(value_result_item, key_result_item)
-                    {
-                        if(value_result_item.type.startsWith("plot/"))
-                        {
-                            plottable_result.push(key_result_item);
-                        }
-
-                    });
-
-                    scope.report_experiments[key].plottable_blocks = plottable_result;
-
-                });
-
-                //Results block are the same for all experiments from same analyzer.
-                //So just grab information from one of them for smart_selector items
-                var single_experiment = scope.report.experiments[0];
-
-                //create smart_selector items
-                angular.forEach(scope.report_experiments[single_experiment].plottable_blocks, function(value_plottable, key_plottable)
-                {
-                    var plot_type = scope.report_experiments[single_experiment].results[scope.report_experiments[single_experiment].analyzer_block][value_plottable].type;
-                    var item_dict = {};
-                    item_dict["identifier"] = value_plottable;
-                    item_dict["name"] = value_plottable;
-                    item_dict["description"] = plot_type;
-
-                    next_content_items.push(item_dict);
-                });
-                break;
-        }
-
-        return next_content_items;
-    }
-
-    //Get the detailed content for next selector
-    function nextDetailContent(scope, content_identifier, sub_content)
-    {
-        var is_end = false;
-        var next_items = [];
-        var return_contents = [];
-        switch(content_identifier)
-        {
-            case 'table':
-                is_end = false;
-                angular.forEach(scope.report_experiments, function(value, key)
-                {
-                    angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer)
-                    {
-                        if(value_analyzer.algorithm == scope.report.analyzer)
-                        {
-                            scope.report_experiments[key].analyzer_block = key_analyzer;
-                        }
-                    });
-
-                    var table_result = [];
-                    angular.forEach(value.results[scope.report_experiments[key].analyzer_block], function(value_result_item, key_result_item)
-                    {
-                        if(!(value_result_item.type.startsWith("plot/")))
-                        {
-                            table_result.push(key_result_item);
-                        }
-
-                    });
-
-                    scope.report_experiments[key].table_result_blocks = table_result;
-
-                });
-
-                //Results block are the same for all experiments from same analyzer.
-                //So just grab information from one of them for smart_selector items
-                var single_experiment = scope.report.experiments[0];
-
-                //create smart_selector items
-                angular.forEach(scope.report_experiments[single_experiment].table_result_blocks, function(value_table_result, key_table_result)
-                {
-
-                    var table_item_data = scope.report_experiments[single_experiment].results[scope.report_experiments[single_experiment].analyzer_block][value_table_result];
-                    var item_dict = {};
-                    item_dict["identifier"] = value_table_result;
-                    item_dict["name"] = value_table_result;
-                    item_dict["description"] = table_item_data.type;
-                    if(table_item_data.primary)
-                        item_dict["selected"] = true;
-                    else
-                        item_dict["selected"] = false;
-                    item_dict["data"] = table_item_data;
-
-                    next_items.push(item_dict);
-                });
-
-                //adding execution time entry information for common blocks
-                angular.forEach(scope.report.common_blocks, function(value_table_result, key_table_result)
-                {
-                    var item_dict = {};
-                    item_dict["identifier"] = "execution_time." + value_table_result;
-                    item_dict["name"] = "execution_time." + value_table_result;
-                    item_dict["selected"] = false;
-                    next_items.push(item_dict);
-
-                });
-
-                //adding total execution time entry information for experiment
-                var item_dict = {};
-                item_dict["identifier"] = "experiment.execution_time";
-                item_dict["name"] = "experiment.execution_time";
-                item_dict["selected"] = false;
-                next_items.push(item_dict);
-
-                //adding globals algorithms parameters
-                angular.forEach(scope.report_algorithm_parameters, function(value_algorithm_parameters, key_algorithm_parameters)
-                {
-                    for(var i = 0; i < Object.keys(value_algorithm_parameters).length; i++)
-                    {
-                        var item_dict = {};
-                        item_dict["identifier"] = "algorithm_parameter." + key_algorithm_parameters + "__" +Object.keys(value_algorithm_parameters)[i];
-                        item_dict["name"] = "algorithm_parameter." + key_algorithm_parameters + "__" +Object.keys(value_algorithm_parameters)[i];
-                        item_dict["selected"] = false;
-                        next_items.push(item_dict);
-                    }
-
-                });
-
-                break;
-            case 'plot':
-                is_end = true;
-                var chart_id = 'chart_' + sub_content.identifier + '_graph' + '_' + scope.num_report_items;
-                var html_div_code = '<div class="panel panel-default" id="' + chart_id + '"><div class="panel-heading" role="tab"><h4 class="panel-title"><a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-' + chart_id + '" aria-expanded="true" aria-controls="collapse-info"><i class="fa fa-area-chart"> ' + sub_content.name + '</i></a></h4></div><div id="collapse-' + chart_id + '" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading"><div id="'+chart_id+'" class="panel-body chart {$ report.status $}"></div></div></div>';
-                generate_element(scope, content_identifier, html_div_code);
-                var element = document.getElementById(chart_id);
-                var label_element  = $(document.createElement('div'));
-                label_element.addClass('row');
-
-                var accessnumber = "no_number";
-                if(scope.report.number == scope.report_number)
-                {
-                    accessnumber = "number";
-                }
-
-                var prepend_buttons = '<div class="col-sm-12"><div class="action-buttons report-buttons pull-left">';
-                var append_buttons = '</div></div>';
-
-                var a_export  = '<div class="btn-group"><button type="button" class="btn btn-primary btn-sm dropdown-toggle item_export ' + scope.report.status + ' ' + accessnumber + '" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-download fa-lg"></i> Export Figure <span class="caret"></span></button><ul class="dropdown-menu"><li id="PNG" buttonexportitem><a>PNG</a></li><li id="JPEG" buttonexportitem><a>JPEG</a></li><li id="PDF" buttonexportitem><a>PDF</a></li></ul></div>';
-                if(scope.report.status == "editable" && scope.report.number != scope.report_number) {
-                  var a_element = '<button class="btn btn-danger btn-sm item_delete ' + scope.report.status + ' ' + accessnumber + '" buttondeleteitem><i class="fa fa-times fa-lg"></i> Delete</button>';
-                  label_element.html(prepend_buttons + a_element + a_export + append_buttons);
-                }
-                else {
-                  label_element.html(prepend_buttons + a_export + append_buttons);
-                }
-                $(element).find('.panel-body').append(label_element);
-
-                angular.element(document.getElementById('space-for-report-items')).append($compile(element)(scope));
-
-                _retrieve_chart(scope, sub_content, element);
-                scope.report.orderedcontent.push(chart_id);
-                break;
-        }
-
-        return_contents = [is_end, next_items];
-
-        return return_contents;
-
-    }
-
-    //Generate and compile DOM element
-    function generate_element(scope, content_identifier, html_div_code)
-    {
-        switch(content_identifier)
-        {
-            case 'table':
-                angular.element(document.getElementById('space-for-report-items')).append($compile(html_div_code)(scope));
-                scope.num_report_items++;
-                break;
-            case 'plot':
-                angular.element(document.getElementById('space-for-report-items')).append($compile(html_div_code)(scope));
-                scope.num_report_items++;
-                break;
-        }
-
-    }
-
-    //Retrieve chart from api and display on proper item
-    function _get_chart(scope, sub_content, content_type, chart_id)
-    {
-        var required_plotter = [];
-        var set_plotter = ''
-        if(sub_content.selected_plotter != undefined)
-        {
-            //required_plotter.push(sub_content.selected_plotter);
-            set_plotter = sub_content.selected_plotter;
-            var requested_dataformat = '';
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].name == sub_content.selected_plotter)
-                {
-                    requested_dataformat = scope.report.plotters[i].dataformat;
-                    break;
-                }
-            }
-
-            //Get default plotter for required dataformat
-            for (var i = 0; i < scope.report.defaultplotters.length; i++)
-            {
-                if(scope.report.defaultplotters[i].dataformat == requested_dataformat)
-                {
-                    sub_content.defaultplotter = scope.report.defaultplotters[i];
-                    break;
-                }
-            }
-
-            required_plotter.push(sub_content.defaultplotter.plotter);
-
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].dataformat == requested_dataformat && scope.report.plotters[i].name != sub_content.defaultplotter.plotter)
-                {
-                    required_plotter.push(scope.report.plotters[i].name);
-                }
-            }
-        }
-        else
-        {
-            //Get default plotter for required dataformat
-            for (var i = 0; i < scope.report.defaultplotters.length; i++)
-            {
-                if(scope.report.defaultplotters[i].dataformat == sub_content.description)
-                {
-                    sub_content.defaultplotter = scope.report.defaultplotters[i];
-                    break;
-                }
-            }
-
-            required_plotter.push(sub_content.defaultplotter.plotter);
-            set_plotter = sub_content.defaultplotter.plotter;
-
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].dataformat == sub_content.description && scope.report.plotters[i].name != sub_content.defaultplotter.plotter)
-                {
-                    required_plotter.push(scope.report.plotters[i].name);
-                }
-            }
-        }
-
-        var plotterparameter = [];
-        //get plotterparameters valid for requested plotter
-        var required_plotter_id = undefined;
-        for(var i = 0; i < scope.report.plotters.length; i++)
-        {
-            if(required_plotter[0] == scope.report.plotters[i].name)
-            {
-                required_plotter_id = scope.report.plotters[i].id
-            }
-        }
-
-        //Get other plotterparameter
-        for (var i = 0; i < scope.report.plotterparameter.length; i++)
-        {
-            if(required_plotter_id == undefined)
-            {
-                plotterparameter.push(scope.report.plotterparameter[i].name);
-            }
-            else
-            {
-                if(scope.report.plotterparameter[i].plotter == required_plotter_id)
-                {
-                    plotterparameter.push(scope.report.plotterparameter[i].name);
-                }
-            }
-        }
-
-        var chart_name = sub_content.name;
-
-        var legend_experiments = '';
-        var alias_experiments = [];
-        for(var i = 0; i < scope.report.experiments.length; i++)
-        {
-            if(i == 0)
-                legend_experiments = scope.report_experiments_alias[scope.report.experiments[i]];
-            else
-                legend_experiments = legend_experiments+ "&" +scope.report_experiments_alias[scope.report.experiments[i]];
-
-            alias_experiments.push(scope.report_experiments_alias[scope.report.experiments[i]]);
-        }
-
-        var request_data = {
-            experiment:   alias_experiments,
-            analyzer:     [scope.report.analyzer],
-            output:       [chart_name],
-            plotter:      set_plotter,
-            legend:       legend_experiments,
-            report_number:  scope.report.number,
-            //height:       300,
-            //width:        400,
-        };
-
-        base_url = scope.report.url_prefix;
-
-        var plot_detail = {};
-        plot_detail["required_plotter"] = required_plotter;
-        plot_detail["data"] =  {
-            analyzer:     [scope.report.analyzer],
-            output:       [chart_name],
-            plotter:      set_plotter,
-        };
-
-        if(sub_content.selected_template != undefined)// && sub_content.selected_template != "Default")
-        {
-            plot_detail["selected_template"] = sub_content.selected_template;
-            request_data.parameter = plot_detail["selected_template"];
-        }
-        else
-        {
-            plot_detail["selected_template"] = sub_content.defaultplotter.parameter;
-            request_data.parameter = plot_detail["selected_template"];
-        }
-
-        if(sub_content.merged != undefined)// && sub_content.selected_template != "Default")
-        {
-            plot_detail["merged"] = sub_content.merged;
-            request_data.merged = plot_detail["merged"];
-        }
-        else
-        {
-            plot_detail["merged"] = true;
-            request_data.merged = plot_detail["merged"];
-        }
-
-        beat.experiments.utils.getPlotData(base_url,
-                                               request_data, required_plotter, plotterparameter, content_type,
-                                               function(r_image_data, selected_content_type) {
-                        download(r_image_data, chart_id + "." + content_type.toLowerCase(), selected_content_type);
-                                               }
-                                               );
-    }
-
-
-    //Retrieve chart from api and display on proper item
-    function _retrieve_chart(scope, sub_content, container)
-    {
-        var required_plotter = [];
-        var set_plotter = ''
-        if(sub_content.selected_plotter != undefined)
-        {
-            //required_plotter.push(sub_content.selected_plotter);
-            set_plotter = sub_content.selected_plotter;
-            var requested_dataformat = '';
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].name == sub_content.selected_plotter)
-                {
-                    requested_dataformat = scope.report.plotters[i].dataformat;
-                    break;
-                }
-            }
-
-            //Get default plotter for required dataformat
-            for (var i = 0; i < scope.report.defaultplotters.length; i++)
-            {
-                if(scope.report.defaultplotters[i].dataformat == requested_dataformat)
-                {
-                    sub_content.defaultplotter = scope.report.defaultplotters[i];
-                    break;
-                }
-            }
-
-            required_plotter.push(sub_content.defaultplotter.plotter);
-
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].dataformat == requested_dataformat && scope.report.plotters[i].name != sub_content.defaultplotter.plotter)
-                {
-                    required_plotter.push(scope.report.plotters[i].name);
-                }
-            }
-        }
-        else
-        {
-            //Get default plotter for required dataformat
-            for (var i = 0; i < scope.report.defaultplotters.length; i++)
-            {
-                if(scope.report.defaultplotters[i].dataformat == sub_content.description)
-                {
-                    sub_content.defaultplotter = scope.report.defaultplotters[i];
-                    break;
-                }
-            }
-
-            required_plotter.push(sub_content.defaultplotter.plotter);
-            set_plotter = sub_content.defaultplotter.plotter;
-
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].dataformat == sub_content.description && scope.report.plotters[i].name != sub_content.defaultplotter.plotter)
-                {
-                    required_plotter.push(scope.report.plotters[i].name);
-                }
-            }
-        }
-
-        var plotterparameter = [];
-        //get plotterparameters valid for requested plotter
-        var required_plotter_id = undefined;
-        for(var i = 0; i < scope.report.plotters.length; i++)
-        {
-            if(required_plotter[0] == scope.report.plotters[i].name)
-            {
-                required_plotter_id = scope.report.plotters[i].id
-            }
-        }
-
-        //Get other plotterparameter
-        for (var i = 0; i < scope.report.plotterparameter.length; i++)
-        {
-            if(required_plotter_id == undefined)
-            {
-                plotterparameter.push(scope.report.plotterparameter[i].name);
-            }
-            else
-            {
-                if(scope.report.plotterparameter[i].plotter == required_plotter_id)
-                {
-                    plotterparameter.push(scope.report.plotterparameter[i].name);
-                }
-            }
-        }
-
-        var chart_name = sub_content.name;
-
-        var legend_experiments = '';
-        var alias_experiments = [];
-
-        for(var i = 0; i < scope.report.experiments.length; i++)
-        {
-            if(i == 0)
-                legend_experiments = scope.report_experiments_alias[scope.report.experiments[i]];
-            else
-                legend_experiments = legend_experiments+ "&" +scope.report_experiments_alias[scope.report.experiments[i]];
-
-            if(Object.keys(scope.$parent.report_experiments_alias_from_content).length === 0 || scope.report_experiments_alias_from_content[scope.report.experiments[i]] == undefined )
-                alias_experiments.push(scope.report_experiments_alias[scope.report.experiments[i]]);
-            else
-                alias_experiments.push(scope.report_experiments_alias_from_content[scope.report.experiments[i]]);
-        }
-
-        var request_data = {
-            experiment:   alias_experiments,
-            analyzer:     [scope.report.analyzer],
-            output:       [chart_name],
-            plotter:      set_plotter,
-            legend:       legend_experiments,
-            report_number: scope.report.number,
-            //height:       300,
-            //width:        400,
-        };
-
-        base_url = scope.report.url_prefix;
-        //scope.plots_details[container.id];
-
-        var plot_detail = {};
-        plot_detail["required_plotter"] = required_plotter;
-        plot_detail["data"] =  {
-            analyzer:     [scope.report.analyzer],
-            output:       [chart_name],
-            plotter:      set_plotter,
-        };
-
-
-        if(sub_content.selected_template != undefined)// && sub_content.selected_template != "Default")
-        {
-            plot_detail["selected_template"] = sub_content.selected_template;
-            request_data.parameter = plot_detail["selected_template"];
-        }
-        else
-        {
-            plot_detail["selected_template"] = sub_content.defaultplotter.parameter;
-            request_data.parameter = plot_detail["selected_template"];
-        }
-
-        if(sub_content.merged != undefined)// && sub_content.selected_template != "Default")
-        {
-            plot_detail["merged"] = sub_content.merged;
-            request_data.merged = plot_detail["merged"];
-        }
-        else
-        {
-            plot_detail["merged"] = true;
-            request_data.merged = plot_detail["merged"];
-        }
-
-        scope.plots_details[container.id]= plot_detail;
-
-        if(scope.report.status == "editable" && scope.report.number != scope.report_number)
-        {
-            beat.experiments.utils.displayPlot(base_url, $(container).find('.panel-body')[0],
-                                               request_data, required_plotter, plotterparameter, false,
-
-                                               function(r_plotter, r_plotterparameter, r_merged) {
-                                                scope.plots_details[container.id]["data"]["plotter"] = r_plotter;
-                                                scope.plots_details[container.id]["selected_template"] = r_plotterparameter;
-                                                scope.plots_details[container.id]["merged"] = r_merged;
-                                               }
-                                               );
-        }
-        else
-        {
-            beat.experiments.utils.displayPlot(base_url, $(container).find('.panel-body')[0],
-                                               request_data, [], [], false,
-
-                                               function(r_plotter, r_plotterparameter, r_merged) {
-                                               }
-                                               );
-        }
-    }
-
-    function arraysInCommon(arrays)
-    {
-        var i, common,
-        L= arrays.length, min= Infinity;
-        while(L){
-            if(arrays[--L].length<min){
-                min= arrays[L].length;
-                i= L;
-            }
-        }
-        common= arrays.splice(i, 1)[0];
-        return common.filter(function(itm, indx){
-            if(common.indexOf(itm)== indx){
-                return arrays.every(function(arr){
-                    return arr.indexOf(itm)!= -1;
-                });
-            }
-        });
-    }
-
-    }
-});
-
-
-//Dynamic tables items
-app.directive('item', function ($compile) {
-   function createTDElement(column) {
-      var table = angular.element('<table><tr><td class="tableitemspace" thecolumn="' + column + '"></td></tr></table>');
-      return table.find('td');
-   }
-
-   function render(element, scope) {
-    var column, html, i;
-    var columns = scope.$parent.tables_details[scope.$parent.dattrs.tableid];
-
-    for (i = 0; i < columns.length ; i++) {
-        if(i == 0 && columns.length > 0)
-        {
-            html = $compile(createTDElement("experiment"))(scope);
-
-        }
-       column = columns[i];
-       if (column.selected) {
-
-          html = $compile(createTDElement(column.name))(scope);
-          element.append(html);
-       }
-    }
-
-
-   }
-
-   return {
-   //   restrict: 'A',
-      scope: {
-         exp_name: "=",
-         item: "=",
-         columns: "="
-      },
-      compile: function () {
-         return function (scope, element) {
-            render(element, scope);
-         }
-
-      }
-   };
-
-});
-
-//Directive used to set proper item value in selected column
-app.directive("thecolumn",['$compile', function ($compile) {
-
-   return {
-      scope: {
-         thecolumn: "@"
-      },
-      restrict: 'A',
-      template: '{{itemValue}}',
-      controller: ['$scope', function ($scope) {
-
-         var the_parent = $scope.$parent.$parent.$parent.$parent;
-         var report_experiments = $scope.$parent.$parent.$parent.$parent.report_experiments;
-         var report_experiments_alias = $scope.$parent.$parent.$parent.$parent.report_experiments_alias;
-         var floating_point_precision = $scope.$parent.$parent.$parent.$parent.floating_point_precision;
-         var report = $scope.$parent.$parent.$parent.report;
-         var experiment_name = $scope.$parent.item;
-
-         if(jQuery.isEmptyObject(report_experiments) || (report_experiments[experiment_name] == undefined))
-         {
-            return
-         }
-
-         var analyzer_block = report_experiments[experiment_name].analyzer_block;
-         var report_algorithm_parameters_experiment = $scope.$parent.$parent.$parent.$parent.report_algorithm_parameters_experiment;
-
-         if($scope.thecolumn != "experiment")
-         {
-            if(analyzer_block == undefined)
-            {
-                angular.forEach(report_experiments, function(value, key)
-                {
-                    angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer)
-                    {
-                        if(value_analyzer.algorithm == report.analyzer && (key_analyzer in report_experiments[experiment_name].declaration.analyzers))
-                        {
-                            report_experiments[experiment_name].analyzer_block = key_analyzer;
-                            analyzer_block = report_experiments[experiment_name].analyzer_block;
-                        }
-                    });
-                });
-            }
-
-            if($scope.thecolumn.indexOf("execution_time.") == 0)
-            {
-                //execution time information
-                var block_name = $scope.thecolumn.split("execution_time.")[1].split("[s]")[0];
-                if(Object.keys(report_experiments[experiment_name].execution_info).length === 0)
-                {
-                    $scope.itemValue = "-";
-                }
-                else
-                {
-                    if(report_experiments[experiment_name].execution_info[block_name] == undefined)
-                    {
-                        $scope.itemValue = "-";
-                    }
-                    else
-                    {
-                        $scope.itemValue = (report_experiments[experiment_name].execution_info[block_name].linear_execution_time).toFixed(floating_point_precision.selected);;
-                    }
-                }
-            }
-            else if($scope.thecolumn.indexOf("experiment.") == 0)
-            {
-                //total execution time information
-                var block_name = $scope.thecolumn.split("execution_time.")[1];
-                var total_time = 0;
-                if(Object.keys(report_experiments[experiment_name].execution_info).length === 0)
-                {
-                    total_time = "-";
-                }
-                else
-                {
-                    angular.forEach(report_experiments[experiment_name].execution_info, function(value, key)
-                    {
-                        total_time += value.linear_execution_time;
-                    });
-                }
-
-                $scope.itemValue = total_time.toFixed(floating_point_precision.selected);
-            }
-            else if($scope.thecolumn.indexOf("algorithm_parameter.") == 0)
-            {
-                //total execution time information
-                var block_name = $scope.thecolumn.split("algorithm_parameter.")[1];
-                var algorithm_name = block_name.split("__")[0];
-                var parameter_name = block_name.split("__")[1];
-                if(report_algorithm_parameters_experiment[experiment_name][algorithm_name] != undefined)
-                {
-                    var value = "";
-                    if(report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name] != undefined)
-                    {
-                        for(var i = 0; i < report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name].length; i++)
-                        {
-                            if(i > 0 && i < report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name].length -1 )
-                            {
-                                value += ",";
-                            }
-                            value = report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name][i];
-                        }
-                    }
-                    else if(report["all_experiments"][experiment_name]["declaration"]["globals"][algorithm_name][parameter_name] != undefined)
-                    {
-                        //get globals value
-                        value = report["all_experiments"][experiment_name]["declaration"]["globals"][algorithm_name][parameter_name];
-                    }
-                    else
-                    {
-                        //nothing is defined
-                        value = "-";
-                    }
-
-                    $scope.itemValue = value;
-                }
-                else
-                {
-                    $scope.itemValue = "-";
-                }
-            }
-            else
-            {
-                //results information
-                $scope.itemValue = (report_experiments[experiment_name].results[analyzer_block][$scope.thecolumn].value).toFixed(floating_point_precision.selected);
-            }
-         }
-         else
-         {
-                var experiment_alias = report_experiments_alias[experiment_name];
-                $scope.itemValue = experiment_alias;
-         }
-      }]//,
-      //link: function(scope, element, attrs)
-      //{
-      //  console.log(scope);
-      //  var report_experiments_alias = scope.$parent.$parent.$parent.$parent.report_experiments_alias;
-      //  var experiment_name = scope.$parent.item;
-      //  var experiment_alias = report_experiments_alias[experiment_name];
-      //  scope.$watch(scope.$parent.$parent.$parent.$parent.report_experiments_alias,function(newValue, oldValue)
-      //  {
-      //      console.log("changed");
-      //      console.log(report_experiments_alias);
-      //      console.log(oldValue);
-      //      console.log(newValue);
-      //  });
-      //
-      //}
-   }
-}]);
-
-//Directive used to generate the dynamic table loading partials
-app.directive("tableDynamic", function(){
-
-    return {
-        restrict: 'A',
-        scope: true,
-        replace: true,
-     Â Ã‚ Ã‚ //templateUrl: "/reports/partials/reportTable/",
-     Â Ã‚ Ã‚ templateUrl: function(scope, elem, attrs)
-        {
-            var prefix = elem['urlprefix'];
-     Â Ã‚ Ã‚     var the_url = prefix + "/reports/partials/reportTable/";
-            return the_url;
-        },
-        link: function(scope, elem, attrs)
-        {
-             var prepend_item = {};
-             prepend_item["name"] = "experiment";
-             prepend_item["selected"] = true;
-
-             scope.tables_details[attrs.tableid].unshift(prepend_item);
-
-             angular.forEach(scope.tables_details, function(table_value, table_key)
-             {
-                for(var i = 0; i < table_value.length; i++)
-                {
-                    if(table_value[i].name.indexOf("execution_time") > -1 &&
-                       table_value[i].name.indexOf("[s]") == -1)
-                    {
-                        table_value[i].name = table_value[i].name + "[s]";
-                    }
-                };
-             });
-
-             scope.dattrs = attrs;
-        }
-    };
-});
-
-//Directive used to generate the dynamic table loading partials
-app.directive("myreportinfo", function(){
-
-    return {
-        restrict: 'E',
-        scope: true,
-        replace: true,
-     Â Ã‚ Ã‚ //templateUrl: "/reports/partials/reportTable/",
-     Â Ã‚ Ã‚ templateUrl: function(scope, elem, attrs)
-        {
-            var prefix = elem['urlprefix'];
-     Â Ã‚ Ã‚     var the_url = prefix + "/reports/partials/reportInfo/";
-            return the_url;
-        },
-        link: function(scope, elem, attrs)
-        {
-        },
-        controller: ['$scope', function ($scope) {
-        }],
-    };
-});
-
-//Directive used to handle table settings click
-app.directive("buttonsettings", function()
-{
-    return {
-        link:function(scope, element, attrs)
-        {
-            element.bind("click", function()
-            {
-                var name_to_remove = "experiment";
-                var tables_details = scope.$parent.tables_details[scope.dattrs.tableid];
-                for(var i = 0; i < tables_details.length; i++)
-                {
-                    if(tables_details[i].name == "experiment")
-                    {
-                        tables_details.splice(i,1);
-                        break;
-                    }
-                }
-                multiple_selector_updater.current_table = scope.dattrs.tableid;
-                multiple_selector_updater.display(tables_details);
-            });
-        }
-    };
-});
-
-//Directive used to handle table settings click
-app.directive("sortdata", function($compile)
-{
-    return {
-        link:function(scope, element, attrs)
-        {
-            element.bind("click", function()
-            {
-                var the_parent = scope.$parent.$parent.$parent.$parent;
-                var analyzer_block = undefined;
-                var report_experiments = the_parent.report_experiments;
-                var report = the_parent.report;
-                var table_id = attrs.sorttblid;   //get table id
-                the_parent.sorted_tables.push(table_id);
-
-                if(the_parent.sorted_experiments_keys_reverse[table_id] == undefined)
-                {
-                    the_parent.sorted_experiments_keys_reverse[table_id] = true;
-                }
-                else
-                {
-                    the_parent.sorted_experiments_keys_reverse[table_id] = !the_parent.sorted_experiments_keys_reverse[table_id];
-                }
-
-                the_parent.sorted_experiments_keys_tables_sortkey[table_id] = attrs.sortth;   //set the sortKey to the param passed
-
-                if(the_parent.sorted_experiments_keys_tables_sortkey[table_id] != "experiment")
-                {
-                   var local_sort_list = [];
-
-                   if(analyzer_block == undefined)
-                   {
-                       angular.forEach(report_experiments, function(value, key)
-                       {
-                           angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer)
-                           {
-                               if(value_analyzer.algorithm == report.analyzer && (key_analyzer in report_experiments[key].declaration.analyzers))
-                               {
-                                   report_experiments[key].analyzer_block = key_analyzer;
-                                   analyzer_block = report_experiments[key].analyzer_block;
-                               }
-                           });
-                       });
-                   }
-
-                   if(the_parent.sorted_experiments_keys_tables_sortkey[table_id].indexOf("execution_time.") == 0)
-                   {
-                       //execution time information
-                       var block_name = the_parent.sorted_experiments_keys_tables_sortkey[table_id].split("execution_time.")[1].split("[s]")[0];
-                       angular.forEach(report_experiments, function(value, key)
-                       {
-                            var local_sort_dict = {};
-                            local_sort_dict["experiment"] = key;
-                            var itemValue = "";
-
-                            if(Object.keys(report_experiments[key].execution_info).length === 0)
-                            {
-                                itemValue = "-";
-                            }
-                            else
-                            {
-                                if(report_experiments[key].execution_info[block_name] == undefined)
-                                {
-                                    itemValue = "-";
-                                }
-                                else
-                                {
-                                    itemValue = (report_experiments[key].execution_info[block_name].linear_execution_time);
-                                }
-                            }
-
-                            local_sort_dict["value"] = itemValue;
-                            local_sort_list.push(local_sort_dict);
-                       });
-                   }
-                   else if(the_parent.sorted_experiments_keys_tables_sortkey[table_id].indexOf("experiment.") == 0)
-                   {
-                       //total execution time information
-                       var block_name = the_parent.sorted_experiments_keys_tables_sortkey[table_id].split("execution_time.")[1];
-
-                       angular.forEach(report_experiments, function(value, key)
-                       {
-                            var local_sort_dict = {};
-                            local_sort_dict["experiment"] = key;
-                            var itemValue = "";
-                            var total_time = 0;
-
-                            if(Object.keys(report_experiments[key].execution_info).length === 0)
-                            {
-                                total_time = "-";
-                            }
-                            else
-                            {
-                                angular.forEach(report_experiments[key].execution_info, function(value, key)
-                                {
-                                    total_time += value.linear_execution_time;
-                                });
-                            }
-
-                            itemValue = total_time;
-
-                            local_sort_dict["value"] = itemValue;
-                            local_sort_list.push(local_sort_dict);
-                       });
-                   }
-                   else if(the_parent.sorted_experiments_keys_tables_sortkey[table_id].indexOf("algorithm_parameter.") == 0)
-                   {
-                       //total execution time information
-                       var block_name = the_parent.sorted_experiments_keys_tables_sortkey[table_id].split("algorithm_parameter.")[1];
-                       var algorithm_name = block_name.split("__")[0];
-                       var parameter_name = block_name.split("__")[1];
-
-                       angular.forEach(report_experiments, function(value, key)
-                       {
-
-                            var local_sort_dict = {};
-                            local_sort_dict["experiment"] = key;
-                            var itemValue = "";
-
-                            if(the_parent.report_algorithm_parameters_experiment[key][algorithm_name] != undefined)
-                            {
-                                var value = "";
-                                if(the_parent.report_algorithm_parameters_experiment[key][algorithm_name][parameter_name] != undefined)
-                                {
-                                    for(var i = 0; i < the_parent.report_algorithm_parameters_experiment[key][algorithm_name][parameter_name].length; i++)
-                                    {
-                                        if(i > 0 && i < the_parent.report_algorithm_parameters_experiment[key][algorithm_name][parameter_name].length -1 )
-                                        {
-                                            value += ",";
-                                        }
-                                        value = the_parent.report_algorithm_parameters_experiment[key][algorithm_name][parameter_name][i];
-                                    }
-                                }
-                                else if(the_parent.report["all_experiments"][key]["declaration"]["globals"][algorithm_name][parameter_name] != undefined)
-                                {
-                                    //get globals value
-                                    value = the_parent.report["all_experiments"][key]["declaration"]["globals"][algorithm_name][parameter_name];
-                                }
-                                else
-                                {
-                                    //nothing is defined
-                                    value = "-";
-                                }
-
-                                itemValue = value;
-                            }
-                            else
-                            {
-                                itemValue = "-";
-                            }
-
-                            local_sort_dict["value"] = itemValue;
-                            local_sort_list.push(local_sort_dict);
-                       });
-                   }
-                   else
-                   {
-                       //results information
-                       angular.forEach(report_experiments, function(value, key)
-                       {
-                            var local_sort_dict = {};
-                            local_sort_dict["experiment"] = key;
-                            local_sort_dict["value"] = report_experiments[key].results[analyzer_block][the_parent.sorted_experiments_keys_tables_sortkey[table_id]].value;
-
-                            local_sort_list.push(local_sort_dict);
-
-                       });
-
-                   }
-
-                   if(the_parent.sorted_experiments_keys_reverse[table_id])
-                   {
-                        local_sort_list.sort(function(a, b)
-                        {
-                             return a.value - b.value;
-                        });
-                   }
-                   else
-                   {
-                        local_sort_list.sort(function(a, b)
-                        {
-                             return b.value - a.value;
-                        });
-                   }
-
-                   the_parent.sorted_experiments_keys_tables[table_id] = [];
-                   the_parent.sorted_experiments_alias_keys_tables[table_id] = [];
-                   for(var i = 0; i < local_sort_list.length; i++)
-                   {
-                       the_parent.sorted_experiments_keys_tables[table_id].push(local_sort_list[i]["experiment"]);
-                       if(the_parent.report.status ==  "editable")
-                       {
-                           the_parent.sorted_experiments_alias_keys_tables[table_id].push(the_parent.report_experiments_alias[local_sort_list[i]["experiment"]]);
-                       }
-                   }
-
-                }
-                else
-                {
-                    var experiments_aliases = [];
-                    for(var i = 0; i < the_parent.report.experiments.length; i++)
-                    {
-                        var experiment_name  = the_parent.report.experiments[i];
-                        var experiment_alias = the_parent.report_experiments_alias[experiment_name];
-                        experiments_aliases.push(experiment_alias);
-                    }
-
-                    if(the_parent.sorted_experiments_keys_reverse[table_id])
-                    {
-                        experiments_aliases.sort(function(a, b)
-                        {
-                             return a.value - b.value;
-                        });
-                    }
-                    else
-                    {
-                        experiments_aliases.reverse(function(a, b)
-                        {
-                             return b.value - a.value;
-                        });
-                    }
-
-                    the_parent.sorted_experiments_keys_tables[table_id] = [];
-                    the_parent.sorted_experiments_alias_keys_tables[table_id] = [];
-                    for(var i = 0; i < experiments_aliases.length; i++)
-                    {
-                        for(var the_experiment_key in the_parent.report_experiments_alias)
-                        {
-                            if(the_parent.report_experiments_alias[the_experiment_key] == experiments_aliases[i])
-                            {
-                                the_parent.sorted_experiments_keys_tables[table_id].push(the_experiment_key);
-                                if(the_parent.report.status ==  "editable")
-                                {
-                                    the_parent.sorted_experiments_alias_keys_tables[table_id].push(the_parent.report_experiments_alias[the_experiment_key]);
-                                }
-                            }
-
-                        }
-                    }
-                }
-
-                var name_to_remove = "experiment";
-                var tables_details = the_parent.tables_details[table_id];
-                for(var i = 0; i < tables_details.length; i++)
-                {
-                    if(tables_details[i].name == "experiment")
-                    {
-                        tables_details.splice(i,1);
-                        break;
-                    }
-                }
-
-                var element = document.getElementById(table_id);
-
-                $(element).attr('id', null);
-                $compile(element)(scope.$parent.$parent);
-                $(element).attr('id', table_id);
-            });
-        }
-    };
-});
-
-//Directive used to handle table settings click
-app.directive("tableprecision", function($compile)
-{
-    return {
-        link:function(scope, element, attrs)
-        {
-            element.bind("change", function()
-            {
-                fixFloatingPoint(scope.$parent.$parent.floating_point_precision.selected);
-            });
-
-            function fixFloatingPoint(selected_precision)
-            {
-                if(!scope.$$phase)
-                {
-                    //$digest or $apply
-                    var parent_scope = scope.$parent;
-                    var table_details = parent_scope.tables_details;
-
-                    for(var i = 0; i < parent_scope.report.orderedcontent.length; i++)
-                    {
-                        var element = document.getElementById(parent_scope.report.orderedcontent[i]);
-
-                        $(element).remove();
-
-                        var id_content = parent_scope.report.orderedcontent[i];
-                        var type = id_content.split("_")[0];
-                        parent_scope.$parent.$parent.$broadcast("addSavedElement", id_content, type);
-                    }
-                }
-            }
-        }
-    };
-});
-
-//Directive used to handle table settings click
-app.directive("buttondeleteitem", function()
-{
-    return {
-        link:function(scope, element, attrs)
-        {
-            element.bind("click", function()
-            {
-                var elementToRemove = element.context.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
-                beat.ui.report.remove_item('report_remove_item', elementToRemove, scope);
-            });
-        }
-    };
-});
-
-//Directive used to export graph/tables on click
-app.directive("buttonexportitem", function()
-{
-    return {
-        link:function(scope, element, attrs)
-        {
-            element.bind("click", function()
-            {
-                var the_element = element.context.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
-                if(the_element.id.indexOf("chart_") == 0)
-                {
-                    var content_detail = {};
-                    var chart_id = the_element.id;
-
-                    content_detail["name"] = scope.plots_details[chart_id].data.output[0];
-
-                    if(scope.plots_details[chart_id].data.plotter != undefined)
-                    {
-                        content_detail["selected_plotter"] = scope.plots_details[chart_id].data.plotter;
-                    }
-                    if(scope.plots_details[chart_id].data.parameter != undefined)
-                    {
-                        content_detail["selected_template"] = scope.plots_details[chart_id].data.parameter;
-                    }
-                    else if(scope.plots_details[chart_id]["selected_template"] != undefined)
-                    {
-                        content_detail["selected_template"] = scope.plots_details[chart_id]["selected_template"];
-                    }
-                    if(scope.plots_details[chart_id].data.merged != undefined)
-                    {
-                        content_detail["merged"] = scope.plots_details[chart_id].data.merged;
-                    }
-
-                    var accessnumber = "no_number";
-                    if(scope.report.number == scope.report_number)
-                    {
-                        accessnumber = "number";
-                    }
-                    _get_chart(scope, content_detail, element[0].id, chart_id);
-                }
-                else if(the_element.id.indexOf("table_") == 0)
-                {
-                    var separator = ",";
-                    var csv_text = export_table_as_csv(scope, the_element.id, separator);
-                    var output_filename = the_element.id + ".csv";
-                    download(csv_text, output_filename, "text/plain");
-                }
-            });
-        }
-    };
-
-    function export_table_as_csv(scope, table_id, separator)
-    {
-        var report_experiments = scope.$parent.report_experiments;
-        var report_experiments_alias = scope.$parent.report_experiments_alias;
-        var floating_point_precision = scope.$parent.floating_point_precision;
-        var report = scope.report;
-        var final_table = [];
-        var table_headers = [];
-        angular.forEach(scope.tables_details[table_id], function(table_value, table_key)
-        {
-            if(table_value.selected)
-            {
-                table_headers.push(table_value.name);
-            }
-        });
-
-        final_table.push(table_headers);
-
-        angular.forEach(report_experiments, function(experiment_value, experiment_key)
-        {
-            var table_items = [];
-
-            angular.forEach(table_headers, function(table_value, table_key)
-            {
-                var experiment_name = experiment_key;
-                var analyzer_block = report_experiments[experiment_name].analyzer_block;
-                var report_algorithm_parameters_experiment = scope.$parent.report_algorithm_parameters_experiment;
-
-                if(table_value != "experiment")
-                {
-                   if(analyzer_block == undefined)
-                   {
-                       angular.forEach(report_experiments, function(value, key)
-                       {
-                           angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer)
-                           {
-                               if(value_analyzer.algorithm == report.analyzer && (key_analyzer in report_experiments[experiment_name].declaration.analyzers))
-                               {
-                                   report_experiments[experiment_name].analyzer_block = key_analyzer;
-                                   analyzer_block = report_experiments[experiment_name].analyzer_block;
-                               }
-                           });
-                       });
-                   }
-
-                   if(table_value.indexOf("execution_time.") == 0)
-                   {
-                       //execution time information
-                       var block_name = table_value.split("execution_time.")[1].split("[s]")[0];
-                       if(Object.keys(report_experiments[experiment_name].execution_info).length === 0)
-                       {
-                           table_items.push("-");
-                       }
-                       else
-                       {
-                           if(report_experiments[experiment_name].execution_info[block_name] == undefined)
-                           {
-                               table_items.push("-");
-                           }
-                           else
-                           {
-                               table_items.push((report_experiments[experiment_name].execution_info[block_name].linear_execution_time).toFixed(floating_point_precision.selected));
-                           }
-                       }
-                   }
-                   else if(table_value.indexOf("experiment.") == 0)
-                   {
-                       //total execution time information
-                       var block_name = table_value.split("execution_time.")[1];
-                       var total_time = 0;
-                       if(Object.keys(report_experiments[experiment_name].execution_info).length === 0)
-                       {
-                           total_time = "-";
-                       }
-                       else
-                       {
-                           angular.forEach(report_experiments[experiment_name].execution_info, function(value, key)
-                           {
-                               total_time += value.linear_execution_time;
-                           });
-                       }
-
-                       table_items.push(total_time.toFixed(floating_point_precision.selected));
-                   }
-                   else if(table_value.indexOf("algorithm_parameter.") == 0)
-                   {
-                       //total execution time information
-                       var block_name = table_value.split("algorithm_parameter.")[1];
-                       var algorithm_name = block_name.split("__")[0];
-                       var parameter_name = block_name.split("__")[1];
-                       if(report_algorithm_parameters_experiment[experiment_name][algorithm_name] != undefined)
-                       {
-                           var value = "";
-                           for(var i = 0; i < report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name].length; i++)
-                           {
-                               if(i > 0 && i < report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name].length -1 )
-                               {
-                                   value += ",";
-                               }
-                               value = report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name][i];
-                           }
-
-                           table_items.push(value);
-                       }
-                       else
-                       {
-                           table_items.push("-");
-                       }
-                   }
-                   else
-                   {
-                       //results information
-                       table_items.push((report_experiments[experiment_name].results[analyzer_block][table_value].value).toFixed(floating_point_precision.selected));
-                   }
-                }
-                else
-                {
-                   var experiment_alias = report_experiments_alias[experiment_name];
-                   table_items.push(experiment_alias);
-                }
-
-            });
-
-            final_table.push(table_items);
-
-        });
-
-        var csv_text = "";
-        for(var i = 0; i < final_table.length; i++)
-        {
-            if(i != 0)
-            {
-                csv_text += "\n";
-            }
-
-            for(var j = 0; j < final_table[i].length; j++)
-            {
-                if( j != 0)
-                {
-                    csv_text += separator;
-                }
-                csv_text += final_table[i][j];
-            }
-        }
-
-        var pre_base64_text = "data:text/plain;base64,";
-        var base_64_csv_text = pre_base64_text + Base64.encode(csv_text);
-        return base_64_csv_text;
-    }
-
-    //Retrieve chart from api and display on proper item
-    function _get_chart(scope, sub_content, content_type, chart_id)
-    {
-        var required_plotter = [];
-        var set_plotter = ''
-        if(sub_content.selected_plotter != undefined)
-        {
-            //required_plotter.push(sub_content.selected_plotter);
-            set_plotter = sub_content.selected_plotter;
-            var requested_dataformat = '';
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].name == sub_content.selected_plotter)
-                {
-                    requested_dataformat = scope.report.plotters[i].dataformat;
-                    break;
-                }
-            }
-
-            //Get default plotter for required dataformat
-            for (var i = 0; i < scope.report.defaultplotters.length; i++)
-            {
-                if(scope.report.defaultplotters[i].dataformat == requested_dataformat)
-                {
-                    sub_content.defaultplotter = scope.report.defaultplotters[i];
-                    break;
-                }
-            }
-
-            required_plotter.push(sub_content.defaultplotter.plotter);
-
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].dataformat == requested_dataformat && scope.report.plotters[i].name != sub_content.defaultplotter.plotter)
-                {
-                    required_plotter.push(scope.report.plotters[i].name);
-                }
-            }
-        }
-        else
-        {
-            //Get default plotter for required dataformat
-            for (var i = 0; i < scope.report.defaultplotters.length; i++)
-            {
-                if(scope.report.defaultplotters[i].dataformat == sub_content.description)
-                {
-                    sub_content.defaultplotter = scope.report.defaultplotters[i];
-                    break;
-                }
-            }
-
-            required_plotter.push(sub_content.defaultplotter.plotter);
-            set_plotter = sub_content.defaultplotter.plotter;
-
-            //Get other plotters for required dataformat
-            for (var i = 0; i < scope.report.plotters.length; i++)
-            {
-                if(scope.report.plotters[i].dataformat == sub_content.description && scope.report.plotters[i].name != sub_content.defaultplotter.plotter)
-                {
-                    required_plotter.push(scope.report.plotters[i].name);
-                }
-            }
-        }
-
-        var plotterparameter = [];
-        //get plotterparameters valid for requested plotter
-        var required_plotter_id = undefined;
-        for(var i = 0; i < scope.report.plotters.length; i++)
-        {
-            if(required_plotter[0] == scope.report.plotters[i].name)
-            {
-                required_plotter_id = scope.report.plotters[i].id
-            }
-        }
-
-        //Get other plotterparameter
-        for (var i = 0; i < scope.report.plotterparameter.length; i++)
-        {
-            if(required_plotter_id == undefined)
-            {
-                plotterparameter.push(scope.report.plotterparameter[i].name);
-            }
-            else
-            {
-                if(scope.report.plotterparameter[i].plotter == required_plotter_id)
-                {
-                    plotterparameter.push(scope.report.plotterparameter[i].name);
-                }
-            }
-        }
-
-        var chart_name = sub_content.name;
-
-        var legend_experiments = '';
-        for(var i = 0; i < scope.report.experiments.length; i++)
-        {
-            if(i == 0)
-                legend_experiments = scope.report_experiments_alias[scope.report.experiments[i]];
-            else
-                legend_experiments = legend_experiments+ "&" +scope.report_experiments_alias[scope.report.experiments[i]];
-        }
-
-        var request_data = {
-            experiment:   scope.report.experiments,
-            analyzer:     [scope.report.analyzer],
-            output:       [chart_name],
-            plotter:      set_plotter,
-            legend:       legend_experiments,
-            //height:       300,
-            //width:        400,
-        };
-
-        base_url = scope.report.url_prefix;
-
-        var plot_detail = {};
-        plot_detail["required_plotter"] = required_plotter;
-        plot_detail["data"] = request_data;
-        if(sub_content.selected_template != undefined)// && sub_content.selected_template != "Default")
-        {
-            plot_detail["selected_template"] = sub_content.selected_template;
-            request_data.parameter = plot_detail["selected_template"];
-        }
-        else
-        {
-            plot_detail["selected_template"] = sub_content.defaultplotter.parameter;
-            request_data.parameter = plot_detail["selected_template"];
-        }
-
-        if(sub_content.merged != undefined)// && sub_content.selected_template != "Default")
-        {
-            plot_detail["merged"] = sub_content.merged;
-            request_data.merged = plot_detail["merged"];
-        }
-        else
-        {
-            plot_detail["merged"] = true;
-            request_data.merged = plot_detail["merged"];
-        }
-
-        beat.experiments.utils.getPlotData(base_url,
-                                               request_data, required_plotter, plotterparameter, content_type,
-                                               function(r_image_data, selected_content_type) {
-                        download(r_image_data, chart_id + "." + content_type.toLowerCase(), selected_content_type);
-                                               }
-                                               );
-    }
-
-
-});
-
-
-//Directive used to handle table settings click
-app.directive("aliasexperiment", function($compile)
-{
-    return {
-        link:function(scope, element, attrs)
-        {
-            var alias_name = scope.$parent.$parent.$parent.$parent.report_experiments_alias[scope.name];
-            createAlias(scope.name, alias_name);
-
-            /*
-            element.on("blur", function()
-            {
-              val = $(this).val();
-              if (!val) {
-                alert("Alias for experiment" + scope.name + " can't be empty!");
-              }
-              else {
-                var experiment_name = this.id.split("input_")[1];
-                createAlias(experiment_name, val);
-              }
-            });
-            */
-
-            element.bind("click", function()
-            {
-                var input_element = document.getElementById("input_"+scope.name);
-                var icon_element = document.getElementById("icon_"+scope.name);
-                var button_element = document.getElementById("button_alias_"+scope.name);
-                if(! $(input_element).hasClass("input-disabled"))
-                {
-                    if ($(input_element).val())
-                    {
-                        $(input_element).addClass("input-disabled");
-                        $(input_element).attr("disabled", true);
-                        $(icon_element).removeClass("fa-unlock");
-                        $(icon_element).addClass("fa-lock");
-                        $(button_element).addClass("setalias");
-                        var experiment_name = attrs.id.split("button_alias_")[1];
-                        var alias_name = $(input_element).val();
-                        $(input_element).val(alias_name);
-                        createAlias(experiment_name, alias_name);
-                    }
-                    else
-                    {
-                        alert("Alias for experiment" + scope.name + " can't be empty!");
-                    }
-                }
-                else
-                {
-                    $(input_element).removeClass("input-disabled");
-                    $(input_element).attr("disabled", false);
-                    $(icon_element).removeClass("fa-lock");
-                    $(icon_element).addClass("fa-unlock");
-                    $(button_element).removeClass("setalias");
-
-                }
-            });
-
-            function createAlias(experiment_name, alias_name)
-            {
-                scope.report_experiments_alias[experiment_name] = alias_name;
-                scope.$parent.$parent.$parent.$parent.report_experiments_alias[experiment_name] = alias_name;
-
-                if(!scope.$$phase)
-                {
-                    //$digest or $apply
-                    var parent_scope = scope.$parent.$parent.$parent.$parent
-                    var table_details = parent_scope.tables_details;
-
-                    for(var i = 0; i < parent_scope.report.orderedcontent.length; i++)
-                    {
-                        var element = document.getElementById(parent_scope.report.orderedcontent[i]);
-
-                        $(element).remove();
-
-                        var id_content = parent_scope.report.orderedcontent[i];
-                        var type = id_content.split("_")[0];
-                        parent_scope.$parent.$broadcast("addSavedElement", id_content, type);
-                    }
-                }
-            }
-        }
-    };
-});
-
-
-////Directive used to generate the dynamic table loading partials
-//app.directive("chartdropdown", function(){
-//    return {
-//        restrict: 'E',
-//        scope: true,
-//        replace: true,
-//     Â Ã‚ Ã‚ templateUrl:"/reports/partials/reportChartDropDown/",
-//        link: function(scope, element, attrs)
-//        {
-//            scope.dattrs = attrs;
-//            var element_id = (element.context.id).split("selector_");
-//            var selected_template = scope.plots_details[element_id[1]].selected_template;
-//            if(selected_template != undefined)
-//            {
-//
-//                scope.dattrs.selected_template = selected_template;
-//                var selector_name = "#"+element.context.id + " select";
-//            }
-//            else
-//            {
-//                scope.dattrs.selected_template = "Default";
-//            }
-//
-//            element.bind("change", function()
-//            {
-//                var selected_template = element.context.children[0].selectedOptions[0].label;
-//                var elem_id = (attrs.id).split("selector_");
-//
-//                scope.plots_details[elem_id[1]].selected_template = selected_template;
-//
-//
-//                var type = "afdaf";
-//                var id_content = elem_id[1];
-//                scope.$parent.report.content[id_content].selected_template = selected_template;
-//                scope.$parent.$broadcast("redrawGraphElement", id_content, type);
-//
-//            });
-//
-//        }
-//
-//    };
-//});
diff --git a/beat/web/reports/static/reports/app/directives/saveReportItems.js b/beat/web/reports/static/reports/app/directives/saveReportItems.js
index 0b3e4119c6b1e453c5fa5604f3c8785631ff6fef..772b85c0a49ea12e99366b6cbb62d0d1bfaf9214 100644
--- a/beat/web/reports/static/reports/app/directives/saveReportItems.js
+++ b/beat/web/reports/static/reports/app/directives/saveReportItems.js
@@ -20,68 +20,21 @@
  * with the BEAT platform. If not, see http://www.gnu.org/licenses/.
  */
 
-//Directive for opening smart_selector list on "Add a report item" button click that displays options
+// Saves the report
 angular.module('reportApp').directive("savereportitems", ['$compile', 'GroupsService', function($compile, GroupsService){
 	return function(scope, element, attrs){
 		//add new report item
 		element.bind("click", function(){
-			//let savecontent = [];
-			let savecontent = {};
-			if(scope.plots_details != undefined){
-				angular.forEach(scope.plots_details, function(value, key){
-					//savecontent.push(key, value);
-					savecontent[key] = value;
-				});
-			}
-			if(scope.tables_details != undefined){
-				angular.forEach(scope.tables_details, function(value, key){
-					savecontent[key] = value;
-					//savecontent.push(key, value);
-				});
-
-				if(scope.floating_point_precision.selected != undefined){
-					savecontent["floating_point_precision"] = scope.floating_point_precision.selected;
-				}
-			}
-			if(scope.report_experiments_alias != undefined){
-				let alias_experiments = {};
-				angular.forEach(scope.report_experiments_alias, function(value, key){
-					alias_experiments[key] = value;
-				});
-				savecontent["alias_experiments"] = alias_experiments;
-			}
-
-			if(!(jQuery.isEmptyObject(scope.sorted_experiments_keys_tables)) && !(jQuery.isEmptyObject(scope.sorted_experiments_keys_reverse)) && !(jQuery.isEmptyObject(scope.sorted_experiments_keys_tables_sortkey))){
-				savecontent["sorted_tables_experiments"] = scope.sorted_experiments_keys_tables;
-				savecontent["sorted_tables_alias_experiments"] = scope.sorted_experiments_alias_keys_tables;
-				savecontent["sorted_tables_keys_reverse"] = scope.sorted_experiments_keys_reverse;
-				savecontent["sorted_tables_sortkey"] = scope.sorted_experiments_keys_tables_sortkey;
-			}
-
-			// save group data
-			savecontent['groups'] = GroupsService.serializeGroups();
-
 			//call set report content from factory
 			let mydict = {};
-			mydict["experiments"] = scope.report.experiments;
-			mydict["content"] = savecontent;
-
-			for(let i = 0; i < scope.report.experiments.length; i++){
-				scope.report_experiments_alias_from_content[scope.report.experiments[i]] = scope.report_experiments_alias[scope.report.experiments[i]];
-			}
+			mydict["content"] = {
+				'groups': GroupsService.serializeGroups()
+			};
 
-			updateReport(mydict);
-		});
-
-
-		function updateReport(data){
-			scope.reportFactory.updateReport(scope.user, scope.report_id, data, scope.url_prefix)
+			scope.reportFactory.updateReport(scope.user, scope.report_id, mydict, scope.url_prefix)
 			.error(function (error){
-				scope.status = `Unable to update report data: ${error ? error.message : ''}`;
-
 			});
-
-		}
+		});
 	};
 }]);
 
diff --git a/beat/web/reports/static/reports/app/directives/sortData.js b/beat/web/reports/static/reports/app/directives/sortData.js
deleted file mode 100644
index 9f9f9a0231dfae07554d448964a19aca76df514a..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/sortData.js
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to handle table settings click
-angular.module('reportApp').directive("sortdata", function($compile){
-	return {
-		link:function(scope, element, attrs){
-			element.bind("click", function(){
-				let the_parent = scope.$parent.$parent.$parent.$parent;
-				let analyzer_block = undefined;
-				let report_experiments = the_parent.report_experiments;
-				let report = the_parent.report;
-				let table_id = attrs.sorttblid;   //get table id
-				the_parent.sorted_tables.push(table_id);
-
-				if(the_parent.sorted_experiments_keys_reverse[table_id] == undefined){
-					the_parent.sorted_experiments_keys_reverse[table_id] = true;
-				}
-				else{
-					the_parent.sorted_experiments_keys_reverse[table_id] = !the_parent.sorted_experiments_keys_reverse[table_id];
-				}
-
-				the_parent.sorted_experiments_keys_tables_sortkey[table_id] = attrs.sortth;   //set the sortKey to the param passed
-
-				if(the_parent.sorted_experiments_keys_tables_sortkey[table_id] != "experiment"){
-					let local_sort_list = [];
-
-					if(analyzer_block == undefined){
-						angular.forEach(report_experiments, function(value, key){
-							angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer){
-								if(value_analyzer.algorithm == report.analyzer && (key_analyzer in report_experiments[key].declaration.analyzers)){
-									report_experiments[key].analyzer_block = key_analyzer;
-									analyzer_block = report_experiments[key].analyzer_block;
-								}
-							});
-						});
-					}
-
-					if(the_parent.sorted_experiments_keys_tables_sortkey[table_id].indexOf("execution_time.") == 0){
-						//execution time information
-						let block_name = the_parent.sorted_experiments_keys_tables_sortkey[table_id].split("execution_time.")[1].split("[s]")[0];
-						angular.forEach(report_experiments, function(value, key){
-							let local_sort_dict = {};
-							local_sort_dict["experiment"] = key;
-							let itemValue = "";
-
-							if(Object.keys(report_experiments[key].execution_info).length === 0){
-								itemValue = "-";
-							}
-							else{
-								if(report_experiments[key].execution_info[block_name] == undefined){
-									itemValue = "-";
-								}
-								else{
-									itemValue = (report_experiments[key].execution_info[block_name].linear_execution_time);
-								}
-							}
-
-							local_sort_dict["value"] = itemValue;
-							local_sort_list.push(local_sort_dict);
-						});
-					}
-					else if(the_parent.sorted_experiments_keys_tables_sortkey[table_id].indexOf("experiment.") == 0){
-						//total execution time information
-						let block_name = the_parent.sorted_experiments_keys_tables_sortkey[table_id].split("execution_time.")[1];
-
-						angular.forEach(report_experiments, function(value, key){
-							let local_sort_dict = {};
-							local_sort_dict["experiment"] = key;
-							let itemValue = "";
-							let total_time = 0;
-
-							if(Object.keys(report_experiments[key].execution_info).length === 0){
-								total_time = "-";
-							}
-							else{
-								angular.forEach(report_experiments[key].execution_info, function(value, key){
-									total_time += value.linear_execution_time;
-								});
-							}
-
-							itemValue = total_time;
-
-							local_sort_dict["value"] = itemValue;
-							local_sort_list.push(local_sort_dict);
-						});
-					}
-					else if(the_parent.sorted_experiments_keys_tables_sortkey[table_id].indexOf("algorithm_parameter.") == 0){
-						//total execution time information
-						let block_name = the_parent.sorted_experiments_keys_tables_sortkey[table_id].split("algorithm_parameter.")[1];
-						let algorithm_name = block_name.split("__")[0];
-						let parameter_name = block_name.split("__")[1];
-
-						angular.forEach(report_experiments, function(value, key){
-
-							let local_sort_dict = {};
-							local_sort_dict["experiment"] = key;
-							let itemValue = "";
-
-							if(the_parent.report_algorithm_parameters_experiment[key][algorithm_name] != undefined){
-								let value = "";
-								if(the_parent.report_algorithm_parameters_experiment[key][algorithm_name][parameter_name] != undefined){
-									for(let i = 0; i < the_parent.report_algorithm_parameters_experiment[key][algorithm_name][parameter_name].length; i++){
-										if(i > 0 && i < the_parent.report_algorithm_parameters_experiment[key][algorithm_name][parameter_name].length -1 ){
-											value += ",";
-										}
-										value = the_parent.report_algorithm_parameters_experiment[key][algorithm_name][parameter_name][i];
-									}
-								}
-								else if(the_parent.report["all_experiments"][key]["declaration"]["globals"][algorithm_name][parameter_name] != undefined){
-									//get globals value
-									value = the_parent.report["all_experiments"][key]["declaration"]["globals"][algorithm_name][parameter_name];
-								}
-								else{
-									//nothing is defined
-									value = "-";
-								}
-
-								itemValue = value;
-							}
-							else{
-								itemValue = "-";
-							}
-
-							local_sort_dict["value"] = itemValue;
-							local_sort_list.push(local_sort_dict);
-						});
-					}
-					else{
-						//results information
-						angular.forEach(report_experiments, function(value, key){
-							let local_sort_dict = {};
-							local_sort_dict["experiment"] = key;
-							local_sort_dict["value"] = report_experiments[key].results[analyzer_block][the_parent.sorted_experiments_keys_tables_sortkey[table_id]].value;
-
-							local_sort_list.push(local_sort_dict);
-
-						});
-
-					}
-
-					if(the_parent.sorted_experiments_keys_reverse[table_id]){
-						local_sort_list.sort(function(a, b){
-							return a.value - b.value;
-						});
-					}
-					else{
-						local_sort_list.sort(function(a, b){
-							return b.value - a.value;
-						});
-					}
-
-					the_parent.sorted_experiments_keys_tables[table_id] = [];
-					the_parent.sorted_experiments_alias_keys_tables[table_id] = [];
-					for(let i = 0; i < local_sort_list.length; i++){
-						the_parent.sorted_experiments_keys_tables[table_id].push(local_sort_list[i]["experiment"]);
-						if(the_parent.report.status ==  "editable"){
-							the_parent.sorted_experiments_alias_keys_tables[table_id].push(the_parent.report_experiments_alias[local_sort_list[i]["experiment"]]);
-						}
-					}
-
-				}
-				else{
-					let experiments_aliases = [];
-					for(let i = 0; i < the_parent.report.experiments.length; i++){
-						let experiment_name  = the_parent.report.experiments[i];
-						let experiment_alias = the_parent.report_experiments_alias[experiment_name];
-						experiments_aliases.push(experiment_alias);
-					}
-
-					if(the_parent.sorted_experiments_keys_reverse[table_id]){
-						experiments_aliases.sort(function(a, b){
-							return a.value - b.value;
-						});
-					}
-					else{
-						experiments_aliases.reverse(function(a, b){
-							return b.value - a.value;
-						});
-					}
-
-					the_parent.sorted_experiments_keys_tables[table_id] = [];
-					the_parent.sorted_experiments_alias_keys_tables[table_id] = [];
-					for(let i = 0; i < experiments_aliases.length; i++){
-						for(let the_experiment_key in the_parent.report_experiments_alias){
-							if(the_parent.report_experiments_alias[the_experiment_key] == experiments_aliases[i]){
-								the_parent.sorted_experiments_keys_tables[table_id].push(the_experiment_key);
-								if(the_parent.report.status ==  "editable"){
-									the_parent.sorted_experiments_alias_keys_tables[table_id].push(the_parent.report_experiments_alias[the_experiment_key]);
-								}
-							}
-
-						}
-					}
-				}
-
-				let name_to_remove = "experiment";
-				let tables_details = the_parent.tables_details[table_id];
-				for(let i = 0; i < tables_details.length; i++){
-					if(tables_details[i].name == "experiment"){
-						tables_details.splice(i,1);
-						break;
-					}
-				}
-
-				let element = document.getElementById(table_id);
-
-				$(element).attr('id', null);
-				$compile(element)(scope.$parent.$parent);
-				$(element).attr('id', table_id);
-			});
-		}
-	};
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/tableDynamic.js b/beat/web/reports/static/reports/app/directives/tableDynamic.js
deleted file mode 100644
index 40d296db4026c14d28686936f7ebb7fda0c227bc..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/tableDynamic.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to generate the dynamic table loading partials
-angular.module('reportApp').directive("tableDynamic", function(){
-	return {
-		restrict: 'A',
-		scope: true,
-		replace: true,
-		 Â Ã‚ Ã‚ //templateUrl: "/reports/partials/reportTable/",
-		 Â Ã‚ Ã‚ templateUrl: function(scope, elem, attrs){
-			let prefix = elem['urlprefix'];
-			 Â Ã‚ Ã‚     let the_url = prefix + "/reports/partials/reportTable/";
-			return the_url;
-		},
-		link: function(scope, elem, attrs){
-			let prepend_item = {};
-			prepend_item["name"] = "experiment";
-			prepend_item["selected"] = true;
-
-			scope.tables_details[attrs.tableid].unshift(prepend_item);
-
-			angular.forEach(scope.tables_details, function(table_value, table_key){
-				for(let i = 0; i < table_value.length; i++){
-					if(table_value[i].name.indexOf("execution_time") > -1 &&
-						table_value[i].name.indexOf("[s]") == -1){
-						table_value[i].name = table_value[i].name + "[s]";
-					}
-				};
-			});
-
-			scope.dattrs = attrs;
-		}
-	};
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/tableItem.js b/beat/web/reports/static/reports/app/directives/tableItem.js
new file mode 100644
index 0000000000000000000000000000000000000000..d602cc8d79883a39bee60227623666ccc4accd21
--- /dev/null
+++ b/beat/web/reports/static/reports/app/directives/tableItem.js
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ *
+ * This file is part of the beat.web module of the BEAT platform.
+ *
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ *
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * 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/.
+ */
+
+/*
+ * tableItem
+ * Desc:
+ * 	displays a table report item
+ */
+angular.module('reportApp')
+.directive("tableItem", ['GroupsService', 'ExperimentsService', function(GroupsService, ExperimentsService){
+	return {
+		scope: {
+			group: '=',
+			content: '=',
+			isViewingCsv: '='
+		},
+		link: function(scope){
+			// aliases
+			scope.fields = scope.content.fields;
+
+			// add 'expName' to the beginning of the fields to show in the table
+			// if it isnt already there
+			if(scope.fields.length === 0 || scope.fields[0] !== 'Experiment'){
+				scope.fields.unshift('Experiment');
+			}
+
+			// get possible table entries
+			scope.tableables = ExperimentsService.tableables || {};
+
+			// gets the field type (int, float, string, nothing)
+			scope.getFieldType = (field) => {
+				if(field === scope.fields[0]){
+					return 'string';
+				}
+
+				let hasFieldObj = Object.values(scope.tableables)
+				.find(o => o[field]);
+				let fVal = hasFieldObj ? hasFieldObj[field] : {};
+				let type;
+				if(fVal.type){
+					type = fVal.type;
+				} else if(Number.isSafeInteger(fVal)){
+					type = 'integer';
+				} else if(Number.isFinite(fVal)){
+					type = 'float';
+				} else if(typeof fVal === 'string'){
+					type = 'string';
+				} else {
+					type = undefined;
+				}
+
+				return type;
+			};
+			// gets the field val for the given exp
+			scope.getFieldVal = (expName, field) => {
+				let fVal = scope.tableables[expName] ?
+					scope.tableables[expName][field] : undefined;
+				let val;
+				if(field === scope.fields[0]){
+					val = scope.group.aliases[expName].length > 0 ? scope.group.aliases[expName] : expName;
+				} else if(!fVal){
+					val = '-';
+				} else {
+					let tmp;
+					if(fVal.value){
+						tmp = fVal.value;
+					} else {
+						tmp = fVal;
+					}
+
+					let type = scope.getFieldType(field);
+					if(type && type.startsWith('float')){
+						val = tmp.toFixed(parseInt(scope.content.precision));
+					} else {
+						val = tmp;
+					}
+				}
+
+				return val;
+			};
+
+			// need to nest actual value in an obj to get angular
+			// to watch it correctly
+			scope.sortField = { val: 'Experiment', isReversed: false };
+			// sort rows (one row per exp)
+			scope.sortFunc = (expName) => {
+				return scope.getFieldType(scope.sortField.val) ?
+					scope.getFieldVal(expName, scope.sortField.val) : expName;
+			};
+			// sets the new sort field and direction
+			scope.setSortField = (field) => {
+				if(scope.sortField.val === field){
+					scope.sortField.isReversed = !scope.sortField.isReversed;
+				} else {
+					scope.sortField.val = field;
+					scope.sortField.isReversed = false;
+				}
+			};
+
+			// a different view of the table
+			scope.getCSV = () => {
+				let fields = scope.fields;
+				let exps = scope.group.experiments
+				// clone arr
+				.map(e => `${e}`)
+				.sort((ea, eb) => (scope.sortField.isReversed ? -1 : 1) * (scope.sortFunc(ea) > scope.sortFunc(eb) ? -1 : 1))
+				;
+
+				let str = '';
+
+				let fieldsStr = fields
+				.map(f => `${f}(${scope.getFieldType(f)})`)
+				.join(',');
+
+				let expsStrs = exps
+				.map(e => fields.map(f => `${scope.getFieldVal(e, f)}`).join(','))
+				.join('\n');
+
+				str = `${fieldsStr}\n${expsStrs}`;
+
+				return str;
+			};
+		},
+		template: `
+<div ng-if='isViewingCsv.val' class='panel-body'>
+	<pre>{{ getCSV() }}</pre>
+</div>
+<div ng-if='!isViewingCsv.val' class='panel-body' style='height: 100%; overflow-x: auto;'>
+	<table class="table table-striped table-hover">
+		<thead>
+			<tr ui-sortable ng-model='fields'>
+				<th ng-repeat='field in fields'>
+					<span
+						ng-if="sortField.val == field"
+						class='glyphicon'
+						ng-class="{
+							'glyphicon-chevron-up': sortField.isReversed,
+							'glyphicon-chevron-down': !sortField.isReversed
+							}"
+						>
+					</span>
+					<a role='button' ng-click='setSortField(field)'>
+						{{ field }} <i>({{ getFieldType(field) }})</i>
+					</a>
+				</th>
+			</tr>
+		</thead>
+		<tbody>
+			<tr ng-repeat="exp in group.experiments | orderBy:sortFunc:sortField.isReversed">
+				<td ng-repeat='field in fields'>
+					{{ getFieldVal(exp, field) }}
+				</td>
+			</tr>
+		</tbody>
+	</table>
+</div>
+`
+	};
+}]);
diff --git a/beat/web/reports/static/reports/app/directives/tablePrecision.js b/beat/web/reports/static/reports/app/directives/tablePrecision.js
deleted file mode 100644
index ac3d582494b8c788fab3e7c015fb8a3715ac3852..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/tablePrecision.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to handle table settings click
-angular.module('reportApp').directive("tableprecision", function($compile){
-	return {
-		link:function(scope, element, attrs){
-			element.bind("change", function(){
-				fixFloatingPoint(scope.$parent.$parent.floating_point_precision.selected);
-			});
-
-			function fixFloatingPoint(selected_precision){
-				if(!scope.$$phase){
-					//$digest or $apply
-					let parent_scope = scope.$parent;
-					let table_details = parent_scope.tables_details;
-
-					for(let i = 0; i < parent_scope.report.orderedcontent.length; i++){
-						let element = document.getElementById(parent_scope.report.orderedcontent[i]);
-
-						$(element).remove();
-
-						let id_content = parent_scope.report.orderedcontent[i];
-						let type = id_content.split("_")[0];
-						parent_scope.$parent.$parent.$broadcast("addSavedElement", id_content, type);
-					}
-				}
-			}
-		}
-	};
-});
-
diff --git a/beat/web/reports/static/reports/app/directives/theColumn.js b/beat/web/reports/static/reports/app/directives/theColumn.js
deleted file mode 100644
index c122954ff3e0d79afe8ecfe34eb6754754ce7f4a..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/directives/theColumn.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-
-//Directive used to set proper item value in selected column
-angular.module('reportApp').directive("thecolumn",['$compile', function ($compile) {
-
-	return {
-		scope: {
-			thecolumn: "@"
-		},
-		restrict: 'A',
-		template: '{{itemValue}}',
-		controller: ['$scope', function ($scope) {
-
-			let the_parent = $scope.$parent.$parent.$parent.$parent;
-			let report_experiments = $scope.$parent.$parent.$parent.$parent.report_experiments;
-			let report_experiments_alias = $scope.$parent.$parent.$parent.$parent.report_experiments_alias;
-			let floating_point_precision = $scope.$parent.$parent.$parent.$parent.floating_point_precision;
-			let report = $scope.$parent.$parent.$parent.report;
-			let experiment_name = $scope.$parent.item;
-
-			if(jQuery.isEmptyObject(report_experiments) || (report_experiments[experiment_name] == undefined)){
-				return;
-			}
-
-			let analyzer_block = report_experiments[experiment_name].analyzer_block;
-			let report_algorithm_parameters_experiment = $scope.$parent.$parent.$parent.$parent.report_algorithm_parameters_experiment;
-
-			if($scope.thecolumn != "experiment"){
-				if(analyzer_block == undefined){
-					angular.forEach(report_experiments, function(value, key){
-						angular.forEach(value.declaration.analyzers, function(value_analyzer, key_analyzer){
-							if(value_analyzer.algorithm == report.analyzer && (key_analyzer in report_experiments[experiment_name].declaration.analyzers)){
-								report_experiments[experiment_name].analyzer_block = key_analyzer;
-								analyzer_block = report_experiments[experiment_name].analyzer_block;
-							}
-						});
-					});
-				}
-
-				if($scope.thecolumn.indexOf("execution_time.") == 0){
-					//execution time information
-					let block_name = $scope.thecolumn.split("execution_time.")[1].split("[s]")[0];
-					if(Object.keys(report_experiments[experiment_name].execution_info).length === 0){
-						$scope.itemValue = "-";
-					}
-					else{
-						if(report_experiments[experiment_name].execution_info[block_name] == undefined){
-							$scope.itemValue = "-";
-						}
-						else{
-							$scope.itemValue = (report_experiments[experiment_name].execution_info[block_name].linear_execution_time).toFixed(floating_point_precision.selected);;
-						}
-					}
-				}
-				else if($scope.thecolumn.indexOf("experiment.") == 0){
-					//total execution time information
-					let block_name = $scope.thecolumn.split("execution_time.")[1];
-					let total_time = 0;
-					if(Object.keys(report_experiments[experiment_name].execution_info).length === 0){
-						total_time = "-";
-					}
-					else{
-						angular.forEach(report_experiments[experiment_name].execution_info, function(value, key){
-							total_time += value.linear_execution_time;
-						});
-					}
-
-					$scope.itemValue = total_time.toFixed(floating_point_precision.selected);
-				}
-				else if($scope.thecolumn.indexOf("algorithm_parameter.") == 0){
-					//total execution time information
-					let block_name = $scope.thecolumn.split("algorithm_parameter.")[1];
-					let algorithm_name = block_name.split("__")[0];
-					let parameter_name = block_name.split("__")[1];
-					if(report_algorithm_parameters_experiment[experiment_name][algorithm_name] != undefined){
-						let value = "";
-						if(report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name] != undefined){
-							for(let i = 0; i < report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name].length; i++){
-								if(i > 0 && i < report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name].length -1 ){
-									value += ",";
-								}
-								value = report_algorithm_parameters_experiment[experiment_name][algorithm_name][parameter_name][i];
-							}
-						}
-						else if(report["all_experiments"][experiment_name]["declaration"]["globals"][algorithm_name][parameter_name] != undefined){
-							//get globals value
-							value = report["all_experiments"][experiment_name]["declaration"]["globals"][algorithm_name][parameter_name];
-						}
-						else{
-							//nothing is defined
-							value = "-";
-						}
-
-						$scope.itemValue = value;
-					}
-					else{
-						$scope.itemValue = "-";
-					}
-				}
-				else{
-					//results information
-					$scope.itemValue = (report_experiments[experiment_name].results[analyzer_block][$scope.thecolumn].value).toFixed(floating_point_precision.selected);
-				}
-			}
-			else{
-				let experiment_alias = report_experiments_alias[experiment_name];
-				$scope.itemValue = experiment_alias;
-			}
-		}]//,
-		//link: function(scope, element, attrs)
-		//{
-		//  console.log(scope);
-		//  let report_experiments_alias = scope.$parent.$parent.$parent.$parent.report_experiments_alias;
-		//  let experiment_name = scope.$parent.item;
-		//  let experiment_alias = report_experiments_alias[experiment_name];
-		//  scope.$watch(scope.$parent.$parent.$parent.$parent.report_experiments_alias,function(newValue, oldValue)
-		//  {
-		//      console.log("changed");
-		//      console.log(report_experiments_alias);
-		//      console.log(oldValue);
-		//      console.log(newValue);
-		//  });
-		//
-		//}
-	};
-}]);
-
diff --git a/beat/web/reports/static/reports/app/directives/view/plotItem.js b/beat/web/reports/static/reports/app/directives/view/plotItem.js
index 6b9d1cc7f948fabffb0fa68ef6b8989316cb25db..d853e89200fee6536ff48166e7bd6566d75ac99a 100644
--- a/beat/web/reports/static/reports/app/directives/view/plotItem.js
+++ b/beat/web/reports/static/reports/app/directives/view/plotItem.js
@@ -26,7 +26,7 @@
  * 	displays a plot report item
  */
 angular.module('reportApp')
-.directive("groupPlotItem", ['ExperimentsService', function(ExperimentsService){
+.directive("groupPlotItem", ['ExperimentsService', 'PlotService', '$timeout', function(ExperimentsService, PlotService, $timeout){
 	return {
 		scope: {
 			group: '=',
@@ -34,12 +34,19 @@ angular.module('reportApp')
 			content: '='
 		},
 		link: function(scope){
+			const group = scope.group;
 			scope.domId = `${scope.group.name}_${scope.id}`;
+			scope.renderDivId = `${scope.domId}-render`;
+
+			$timeout(function() {
+				PlotService.addPlot(scope.group, scope.id, scope.renderDivId);
+			});
 		},
 		template: `
 <div class='panel-body'>
-	<p>{{ id }} content</p>
-	<strong class='text-danger'>Plot items in reports are not implemented yet.</strong>
+	<div class='panel-body'>
+		<div id='{{ renderDivId }}'></div>
+	</div>
 </div>
 `
 	};
diff --git a/beat/web/reports/static/reports/app/directives/view/tableItem.js b/beat/web/reports/static/reports/app/directives/view/tableItem.js
index e34c6cfba152c7b1342ecd51ed11cf31c7d34190..39bd27a0fcc478eeeaf3cc4d7bd8356113eeaac2 100644
--- a/beat/web/reports/static/reports/app/directives/view/tableItem.js
+++ b/beat/web/reports/static/reports/app/directives/view/tableItem.js
@@ -26,7 +26,7 @@
  * 	displays a table report item
  */
 angular.module('reportApp')
-.directive("groupTableItem", ['GroupsService', 'ExperimentsService', function(GroupsService, ExperimentsService){
+.directive("groupTableItem", [function(){
 	return {
 		scope: {
 			group: '=',
@@ -34,123 +34,10 @@ angular.module('reportApp')
 			content: '='
 		},
 		link: function(scope){
-			// aliases
-			scope.fields = scope.content.fields;
 			// ids
 			scope.domId = `${scope.group.name}_${scope.id}`;
 			scope.colSelectorId = `${scope.domId}_columnSelector`;
 
-			// add 'expName' to the beginning of the fields to show in the table
-			// if it isnt already there
-			if(scope.fields.length === 0 || scope.fields[0] !== 'Experiment'){
-				scope.fields.unshift('Experiment');
-			}
-
-			// 1 - 10
-			scope.floatingPointRange = [...(new Array(10)).keys()].map(i => i + 1);
-
-			// get possible table entries
-			scope.tableables = ExperimentsService.tableables || {};
-
-			// gets the field type (int, float, string, nothing)
-			scope.getFieldType = (field) => {
-				if(field === scope.fields[0]){
-					return 'string';
-				}
-
-				let hasFieldObj = Object.values(scope.tableables)
-				.find(o => o[field]);
-				let fVal = hasFieldObj ? hasFieldObj[field] : {};
-				let type;
-				if(fVal.type){
-					type = fVal.type;
-				} else if(Number.isSafeInteger(fVal)){
-					type = 'integer';
-				} else if(Number.isFinite(fVal)){
-					type = 'float';
-				} else if(typeof fVal === 'string'){
-					type = 'string';
-				} else {
-					type = undefined;
-				}
-
-				return type;
-			};
-			// gets the field val for the given exp
-			scope.getFieldVal = (expName, field) => {
-				const shortExpName = expName.split('/').pop();
-				const tableables = scope.tableables[expName] || scope.tableables[shortExpName];
-				const alias = Object.entries(scope.group.aliases)
-				.find(([e, a]) => e == expName || e.endsWith(shortExpName))[1];
-
-				let fVal = tableables ?
-					tableables[field] : undefined;
-				let val;
-				if(field === scope.fields[0]){
-					val = alias.length > 0 ? alias : expName;
-				} else if(!fVal){
-					val = '-';
-				} else {
-					let tmp;
-					if(fVal.value){
-						tmp = fVal.value;
-					} else {
-						tmp = fVal;
-					}
-
-					let type = scope.getFieldType(field);
-					if(type && type.startsWith('float')){
-						val = tmp.toFixed(parseInt(scope.content.precision));
-					} else {
-						val = tmp;
-					}
-				}
-
-				return val;
-			};
-
-			// need to nest actual value in an obj to get angular
-			// to watch it correctly
-			scope.sortField = { val: 'Experiment', isReversed: false };
-			// sort rows (one row per exp)
-			scope.sortFunc = (expName) => {
-				return scope.getFieldType(scope.sortField.val) ?
-					scope.getFieldVal(expName, scope.sortField.val) : expName;
-			};
-			// sets the new sort field and direction
-			scope.setSortField = (field) => {
-				if(scope.sortField.val === field){
-					scope.sortField.isReversed = !scope.sortField.isReversed;
-				} else {
-					scope.sortField.val = field;
-					scope.sortField.isReversed = false;
-				}
-			};
-
-			// a different view of the table
-			scope.getCSV = () => {
-				let fields = scope.fields;
-				let exps = scope.group.experiments
-				// clone arr
-				.map(e => `${e}`)
-				.sort((ea, eb) => (scope.sortField.isReversed ? -1 : 1) * (scope.sortFunc(ea) > scope.sortFunc(eb) ? -1 : 1))
-				;
-
-				let str = '';
-
-				let fieldsStr = fields
-				.map(f => `${f}(${scope.getFieldType(f)})`)
-				.join(',');
-
-				let expsStrs = exps
-				.map(e => fields.map(f => `${scope.getFieldVal(e, f)}`).join(','))
-				.join('\n');
-
-				str = `${fieldsStr}\n${expsStrs}`;
-
-				return str;
-			};
-
 			// toggle val for viewing CSV
 			scope.isViewingCSV = { val: false };
 			scope.toggleViewingCSV = () => {
@@ -168,42 +55,7 @@ angular.module('reportApp')
 			</div>
 		</div>
 	</div>
-	<div class='row' style='margin-top: 5px;'>
-		<div class='col-sm-12'>
-			<div ng-if='isViewingCSV.val'>
-				<pre>{{ getCSV() }}</pre>
-			</div>
-			<div ng-if='!isViewingCSV.val' style='height: 100%; overflow-x: auto;'>
-				<table class="table table-striped table-hover">
-					<thead>
-						<tr>
-							<th ng-repeat='field in fields track by $index'>
-								<span
-									ng-if="sortField.val == field"
-									class='glyphicon'
-									ng-class="{
-										'glyphicon-chevron-up': sortField.isReversed,
-										'glyphicon-chevron-down': !sortField.isReversed
-										}"
-									>
-								</span>
-								<a role='button' ng-click='setSortField(field)'>
-									{{ field }} <i>({{ getFieldType(field) }})</i>
-								</a>
-							</th>
-						</tr>
-					</thead>
-					<tbody>
-						<tr ng-repeat="exp in group.experiments | orderBy:sortFunc:sortField.isReversed">
-							<td ng-repeat='field in fields track by $index'>
-								{{ getFieldVal(exp, field) }}
-							</td>
-						</tr>
-					</tbody>
-				</table>
-			</div>
-		</div>
-	</div>
+	<div table-item group='group' content='content' is-viewing-csv='isViewingCSV'></div>
 </div>
 `
 	};
diff --git a/beat/web/reports/static/reports/app/factories/dataFactory.js b/beat/web/reports/static/reports/app/factories/dataFactory.js
deleted file mode 100644
index aeb3cb67ffdca308685f23cce6ebcc15bddd0275..0000000000000000000000000000000000000000
--- a/beat/web/reports/static/reports/app/factories/dataFactory.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
- */
-//This factory retrieves data from the static files and associates it with the $scope
-angular.module('reportApp').factory('dataFactory', function($http){
-	let getData = function (URL) {
-		//return $http.get(URL + 'itemcontent.json');
-		let obj = {content:null};
-		//return $http.get(URL);
-		$http.get(URL).success(function(data){
-			obj.content = data;
-		});
-
-		return obj;
-	};
-
-	return {
-		getData: getData
-	};
-});
diff --git a/beat/web/reports/static/reports/app/services/experimentsService.js b/beat/web/reports/static/reports/app/services/experimentsService.js
index 7407290d3b47fa22312ddca42c1783d07cb8d209..98a1999f035b702ec1288f8b7ccc7609b49c73f2 100644
--- a/beat/web/reports/static/reports/app/services/experimentsService.js
+++ b/beat/web/reports/static/reports/app/services/experimentsService.js
@@ -26,7 +26,7 @@
  * 	Manages the experiments data, including storing it and generating
  * 	different views of the data
  */
-angular.module('reportApp').factory('ExperimentsService', ['experimentFactory', 'GroupsService', function(experimentFactory, GroupsService){
+angular.module('reportApp').factory('ExperimentsService', ['experimentFactory', 'GroupsService', 'UrlService', function(experimentFactory, GroupsService, UrlService){
 	// holds the raw exp data received from the server
 	const expData = {};
 	// holds the view generated from `getTableablesFromExpName`
@@ -146,12 +146,10 @@ angular.module('reportApp').factory('ExperimentsService', ['experimentFactory',
 	};
 
 	// make sure there are no invalid experiments in the report's groups
+	// invalid -> in a group but not in the report
 	const cleanGroups = (validExpNames) => {
-		console.log(`valid exp names: ${validExpNames}`);
 		GroupsService.groups.forEach(g => {
-			console.log(`Found group: ${g.name}`);
 			g.experiments.forEach(e => {
-				console.log(`Found exp in ${g.name}: ${e}`);
 				// when the user can see the exp, the full name of the exp is available,
 				// including the user who made it, and the toolchain
 				const fullNameValid = validExpNames.includes(e);
@@ -159,30 +157,43 @@ angular.module('reportApp').factory('ExperimentsService', ['experimentFactory',
 				// only the actual name of the exp is available
 				const partNameValid = validExpNames.includes(e.split('/').pop());
 				if(!(fullNameValid || partNameValid)){
-					console.log(`NOT VALID: ${e}`);
+					console.log(`INVALID EXPERIMENT: ${e}`);
 					g.removeExperiment(e);
 				}
 			});
 		});
 	};
 
-	const loadExperiments = (...args) => {
+	// fetch the exp data and process it
+	const loadExperiments = () => {
 		let expFetch;
-		if(args.length === 2){
-			// 2 args -> get exps by report num
-			expFetch = experimentFactory.getAllExperimentResults(...args);
-
-		} else if (args.length === 3){
-			// 3 args -> by report author
-			expFetch = experimentFactory.getAllExperimentResultsForAuthor(...args);
+		const namePath = UrlService.getByNamePath();
+		const numberPath = UrlService.getByNumberPath();
+
+		if(namePath && namePath.length > 0){
+			// get exps by report num
+			const user = namePath.split('/')[1];
+			const name = namePath.split('/')[2];
+			expFetch = experimentFactory.getAllExperimentResultsForAuthor(user, name, '');
+		} else if(numberPath && numberPath.length > 0){
+			//by report author
+			const num = numberPath.split('/')[1];
+			expFetch = experimentFactory.getAllExperimentResults('', num);
 		}
 
 		return expFetch
 		.then((exps) => {
+			// save the exp names as they were given by the server
 			Object.keys(exps).forEach(e => rawExpNames.push(e));
 
+			// process the given exp data
 			Object.entries(exps)
 			.forEach(([expName, exp]) => {
+				// depending on the permissions of the user
+				// when viewing this report
+				// the author/toolchain may be hidden.
+				// to be safe, always process
+				// both the given name (either the full or short name)
 				const shortName = expName.split('/').pop();
 				for(let n of [shortName, expName]){
 					expData[n] = exp;
@@ -201,6 +212,8 @@ angular.module('reportApp').factory('ExperimentsService', ['experimentFactory',
 		});
 	};
 
+	loadExperiments();
+
 	return {
 		experimentNames: rawExpNames,
 		experiments: expData,
diff --git a/beat/web/reports/static/reports/app/services/groupsService.js b/beat/web/reports/static/reports/app/services/groupsService.js
index 5410187409e96477e053f105583180c63c9b5ade..e4f8908f9fd5ec6a2346cd52e280895bef9a803d 100644
--- a/beat/web/reports/static/reports/app/services/groupsService.js
+++ b/beat/web/reports/static/reports/app/services/groupsService.js
@@ -23,8 +23,8 @@
 /*
  * GroupsService
  * Desc:
- * 	The datastore for the reports app, including data fetched from the
- * 	server and cross-component app state
+ * 	The main datastore for the reports app, holding the tree of
+ * 	relationships between groups, experiments, aliases, and report items
  */
 angular.module('reportApp').factory('GroupsService', ['reportFactory', function(reportFactory){
 	let groupsServiceInstance = {};
@@ -47,6 +47,9 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 		/*
 		 * deepFreeze func found http://stackoverflow.com/a/34776962
 		 * changed to use ES6 functionality
+		 * recursively freezes a JS obj so it cant be altered
+		 * just a nice layer of safety to make sure
+		 * the frontend doesnt change if the backend wont change
 		 */
 		const deepFreeze = (o) => {
 			Object.freeze(o);
@@ -114,6 +117,8 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 		}
 
 		// add an exp to this group
+		// optionally sets the analyzer
+		// initializes the new exp's alias to its name
 		addExperiment (expName, analyzer) {
 			let res = this._experimentNames.add(expName);
 			if(this._experimentNames.size === 1 && analyzer && analyzer.length > 0){
@@ -127,7 +132,7 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 			return res;
 		}
 
-		// rm an exp from this group
+		// rm an exp from this group as well as its alias
 		removeExperiment (expName) {
 			let res = this._experimentNames.delete(expName);
 			if(this._experimentNames.size === 0){
@@ -139,7 +144,7 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 			return res;
 		}
 
-		// add an item (table or plot) to this group
+		// add an item (table, plot, or text block) to this group
 		// if the id already exists, it just replaces the old
 		// element with the new one
 		addReportItem (id, content) {
@@ -157,7 +162,7 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 			}
 		}
 
-		// rm an exp from this group
+		// rm a report item from this group
 		removeReportItem (id) {
 			let idx = this._reportItems
 			.indexOf(this._reportItems.find(o => o.id === id));
@@ -185,9 +190,16 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 
 	// serializes groups as an object with form:
 	// {
-	// 	<group name 1>: { experiments: [], reportItems: [], analyzer: '' },
+	// 	<group name 1>: {
+	// 		experiments: [],
+	// 		reportItems: [],
+	// 		analyzer: '',
+	// 		aliases: {},
+	// 		idx: 1
+	// 		},
 	// 	...
 	// }
+	// the 'idx' saves the ordering of the groups
 	groupsServiceInstance.serializeGroups = () => {
 		return groupData
 		.map((g, i) => { return {
@@ -222,6 +234,7 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 	};
 
 	// delete a group
+	// via MUTATING the groupdata
 	groupsServiceInstance.deleteGroup = (name) => {
 		let idx = groupData.indexOf(groupData.find(g => g.name === name));
 		if (idx > -1) {
@@ -231,11 +244,17 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 
 	// load group info from the serialized format:
 	// {
-	// 	<group name 1>: { experiments: [], reportItems: [], analyzer: '' },
+	// 	<group name 1>: {
+	// 		experiments: [],
+	// 		reportItems: [],
+	// 		analyzer: '',
+	// 		aliases: {},
+	// 		idx: 1
+	// 		},
 	// 	...
 	// }
 	groupsServiceInstance.loadGroups = (data) => {
-		// wipe data and load groups
+		// wipe data
 		groupData.splice(0, groupData.length);
 		let safeData = data || {};
 
@@ -251,10 +270,15 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 		})
 		.forEach(([groupName, gData]) => {
 			let g = groupsServiceInstance.createGroup(groupName);
+
+			// default group data to empty
 			let analyzer = gData.analyzer || '';
 			let experiments = gData.experiments || [];
 			let reportItems = gData.reportItems || [];
 			let aliases = gData.aliases || {};
+
+			// save fields to group
+			// by MUTATING the group obj
 			g.analyzer = analyzer;
 			experiments.forEach(n => g.addExperiment(n));
 			reportItems.forEach(i => g.addReportItem(i.id, i.content));
@@ -262,64 +286,5 @@ angular.module('reportApp').factory('GroupsService', ['reportFactory', function(
 		});
 	};
 
-	// save group info via sending to server
-	groupsServiceInstance.saveGroups = () => {
-		console.warn(`not implemented: groupsServiceInstance.saveGroups`);
-	};
-
-	// add experiment to group
-	groupsServiceInstance.addExperimentToGroup = (expName, groupName, analyzer) => {
-		checkForGroup(groupName);
-
-		let group = groupData.find(g => g.name === groupName);
-		return group.addExperiment(expName, analyzer);
-	};
-
-	// rm experiment from group
-	groupsServiceInstance.removeExperimentFromGroup = (expName, groupName) => {
-		checkForGroup(groupName);
-		let group = groupData.find(g => g.name === groupName);
-		return group.removeExperiment(expName);
-	};
-
-	// gets experiments from a group
-	groupsServiceInstance.getGroupExperiments = (groupName) => {
-		checkForGroup(groupName);
-		let group = groupData.find(g => g.name === groupName);
-		return group.experiments;
-	};
-
-	// gets groups for an experiment
-	groupsServiceInstance.getExperimentGroups = (expName) => {
-		return groupData.filter(g => g.experiments.includes(expName)).map(g => g.name);
-	};
-
-	// add report item to group
-	groupsServiceInstance.addReportItemToGroup = (id, content, groupName) => {
-		checkForGroup(groupName);
-
-		let group = groupData.find(g => g.name === groupName);
-		group.addReportItem(id, content);
-	};
-
-	// rm report item id from group
-	groupsServiceInstance.removeReportItemFromGroup = (id, groupName) => {
-		checkForGroup(groupName);
-		let group = groupData.find(g => g.name === groupName);
-		return group.removeReportItem(id);
-	};
-
-	// gets group for a report item's id
-	groupsServiceInstance.getReportItemGroup = (id) => {
-		return groupData.find(g => g.reportItems.find(i => i.id === id));
-	};
-
-	// helper to assert that a group exists
-	function checkForGroup (groupName) {
-		if(!groupData.find(g => g.name === groupName)){
-			throw new Error(`Could not find group "${JSON.stringify(groupName)}"`);
-		}
-	}
-
 	return groupsServiceInstance;
 }]);
diff --git a/beat/web/reports/static/reports/app/services/plotService.js b/beat/web/reports/static/reports/app/services/plotService.js
new file mode 100644
index 0000000000000000000000000000000000000000..95fdbb2f0b9d86d5eb7141a1ab28169aab6c1d30
--- /dev/null
+++ b/beat/web/reports/static/reports/app/services/plotService.js
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ *
+ * This file is part of the beat.web module of the BEAT platform.
+ *
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ *
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * 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/.
+ */
+
+/*
+ * PlotService
+ * Desc:
+ * 	Manages the plots in the report,
+ * 	including:
+ * 	- Adding new/saved
+ * 	- Configuring
+ * 	- Deleting
+ * 	- Rendering
+ */
+angular.module('reportApp').factory('PlotService', ['UrlService', function(UrlService){
+	const ps = {};
+
+	// these are provided by ReportService
+	let plotters;
+	let defaultPlotters;
+	let plotterParameters;
+	let reportNumber;
+
+	// this 'queue' idea is the solution to trying to load plots before we receive all the data
+	// from the server that we need (report info, exp info, etc.).
+	// until the queue is processed (after which, no queue is needed),
+	// plots that want to be rendered are added to a queue to wait.
+	const queue = [];
+	let noQueueNeeded = false;
+
+	// constructs the payload to send to the server
+	const constructPlotInfo = (group, itemId) => {
+		const content = group.reportItems.find(i => i.id === itemId).content;
+
+		// defaults obj, in case we're using defaults
+		const defaults = defaultPlotters.find(p => p.dataformat === content.type);
+		if (!defaults) {
+			console.log(`No default plotter found for content type ${JSON.stringify(content.type)} out of:`);
+			console.log(defaultPlotters);
+		}
+
+		// a plot obj can have different plotters,
+		let plotter = content.savedPlotter || plotters.find(p => p.name === defaults.plotter);
+
+		// which can each have different configurations (plotter parameter instances)
+		let config = content.savedConfig || plotterParameters.find(pp => pp.name === defaults.parameter);
+
+		// sanity check for the plotter & config
+		// the config's "plotter" field should be equal to the plotter's id field
+		if(!(config.plotter === plotter.id)){
+			console.error(`Config plotter val "${config.plotter}" != plotter id "${plotter.id}":`);
+			console.log(config);
+			console.log(plotter);
+		};
+
+		// the data to be sent to the server
+		const requestData = {
+			report_number: reportNumber,
+			// exps in the group
+			experiment: group.experiments,
+			// group's analyzer
+			analyzer: [group.analyzer],
+			// ?
+			output: [content.name],
+			// plotter to use
+			plotter: plotter.name,
+			// config to use
+			parameter: config.name,
+			// string for making the legend in the plot
+			legend: group.experiments.map(e => group.aliases[e]).join('&'),
+			// whether one plot with all the exps' data,
+			// or one plot for each exp
+			merged: content.merged === undefined ? true : content.merged
+		};
+
+		// data to be used in to interact with the plot
+		// users can change the plotter used if theres more than 1 available for that
+		// plot type
+		const possiblePlotters = plotters
+		.filter(p => p.dataformat === content.type)
+		.map(p => p.name)
+		;
+
+		// users can choose which params to use if more than 1 available
+		// for that plotter
+		const possibleParameters = plotterParameters
+		.filter(pp => plotter.id === pp.plotter)
+		.map(pp => pp.name)
+		;
+
+		console.log(`Request data:`);
+		console.log(requestData);
+		console.log(`possible plotters:`);
+		console.log(possiblePlotters);
+		console.log(`possible parameters:`);
+		console.log(possibleParameters);
+
+		const returnStruct = [requestData, possiblePlotters, possibleParameters];
+
+		return returnStruct;
+	};
+
+	// makes the call to the server, via some helper funcs found in the global namespace
+	const fetchRenderUsingStruct = (requestData, containerId) => {
+		// the 'url_prefix' field found throughout the BEAT code is confusing,
+		// but it seems to always be an empty string now
+		const urlPrefix = '';//UrlService.getApiSegment().split('/').filter(s => s.length > 0).join('/');
+
+		// promisify this utils func so we dont have to deal with callback hell
+		return new Promise((resolve, reject) => {
+			beat.experiments.utils.displayPlot(
+				// url_prefix
+				urlPrefix,
+				// element to append render to
+				document.querySelector(`#${containerId}`),
+				// spread out the request data to fill the next 3 spots
+				...requestData,
+				// dont replace inner content
+				false,
+				// callback
+				(...args) => {
+					// resolve promise
+					resolve(...args);
+				}
+			);
+		});
+	};
+
+	// helper func to process a request to plot
+	const processItem = (group, itemId, containerId) => {
+		const reqData = constructPlotInfo(group, itemId);
+		return fetchRenderUsingStruct(reqData, containerId);
+	};
+
+	// used if we arent ready to directly service requests
+	const addItemToQueue = (group, itemId, containerId) => {
+		queue.push([
+			group,
+			itemId,
+			containerId
+		]);
+	};
+
+	// called by ReportService,
+	// or by someone having this report-level info.
+	// processes the queue and sets the plotService state such that we dont need to use
+	// the queue after this
+	ps.processQueue = (rsPlotters, rsDefaultPlotters, rsPlotterParameters, rsReportNumber) => {
+		plotters = rsPlotters;
+		defaultPlotters = rsDefaultPlotters;
+		plotterParameters = rsPlotterParameters;
+		reportNumber = rsReportNumber;
+
+		noQueueNeeded = true;
+		const promises = queue.map(q => processItem(...q));
+		queue.length = 0;
+
+		return promises;
+	};
+
+	// chooses whether to add the plot request to a queue or service directly
+	// args: group, itemId, containerId
+	ps.addPlot = (...args) => noQueueNeeded ? processItem(...args) : addItemToQueue(...args);
+
+	return ps;
+}]);
diff --git a/beat/web/reports/static/reports/app/services/reportService.js b/beat/web/reports/static/reports/app/services/reportService.js
new file mode 100644
index 0000000000000000000000000000000000000000..160963c2a795750fda9b25f6074940297a0bdf6a
--- /dev/null
+++ b/beat/web/reports/static/reports/app/services/reportService.js
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
+ * Contact: beat.support@idiap.ch
+ *
+ * This file is part of the beat.web module of the BEAT platform.
+ *
+ * Commercial License Usage
+ * Licensees holding valid commercial BEAT licenses may use this file in
+ * accordance with the terms contained in a written agreement between you
+ * and Idiap. For further information contact tto@idiap.ch
+ *
+ * Alternatively, this file may be used under the terms of the GNU Affero
+ * Public License version 3 as published by the Free Software and appearing
+ * in the file LICENSE.AGPL included in the packaging of this file.
+ * The BEAT platform is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * 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/.
+ */
+
+/*
+ * ReportService
+ * Desc:
+ * 	Consumes the "report" object from the API and digests it into helper
+ * 	funcs and report-wide info. Basically an adaptor & bootstrap-er.
+ */
+angular.module('reportApp').factory('ReportService', ['GroupsService', 'plotterFactory', 'PlotService', 'reportFactory', 'UrlService', function(GroupsService, plotterFactory, PlotService, reportFactory, UrlService){
+	const rs = {};
+
+	rs.isAnonymous = undefined;
+	rs.isOwner = undefined;
+	rs.status = undefined;
+	rs.number = undefined;
+	rs.user = undefined;
+
+	rs.plotters = [];
+	rs.defaultPlotters = [];
+	rs.plotterParameters = [];
+
+	// processed the report data received from the server,
+	// and bootstraps the state of various services
+	rs.processReport = (report) => {
+		console.log(report);
+
+		// useful info about the app
+		rs.isAnonymous = report.anonymous;
+		rs.isOwner = report.is_owner;
+		rs.status = report.status;
+		rs.number = report.number;
+		rs.user = report.user;
+
+		// start up our GroupsService
+		GroupsService.loadGroups(report.content.groups);
+		// if the report should not change,
+		// add a nice layer of immutability by freezing the Group tree
+		// (see GroupsService for more info)
+		const isEditable = rs.isOwner && rs.status === 'editable' && !rs.isAnonymous;
+		GroupsService.setEditable(isEditable);
+
+		console.log(GroupsService.groups);
+
+		// fetch all our plotter data
+		// these three fetches do not depend on eachother
+		const pPlotters = plotterFactory.getPlotters('')
+		.then(res => {
+			res.data.forEach(p => rs.plotters.push(p));
+		});
+
+		const pDefaults = plotterFactory.getDefaultPlotters('')
+		.then(res => {
+			res.data.forEach(p => rs.defaultPlotters.push(p));
+		});
+
+		const pParams = plotterFactory.getPlotterParameter('')
+		.then(res => {
+			res.data.forEach(p => rs.plotterParameters.push(p));
+		});
+
+		// process the fetched plot info
+		return Promise.all([pPlotters, pDefaults, pParams])
+		.then(() => PlotService.processQueue(rs.plotters, rs.defaultPlotters, rs.plotterParameters, rs.number))
+		;
+	};
+
+	// fetch the report data using either the by-name or by-number scheme,
+	// according to what URLService found
+	rs.fetchReport = () => {
+		const nameSeg = UrlService.getNameSegment();
+		const numSeg = UrlService.getNumberSegment();
+
+		if(nameSeg){
+			// the nameSeg is something like 'report/<user>/<report name>/'
+			let [user, reportId] = nameSeg.split('/').filter(s => s.length > 0).slice(1);
+
+			return reportFactory.getReportInformation(user, reportId, '')
+			.then(res => rs.processReport(res.data));
+		} else if(numSeg) {
+			// the numSeg is something like 'report/<report number>/'
+			let [number] = numSeg.split('/').filter(s => s.length > 0).slice(1);
+
+			return reportFactory.getReportInformationFromNumber(number, '')
+			.then(res => rs.processReport(res.data));
+		} else {
+			console.error('UrlService could not parse the current URL');
+		}
+	};
+
+	rs.fetchReport()
+	.catch(e => console.error(e));
+
+	console.log(rs);
+
+	return rs;
+}]);
diff --git a/beat/web/reports/static/reports/app/services/urlService.js b/beat/web/reports/static/reports/app/services/urlService.js
index 57e1594cc934322fd587ae12409b5b687edb19d2..8350507daf60fdbd46f3201b7d124e6be4a3f210 100644
--- a/beat/web/reports/static/reports/app/services/urlService.js
+++ b/beat/web/reports/static/reports/app/services/urlService.js
@@ -47,7 +47,7 @@ angular.module('reportApp').factory('UrlService', [function(){
 
 		prefix = url.slice(0, idxSplit);
 		// find how many '/' are in path via splitting str on '/'
-		if(path.split('/').length === 2){
+		if(path.split('/').length === 3){
 			// report number
 			reportByNumber = path;
 		} else {
@@ -56,6 +56,12 @@ angular.module('reportApp').factory('UrlService', [function(){
 		}
 	};
 
+	const getPrefix = () => prefix;
+
+	const getApiSegment = () => apiSegment;
+	const getNameSegment = () => reportByName;
+	const getNumberSegment = () => reportByNumber;
+
 	const experimentPath = () => `${prefix}${experimentSegment}`;
 	const blockPath = () => `${prefix}${blockSegment}`;
 	const databasePath = () => `${prefix}${databaseSegment}`;
@@ -66,8 +72,10 @@ angular.module('reportApp').factory('UrlService', [function(){
 	const getDatabaseUrl = (databaseName) => `${databasePath()}${databaseName}/`;
 	const getApiUrl = (apiSubpath) => `${apiPath()}${apiSubpath}`;
 
-	const getCompileRstUrl = () => `${getApiUrl(`${reportByName}rst/`)}`;
+	const getCompileRstUrl = () => `${getApiUrl(`${reportByName || reportByNumber}rst/`)}`;
 	const getRemoveExperimentUrl = () => `${getApiUrl(`${reportByName}remove/`)}`;
+	const getByNamePath = () => reportByName;
+	const getByNumberPath = () => reportByNumber;
 
 	extractUsingCurrentUrl();
 
@@ -76,6 +84,12 @@ angular.module('reportApp').factory('UrlService', [function(){
 		getBlockUrl,
 		getDatabaseUrl,
 		getCompileRstUrl,
-		getRemoveExperimentUrl
+		getRemoveExperimentUrl,
+		getByNamePath,
+		getByNumberPath,
+		getApiSegment,
+		getNameSegment,
+		getNumberSegment,
+		getPrefix
 	};
 }]);
diff --git a/beat/web/reports/static/reports/test/report-spec.js b/beat/web/reports/static/reports/test/report-spec.js
index 57b186fb6962631c2ca0edcb91ad3b615b00658b..744037f10e1b4c2d54d1cc7d42a005c7a9a6be32 100644
--- a/beat/web/reports/static/reports/test/report-spec.js
+++ b/beat/web/reports/static/reports/test/report-spec.js
@@ -1,6 +1,30 @@
 // general tests for the reports app
 describe('reports app', function(){
+	// contain helpers for browser.wait
+	const until = protractor.ExpectedConditions;
+	// just use enter.perform() to send the enter key
+	const enter = browser.actions().sendKeys(protractor.Key.ENTER);
+	// since angular isnt configured correctly for protractor,
+	// dont make protractor wait for angular
 	browser.ignoreSynchronization = true;
+	// just to make sure the window is maximized during these tests.
+	// helps with button clicking & such
+	browser.driver.manage().window().maximize();
+
+	// login to the default user ('user') once before running all these tests
+	beforeAll(function(){
+		browser.get('http://localhost:8000/login/?next=/');
+		//browser.findElement(by.partialLinkText('Sign-in')).click();
+		browser.findElement(by.id('id_username')).sendKeys('user');
+		browser.findElement(by.id('id_password')).sendKeys('user');
+		browser.findElement(by.partialButtonText('Sign-in')).click();
+		return browser.wait(function(){
+			return browser.getCurrentUrl().then(function(url){
+				const rxUserLoggedIn = /events\/user\//;
+				return rxUserLoggedIn.test(url);
+			});
+		});
+	});
 
 	// if there's an error in the web browser's console,
 	// fail the test and print the error
@@ -23,45 +47,26 @@ describe('reports app', function(){
 	// /reports
 	describe('home', function(){
 		beforeEach(function(){
-			browser.get('http://localhost:8000/reports');
+			browser.get('http://localhost:8000/reports/user');
 		});
 
 		it('should load', function(){
-			expect(browser.getTitle()).toEqual('BEAT - Public Reports');
-		});
-
-		it('should have no reports', function(){
-			let noReportsText = browser.findElement(by.className('not-found'));
-			expect(noReportsText.getText()).toBe('No report found');
+			expect(browser.getTitle()).toEqual("BEAT - user's Reports");
 		});
 	});
 
 	// /reports/user
 	describe('home for the test user', function(){
-		// login to the default user ('user') once before running all these tests
-		beforeAll(function(){
-			browser.get('http://localhost:8000/login/?next=/');
-			//browser.findElement(by.partialLinkText('Sign-in')).click();
-			browser.findElement(by.id('id_username')).sendKeys('user');
-			browser.findElement(by.id('id_password')).sendKeys('user');
-			browser.findElement(by.partialButtonText('Sign-in')).click();
-			return browser.wait(function(){
-				return browser.getCurrentUrl().then(function(url){
-					const rxUserLoggedIn = /events\/user\//;
-					return rxUserLoggedIn.test(url);
-				});
-			});
-		});
 
 		// go to user's reports page before each test
-		beforeEach(function(){
+		beforeAll(function(){
 			browser.get('http://localhost:8000/reports/user/');
 		});
 
 		// before adding a report, there shouldn't be any
-		it('should have no reports', function(){
-			let noReportsText = browser.findElement(by.className('not-found'));
-			expect(noReportsText.getText()).toBe('No report found');
+		it('should not have the "user/test" report', function(){
+			let noReportsText = browser.findElement(by.css('.name > a'));
+			expect(noReportsText.getText()).not.toBe('user/test');
 		});
 
 		// create a report
@@ -87,4 +92,432 @@ describe('reports app', function(){
 			expect(browser.getTitle()).toBe('BEAT - Report');
 		});
 	});
+
+	// go to the experiments page and add up to 5 already-ran experiments
+	describe('adding experiments to the "test" report', function(){
+		// go to experiments page
+		beforeAll(function(){
+			browser.get('http://localhost:8000/experiments/user/');
+		});
+
+		it('should show the experiments list page', function(){
+			expect(browser.getTitle()).toEqual("BEAT - user's Experiments");
+		});
+
+		it('should list successfully-ran experiments accessible by user', function(){
+			expect(browser.isElementPresent(by.css('.Done'))).toBeTruthy();
+		});
+
+		it('should add up to the first 5 experiments to the "test" report', function(){
+			let finishedExpTableRows = element.all(by.css('.Done'));
+			let addButton = element(by.css('#add-to-report'));
+			expect(addButton.getAttribute('disabled')).toBe('true');
+
+			let fiveRows = finishedExpTableRows.filter((r, i) => i < 5);
+
+			fiveRows
+			.then(rs => Promise.all(rs.map(r => r.element(by.css('.report-checkbox')).element(by.css('input')).click())))
+			.then(() => {
+				browser.wait(until.elementToBeClickable(addButton), 5000, 'Button still isnt clickable!');
+				return browser.executeScript('arguments[0].click();', addButton.getWebElement());
+			})
+			.then(() => browser.wait(until.presenceOf(element(by.css('.modal'))), 5000, 'Element taking too long to appear in the DOM'))
+			.then(() => element(by.css('.chosen-single')).click())
+			.then(() => element(by.css('.chosen-results')).element(by.css('.active-result')).click())
+			.then(() => {
+				let submitButton = element(by.buttonText('Add'));
+				return submitButton.click();
+			})
+			.then(() => browser.wait(until.presenceOf(element(by.buttonText("View Report"))), 5000))
+			.then(() => {
+				let headerText = element(by.css('.report-results > h5'));
+				expect(headerText.getText()).toContain('Successfully added');
+
+				return browser.get('http://localhost:8000/reports/user/test/');
+			})
+			.then(() => {
+				expect(element.all(by.css('#experiment-list-test > tbody > tr')).count()).toBeGreaterThan(0);
+			})
+			;
+		});
+	});
+
+	// create 2 groups, 'group1' & 'group2'
+	describe('creating groups', function(){
+		let newGroupInput = element(by.css('#createNewGroupInput'));
+
+		it('should create the "group1" group using the enter key', function(){
+			newGroupInput.sendKeys('group1')
+			.then(() => enter.perform())
+			.then(() => browser.wait(until.textToBePresentInElementValue(element(by.css('#createNewGroupInput')), ''), 1000))
+			.then(() => expect(element.all(by.css('#groupsLayout > div')).count()).toBe(1))
+			;
+		});
+
+		it('should create the "group2" group using the "+" button', function(){
+			newGroupInput.sendKeys('group2')
+			.then(() => element(by.css('#space-for-report-items + div button')).click())
+			.then(() => browser.wait(until.textToBePresentInElementValue(element(by.css('#createNewGroupInput')), ''), 1000))
+			.then(() => expect(element.all(by.css('#groupsLayout > div')).count()).toBe(2))
+			;
+		});
+	});
+
+	// make sure the initial report layouts page is correct
+	describe('report page state with <6 experiments & two groups', function(){
+		describe('header block', function(){
+			const header = element(by.css('.col-sm-12 > p.bs-callout.bs-callout-danger'));
+
+			it('has 4 labels', function() {
+				expect(header.all(by.tagName('br')).count()).toBe(4);
+			});
+
+			it('shows the unique report id', function() {
+				expect(header.element(by.css('.fa-arrow-circle-right + a')).getAttribute('href')).toMatch(/\/reports\/[0-9]+/);
+			});
+
+			it('shows the created date', function() {
+				expect(header.element(by.css('.fa-calendar-o + strong')).getText()).toMatch(/.+ago/);
+			});
+
+			it('shows the "last edited" date', function() {
+				expect(header.element(by.css('.fa-calendar-o + strong + br + .fa-calendar-o + strong')).getText()).toMatch(/.+ago/);
+			});
+
+			it('shows that the report is editable', function() {
+				expect(header.element(by.css('.fa-warning + strong')).getText()).toBe('Editable');
+			});
+		});
+
+		describe('documentation panel', function(){
+			it('shows the empty warning', function() {
+				expect(element(by.css('#description-display > div')).getAttribute('class')).toContain('alert-warning');
+			});
+
+			it('has an "Add" button', function() {
+				expect(element(by.css('#btn-edit-doc > i')).getAttribute('class')).toContain('fa-edit');
+			});
+		});
+
+		describe('experiments list panel', function(){
+			describe('inline toolbar', function(){
+				const toolbar = element(by.css('#filters'));
+
+				it('has 3 filters', function(){
+					expect(toolbar.all(by.xpath("./div")).count()).toBe(3);
+				});
+
+				it('has a filter-rows widget', function(){
+					expect(toolbar.element(by.css('#text-filter')).getAttribute('placeholder')).toBe('Filter rows...');
+				});
+
+				it('has an attestation filter', function(){
+					expect(toolbar.element(by.css('#attestation-filter')).all(by.tagName('option')).count()).toBe(3);
+				});
+
+				it('has a privacy filter', function(){
+					expect(toolbar.element(by.css('#privacy-filter')).all(by.tagName('option')).count()).toBe(4);
+				});
+			});
+
+			describe('table', function(){
+				const table = element(by.css('#experiment-list-test'));
+
+				it('has 8 columns', function(){
+					const cols = table.all(by.css('thead th'));
+					expect(cols.count()).toBe(8);
+
+					expect(cols.get(0).getAttribute('class')).toBe('delete');
+					expect(cols.get(1).getAttribute('class')).toBe('attestation');
+					expect(cols.get(2).getAttribute('class')).toBe('privacy');
+					expect(cols.get(3).getAttribute('class')).toBe('status');
+					expect(cols.get(4).getAttribute('class')).toBe('date');
+					expect(cols.get(5).getText()).toBe('Name');
+					expect(cols.get(6).getAttribute('class')).toBe('datasets');
+					expect(cols.get(7).getAttribute('class')).toBe('analyzers');
+				});
+
+				it('has 5 or less rows', function() {
+					const rows = table.all(by.css('tbody > tr'));
+					expect(rows.count()).toBeLessThan(6);
+				});
+
+				it('has a "Remove Selected Experiments" button', function() {
+					const b = element(by.buttonText('Remove Selected Experiments'));
+					expect(b.getAttribute('disabled')).toBeDefined();
+				});
+			});
+		});
+
+		describe('report content block', function(){
+			const groupsLayout = element(by.css('#groupsLayout'));
+
+			describe('group1 container header', function(){
+				const header = element(by.css('#group1-heading'));
+				const children = element.all(by.css('#group1-heading > h4 > *'));
+
+				it('has 5 children: a collapse link, name widget, a button group, and the add items menu', function(){
+					expect(children.count()).toBe(4);
+				});
+
+				describe('collapse link', function(){
+					it('toggles "#collapse-group1"', function(){
+						expect(children.get(0).getAttribute('href')).toContain('#collapse-group1');
+					});
+				});
+
+				describe('group name widget', function(){
+					const widgetEls = children.get(1).all(by.tagName('span'));
+					const label = widgetEls.get(0);
+					const button = widgetEls.get(1);
+
+					it('has value "group1"', function(){
+						expect(label.getText()).toBe('group1');
+					});
+
+					describe('edit button', function(){
+						it('has the pencil glyphicon', function(){
+							expect(button.getAttribute('class')).toBe('glyphicon glyphicon-pencil');
+						});
+
+						it('is clickable', function(){
+							// no direct way to check if clickable...
+							// so make sure that its not disabled & its displayed instead
+							expect(button.getAttribute('disabled')).toBeNull();
+							expect(button.isDisplayed()).toBeTruthy();
+						});
+					});
+
+				});
+
+				describe('button group', function(){
+					const grp = children.get(2);
+					const btnChildren = grp.all(by.className('btn'));
+
+					it('is a btn group', function(){
+						expect(grp.getAttribute('class')).toContain('btn-group');
+					});
+
+					it('is an action buttons', function(){
+						expect(grp.getAttribute('class')).toContain('action-buttons');
+					});
+
+					it('has btn children', function() {
+						const btnChildren = grp.all(by.className('btn'));
+						expect(btnChildren.count()).toBe(2);
+					});
+
+					it('has a button to delete a group with a red "X"', function(){
+						expect(btnChildren.get(0).getAttribute('title')).toBe('Delete Group');
+						expect(btnChildren.get(0).element(by.css('i')).getAttribute('class')).toContain('fa-times');
+					});
+
+					it('has a button to drag & sort the group with a 4-directional arrow', function(){
+						expect(btnChildren.get(1).getAttribute('title')).toBe('Drag to re-order group');
+						expect(btnChildren.get(1).element(by.css('i')).getAttribute('class')).toContain('fa-arrows');
+					});
+				});
+
+				describe('add items menu', function(){
+					const grp = children.get(3);
+					const buttons = grp.all(by.tagName('button'));
+
+					it('has 3 buttons', function(){
+						expect(buttons.count()).toBe(3);
+					});
+
+					it('is all disabled', function(){
+						expect(buttons.filter(b => b.getAttribute('disabled').then(d => d)).count()).toBe(3);
+					});
+
+					it('has a button to add plots', function(){
+						const el = element(by.buttonText('Add Plot'));
+						expect(el).toBeDefined();
+					});
+
+					it('has a button to add tables', function(){
+						const el = element(by.buttonText('Add Table'));
+						expect(el).toBeDefined();
+					});
+
+					it('has a button to add text blocks', function(){
+						const el = element(by.buttonText('Add Text Block'));
+						expect(el).toBeDefined();
+					});
+				});
+			});
+
+			describe('group1 experiments panel', function(){
+				const header = element(by.css('#group1-explist-heading'));
+				const body = element(by.css('#collapse-group1-explist'));
+
+				it('is visible', function(){
+					expect(header.isDisplayed()).toBeTruthy();
+					expect(body.isDisplayed()).toBeTruthy();
+				});
+
+				it('is empty', function(){
+					expect(element.all(by.css('#collapse-group1-explist > .panel-body > *')).count()).toBe(0);
+				});
+
+				describe('header button', function(){
+					const button = element(by.css('#group1_exp_add_dropdown'));
+
+					it('is non-disabled', function(){
+						expect(button.getAttribute('disabled')).toBeNull();
+					});
+
+					it('has the text "Add Experiment"', function(){
+						expect(button.getText()).toBe('Add Experiment');
+					});
+				});
+			});
+
+			describe('group1 content panel', function(){
+				it('doesnt exist', function(){
+					expect(element.all(by.css('#collapse-group1 > *')).count()).toBe(1);
+				});
+			});
+		});
+	});
+
+	describe('group experiments panel management', function(){
+		const g1ExpPanel = element(by.css('#collapse-group1 > .panel-body > .panel'));
+		const addButton = element(by.css('#group1_exp_add_dropdown'));
+
+		// add back the exp to group1
+		afterAll(function(){
+			return addButton.click()
+			.then(() => {
+				// make sure list opened correctly & add an exp
+				const parent = addButton.element(by.xpath('..'));
+				const list = parent.element(by.css('ul'));
+				const lis = list.all(by.css('li'));
+
+				return lis.get(0).element(by.css('a')).click();
+			});
+		});
+
+		it('adds 1 experiment to group1', function(){
+			// open add exp menu
+			addButton.click()
+			.then(() => {
+				// make sure list opened correctly & add an exp
+				const parent = addButton.element(by.xpath('..'));
+				expect(parent.getAttribute('class')).toContain('open');
+				const list = parent.element(by.css('ul'));
+				const lis = list.all(by.css('li'));
+				expect(lis.count()).toBeGreaterThan(0);
+
+				return lis.get(0).element(by.css('a')).click();
+			})
+			.then(() => {
+				// check state of group after adding exp
+				const analyzer = element(by.css('#group1-explist-heading > h4 > i'));
+				expect(analyzer.isDisplayed()).toBeTruthy();
+				expect(analyzer.getInnerHtml()).not.toBe('');
+
+				const expsBody = element(by.css('#collapse-group1-explist > .panel-body'));
+				expect(expsBody.getInnerHtml()).not.toBe('');
+			});
+		});
+
+		describe('group1 experiment table state with 1 experiment', function(){
+			const expsTable = element(by.css('#collapse-group1-explist > .panel-body > table'));
+			const headers = expsTable.all(by.css('thead > tr > th'));
+			const row = expsTable.element(by.css('tbody > tr'));
+
+			describe('column layout', function(){
+				it('has 4 columns', function(){
+					expect(headers.count()).toBe(4);
+				});
+
+				it('has an empty col for rm exp buttons', function() {
+					expect(headers.get(0).getText()).toBe('');
+				});
+
+				it('has experiment names col', function() {
+					expect(headers.get(1).getText()).toBe('Experiment');
+				});
+
+				it('has dbs/protocols col', function() {
+					expect(headers.get(2).getText()).toBe('Databases/Protocols');
+				});
+
+				it('has aliases col', function() {
+					expect(headers.get(3).getText()).toBe('Alias');
+				});
+			});
+
+			describe('row layout', function(){
+				const cells = row.all(by.css('td'));
+
+				it('has 4 entries', function() {
+					expect(cells.count()).toBe(4);
+				});
+
+				it('has a delete button in the first row', function() {
+					expect(cells.get(0).element(by.tagName('span')).getAttribute('class')).toContain('btn-delete');
+				});
+
+				it('has the experiment name and a link to the experiment in the second row', function() {
+					const a = cells.get(1).element(by.css('a'));
+					expect(a.getText()).not.toBe('');
+				});
+
+				it('has a list of formatted dbs & protocols in the third row', function() {
+					const els = cells.get(2).all(by.css('span'));
+					expect(els.count()).toBeGreaterThan(0);
+					expect(els.element(by.css('a')).getText()).toMatch(/\S+@\S+/);
+				});
+
+				it('has an editable alias input in the fourth row', function() {
+					const input = cells.get(3).element(by.css('input'));
+					const pExpName = cells.get(1).element(by.css('a')).getText();
+					pExpName
+					.then(expName => {
+						const lastSeg = expName.split('/').filter(s => s.length > 0).pop();
+						expect(input.getAttribute('value')).toBe(lastSeg);
+						expect(input.getAttribute('disabled')).toBeNull();
+					});
+				});
+
+			});
+		});
+
+		describe('removing 1 experiment', function(){
+			beforeAll(function(){
+				const rmExpButton = element(by.css('#collapse-group1-explist > tbody > .btn-delete'));
+				return rmExpButton.click();
+			});
+
+			it('removes the exp table', function(){
+				expect(g1ExpPanel.getInnerHtml()).toBe('');
+			});
+
+			it('removes the analyzer tag', function() {
+				expect(element.all(by.css('#group1-explist-heading > h4 > *')).count()).toBe(2);
+			});
+
+			it('lets the user add any experiment again', function() {
+				const expRowsCount = element.all(by.css('#experiment-list-test tbody > tr')).count();
+
+				Promise.all([expRowsCount, addOptionsCount, element(by.css('#group1_exp_add_dropdown')).click()])
+				.then(([expCount]) =>
+					Promise.all([
+						expCount,
+						element('#group1_exp_add_dropdown').element(by.xpath('..')).element(by.css('ul')).all(by.css('li')).count()
+					])
+				)
+				.then(([expCount, optsCount]) => {
+					expect(expCount).toBe(optsCount);
+				});
+			});
+		});
+	});
+
+	// should have 1 exp in group
+	describe('adding & removing report items to groups', function(){
+
+	});
 });
diff --git a/beat/web/reports/templates/reports/dialogs/report_remove_item.html b/beat/web/reports/templates/reports/dialogs/report_remove_item.html
deleted file mode 100644
index 4bc2feeabf65b8a9a093798ab2506847894d316e..0000000000000000000000000000000000000000
--- a/beat/web/reports/templates/reports/dialogs/report_remove_item.html
+++ /dev/null
@@ -1,40 +0,0 @@
-{% comment %}
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- * 
- * This file is part of the beat.web module of the BEAT platform.
- * 
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- * 
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- * 
- * 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/.
-{% endcomment %}
-<div id="{{ dialog_id }}" class="report_remove_item" title="Report" style="display:none">
-    <p class="explanation">Remove item from report</p>
-
-
-    <div class="explanation_text">
-        <p class="experiments">
-        Do you wish to delete this item from your report?
-        </p>
-    </div>
-    <div class="warnings" style="display:none">
-        <p class="experiments">Sucessfully removed report item</p>
-        <ul class="experimentslist"></ul>
-    </div>
-
-    <div class="errors" style="display:none">
-        <p class="errors">Some errors occured while attempting to remove the report item:</p>
-        <ul class="errorslist"></ul>
-    </div>
-</div>
diff --git a/beat/web/reports/templates/reports/dialogs/report_saved.html b/beat/web/reports/templates/reports/dialogs/report_saved.html
deleted file mode 100644
index ec97e677216732586740f73eb9822f3b9024185e..0000000000000000000000000000000000000000
--- a/beat/web/reports/templates/reports/dialogs/report_saved.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% comment %}
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- * 
- * This file is part of the beat.web module of the BEAT platform.
- * 
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- * 
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- * 
- * 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/.
-{% endcomment %}
-<div id="{{ dialog_id }}" class="report_remove_experiment" title="Report" style="display:none">
-    <p class="explanation">Report status</p>
-
-
-    <div class="explanation_text">
-        <p>Your report has been successfully saved</p>
-    </div>
-</div>
diff --git a/beat/web/reports/templates/reports/panels/experiment_table.html b/beat/web/reports/templates/reports/panels/experiment_table.html
index 9be394bc81ebf532ced9b3824d68f9c757cd2b4e..401cefc135c5f3ba4a836f73a15a375381fffa35 100644
--- a/beat/web/reports/templates/reports/panels/experiment_table.html
+++ b/beat/web/reports/templates/reports/panels/experiment_table.html
@@ -72,7 +72,7 @@
 				<table id="{{ panel_id }}" class="table table-hover table-condensed object-list experiment-list">
 					<thead>
 						<tr>
-							{% if not report_number and owner and obj.get_status_display == 'Editable' %}
+							{% if not report_number and owner and report_status == 'Editable' %}
 							<th class='delete'></th>
 							{% endif %}
 							<th class="attestation"></th>
@@ -88,7 +88,7 @@
 						{% for obj in objects %}
 						{% with obj.get_status_display as status %}
 						<tr class="{{ status }}">
-							{% if not report_number and owner and obj.get_status_display == 'Editable' %}
+							{% if not report_number and owner and report_status == 'Editable' %}
 							<td class="delete">
 								<input
 								name='ctrl.expNamesToRemove[]'
@@ -159,7 +159,7 @@
 			</div>
 		</div><!-- col -->
 	</div><!-- row -->
-	{% if not report_number and owner and obj.get_status_display == 'Editable' %}
+	{% if not report_number and owner and report_status == 'Editable' %}
 	<div>
 		<button
 			ng-disabled='ctrl.expNamesToRemove.length == 0'
diff --git a/beat/web/reports/templates/reports/panels/viewer.html b/beat/web/reports/templates/reports/panels/viewer.html
index c3a84b884604276df4a0cf4ae52d586b966df73b..262adcd5f55d8bc45c1b02b0235685589e033f9c 100644
--- a/beat/web/reports/templates/reports/panels/viewer.html
+++ b/beat/web/reports/templates/reports/panels/viewer.html
@@ -46,18 +46,22 @@
 				</div>{# collapse #}
 			</div>{# panel #}
 
+			{% if not report_number and owner %}
 			<div class="panel panel-default" ng-if="report.experiments.length != 0" >
 				<div class="panel-heading" role="tab" id="info-heading">
 					<h4 class="panel-title">
 						<a role="button" data-toggle="collapse" data-parent="#info-heading" href="#collapse-info" aria-expanded="true" aria-controls="collapse-info">Experiments List</a>
 					</h4>
 				</div>{# panel-heading #}
+
 				<div id="collapse-info" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="info-heading">
 					<div class="panel-body">
-						{% experiment_table exps owner report_number "experiment-list-test" %}
+						{% experiment_table exps owner report_number "experiment-list-test" status %}
 					</div>{# panel-body #}
 				</div>{# collapse #}
 			</div>{# panel #}
+			{% endif %}
+
 		<div ng-if="report.experiments.length == 0" class="alert alert-warning"{% if not owner or status != 'Editable' %}style="display:none;"{% endif %}>
 			<i class="fa fa-warning fa-lg"></i> You have <strong>not added any experiments</strong> in this report yet. You may add experiments from <a href="{{ URL_PREFIX }}/experiments/{{ object.author.username }}/">any experiment list page</a> to unlock editing features for this report.
 		</div>
diff --git a/beat/web/reports/templates/reports/partials/reportChartDropDown.html b/beat/web/reports/templates/reports/partials/reportChartDropDown.html
deleted file mode 100644
index fa1e939d54acb1ee1cb9879086059829fdd77dbc..0000000000000000000000000000000000000000
--- a/beat/web/reports/templates/reports/partials/reportChartDropDown.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% comment %}
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- * 
- * This file is part of the beat.web module of the BEAT platform.
- * 
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- * 
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- * 
- * 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/.
-{% endcomment %}
-<div>
-<select>
-  <option ng-model="selectedName" ng-repeat="(key, value) in report.plotterparameter" ng-selected="'{$ value.name $}' == '{$ dattrs.selected_template $}'" value="{$ value.name $}">{$ value.name $}</i></p>
-</select>
-</div>
diff --git a/beat/web/reports/templates/reports/partials/reportGeneralInfo.html b/beat/web/reports/templates/reports/partials/reportGeneralInfo.html
deleted file mode 100644
index 23564e2ec0341a4254ee42619454eefdd39c8f4c..0000000000000000000000000000000000000000
--- a/beat/web/reports/templates/reports/partials/reportGeneralInfo.html
+++ /dev/null
@@ -1,70 +0,0 @@
-{% comment %}
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- * 
- * This file is part of the beat.web module of the BEAT platform.
- * 
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- * 
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- * 
- * 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/.
-{% endcomment %}
-<div class="reportInfo view">
-<div class="reportInfoContainer">
-  <div class="span3 report">
-    <div class="reportBody">
-      <div class="row table-responsive">
-        <div class="col-sm-12">
-            <table class="table table-hover table-striped table-condensed">
-                <thead>
-                    <tr>
-                        <th>Experiments</th>
-                        <th ng-if=" report.status == 'editable' && report.number != report_number" class="experiment_alias_title">Experiments Alias</th>
-                    </tr>
-                </thead>
-                <tbody>
-                    <tr class="counter" ng-repeat="(name, information) in report.all_experiments">
-                        <td ng-if=" report.status == 'editable' && report.number != report_number">
-                        <a id='{$ name $}' class='button remove_experiment {$ report.status $}' removeexperiment>
-                            <i class='fa fa-times fa-lg'></i>
-                        </a>
-                        <a ng-if="report.status == 'editable' && report.number != report_number" href="{$ report.url_prefix $}/experiments/{$ name $}/">{$ name $}
-                        </a>
-                        <a ng-if="report.status != 'editable' && report.number != report_number" href="{$ report.url_prefix $}/experiments/{$ name $}/">{$ report_experiments_alias[name] $}
-                        </a>
-                        </td>
-                        <td>
-                        <a ng-if="report.number == report_number || (report.status == 'published' && report.number != report_number)" href="{$ report.url_prefix $}/experiments/{$ name $}/">{$ report_experiments_alias[name] $}
-                        </a>
-                        <div ng-if=" report.status == 'editable' && report.number != report_number" id='{$ name $}' class="experiment_alias">
-                          <a ng-if=" report.status == 'editable' && report.number != report_number" id='button_alias_{$ name $}' class='button edit_alias setalias {$ report.status $}' aliasexperiment>
-                            <i id ='icon_{$ name $}' class='fa fa-lock fa-lg'></i>
-                          </a>
-                          <input id='input_{$ name $}' class="form-control input-sm experiment_alias input-disabled" type="text" name="experiment_alias" value='{$ report_experiments_alias[name] $}' disabled="disabled" style="width:90%;display:inline">
-                        </div>
-                        </td>
-                    </tr>
-                </tbody>
-            </table>
-        </div>
-      </div>
-      <div class="form form-inline">
-        <div ng-if="report.status == 'editable' && report.number != report_number" class="form-group tableprecision {$ dattrs.reportstatus $} {$ dattrs.accessnumber $}" tableprecision>
-          <label for="float-precision">Floating-point Precision: </label>
-          <select id="float-precision" ng-model="floating_point_precision.selected" class="form-control input-sm" ng-options="item for item in floating_point_precision ">Select the floating point precision for your tables in the report</select>
-          <p class="help">Select the floating point precision for your tables in the report.</p>
-        </div>
-      </div>
-    </div>
-  </div>
-</div>
diff --git a/beat/web/reports/templates/reports/partials/reportSingleTable.html b/beat/web/reports/templates/reports/partials/reportSingleTable.html
deleted file mode 100644
index 5d2e92a631a9812484a9ad50f040a1856ba27694..0000000000000000000000000000000000000000
--- a/beat/web/reports/templates/reports/partials/reportSingleTable.html
+++ /dev/null
@@ -1,58 +0,0 @@
-{% comment %}
- * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
- * Contact: beat.support@idiap.ch
- *
- * This file is part of the beat.web module of the BEAT platform.
- *
- * Commercial License Usage
- * Licensees holding valid commercial BEAT licenses may use this file in
- * accordance with the terms contained in a written agreement between you
- * and Idiap. For further information contact tto@idiap.ch
- *
- * Alternatively, this file may be used under the terms of the GNU Affero
- * Public License version 3 as published by the Free Software and appearing
- * in the file LICENSE.AGPL included in the packaging of this file.
- * The BEAT platform is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * 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/.
-{% endcomment %}
-<div id="{$ dattrs.tableid $}">
-
-<div class="row">
-  <div class="col-sm-12">
-    <div class="pull-left action-buttons">
-      <a ng-if="dattrs.reportstatus == 'editable' && dattrs.accessnumber != 'number'" class="btn btn-success btn-sm settings {$ dattrs.reportstatus $} {$ dattrs.accessnumber $}" buttonsettings><i class="fa fa-cog fa-lg"></i> Settings</a>
-      <a ng-if="dattrs.reportstatus == 'editable' && dattrs.accessnumber != 'number'" class="btn btn-danger btn-sm item_delete {$ dattrs.reportstatus $} {$ dattrs.accessnumber $}" buttondeleteitem><i class="fa fa-times fa-lg"></i> Delete</a>
-      <a class="btn btn-primary btn-sm item_export {$ dattrs.reportstatus $} {$ dattrs.accessnumber $}" buttonexportitem><i class="fa fa-download fa-lg"></i> Export Table</a>
-    </div>
-  </div>
-</div>
-
-<div class="row table-responsive">
-  <div class="col-sm-12">
-    <table class="table table-hover table-striped table-condensed report-table">
-      <thead>
-        <tr>
-          <th ng-repeat="(key, value) in tables_details[dattrs.tableid]" ng-if="value.selected" class="tableitemspace" sortth="{$ value.name $}" sorttblid="{$ dattrs.tableid $}" sortdata>
-          <span style="white-space:nowrap">
-          <span ng-if="(sorted_tables | contains:dattrs.tableid)" class="glyphicon sort-icon" ng-show="sorted_experiments_keys_tables_sortkey[dattrs.tableid]==value.name" ng-class="{'glyphicon-chevron-up':sorted_experiments_keys_reverse[dattrs.tableid],'glyphicon-chevron-down':!(sorted_experiments_keys_reverse[dattrs.tableid])}"></span>
-          <span ng-if="!(sorted_tables | contains:dattrs.tableid) && sorted_experiments_keys_reverse[dattrs.tableid]!=undefined" class="glyphicon sort-icon" ng-show="sorted_experiments_keys_tables_sortkey[dattrs.tableid]==value.name" ng-class="{'glyphicon-chevron-up':sorted_experiments_keys_reverse[dattrs.tableid],'glyphicon-chevron-down':!(sorted_experiments_keys_reverse[dattrs.tableid])}"></span>
-          <small style="white-space:nowrap"><span style="color:#D3D3D3" ng-if="!(sorted_tables | contains:dattrs.tableid) || sorted_experiments_keys_reverse[dattrs.tableid]==undefined || sorted_experiments_keys_tables_sortkey[dattrs.tableid]!=value.name" class="glyphicon glyphicon-sort sort-icon" ng-show="sorted_experiments_keys_tables_sortkey[dattrs.tableid]!=value.name"></span></small>
-          {$ value.name $}
-          </span>
-          </th>
-        </tr>
-      </thead>
-      <tbody>
-        <tr ng-if="!(sorted_tables | contains:dattrs.tableid)" ng-repeat="(exp_key, exp_val) in report_experiments" item="exp_key">
-        <tr ng-if="(dattrs.reportstatus == 'editable' || dattrs.reportstatus == 'published')  && sorted_tables | contains:dattrs.tableid" ng-repeat="(exp_key, exp_val) in sorted_experiments_keys_tables[dattrs.tableid]" item="exp_val">
-        <tr ng-if="(dattrs.reportstatus == 'locked') && sorted_tables | contains:dattrs.tableid" ng-repeat="(exp_key, exp_val) in sorted_experiments_keys_tables[dattrs.tableid]" item="exp_val">
-        </tr>
-      </tbody>
-    </table>
-  </div>
-</div>
-</div>
diff --git a/beat/web/reports/templates/reports/report.html b/beat/web/reports/templates/reports/report.html
index 1cc1a514a06543230ed7dc246329487fe457e487..d27e338a141b2d1baa1783b52f789b45e8e62f86 100644
--- a/beat/web/reports/templates/reports/report.html
+++ b/beat/web/reports/templates/reports/report.html
@@ -83,31 +83,22 @@
     <script src="{% fingerprint "reports/app/factories/reportFactory.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/factories/experimentFactory.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/factories/plotterFactory.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/factories/dataFactory.js" %}" type="text/javascript" charset="utf-8"></script>
 
     <!-- services -->
     <script src="{% fingerprint "reports/app/services/groupsService.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/services/experimentsService.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/services/urlService.js" %}" type="text/javascript" charset="utf-8"></script>
+    <script src="{% fingerprint "reports/app/services/reportService.js" %}" type="text/javascript" charset="utf-8"></script>
+    <script src="{% fingerprint "reports/app/services/plotService.js" %}" type="text/javascript" charset="utf-8"></script>
 
     <!-- directives -->
     <!-- old -->
-    <script src="{% fingerprint "reports/app/directives/addReportItem.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/aliasExperiment.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/buttonDeleteItem.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/buttonExportItem.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/buttonSettings.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/item.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/loadedContent.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/directives/lockReport.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/myReportInfo.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/directives/publishReport.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/removeExperiment.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/directives/saveReportItems.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/sortData.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/tableDynamic.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/tablePrecision.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/theColumn.js" %}" type="text/javascript" charset="utf-8"></script>
+
+    <!-- new -->
+    <script src="{% fingerprint "reports/app/directives/tableItem.js" %}" type="text/javascript" charset="utf-8"></script>
 
     <!-- edit view -->
     {% if not report_number and report.get_status_display == 'Editable' and owner %}
@@ -122,7 +113,6 @@
     <script src="{% fingerprint "reports/app/directives/edit/tableFieldSelector.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/directives/edit/tableItem.js" %}" type="text/javascript" charset="utf-8"></script>
     <script src="{% fingerprint "reports/app/directives/edit/textItem.js" %}" type="text/javascript" charset="utf-8"></script>
-    <script src="{% fingerprint "reports/app/directives/edit/viewSerialized.js" %}" type="text/javascript" charset="utf-8"></script>
     {% else %}
     <!-- readonly view -->
     <script src="{% fingerprint "reports/app/directives/view/layout.js" %}" type="text/javascript" charset="utf-8"></script>
@@ -145,10 +135,10 @@
 {% block content %}
 
 {% if author %}
-    <div class="col-sm-12" ng-app="reportApp" ng-controller="reportController" ng-init="init('{{ author }}', '{{report_name}}', '{{ URL_PREFIX }}', '{% fingerprint "reports/app/data/itemcontent.json" %}', '{% fingerprint "reports/app/data/table_itemcontent.json" %}')">
+    <div class="col-sm-12" ng-app="reportApp" ng-controller="reportController" ng-init="init('{{ author }}', '{{report_name}}', '{{ URL_PREFIX }}')">
 {% endif %}
 {% if report_number %}
-    <div class="col-sm-12" ng-app="reportApp" ng-controller="reportController" ng-init="initWithReportNumber('{{report_number}}', '{{ URL_PREFIX }}', '{% fingerprint "reports/app/data/itemcontent.json" %}', '{% fingerprint "reports/app/data/table_itemcontent.json" %}')">
+    <div class="col-sm-12" ng-app="reportApp" ng-controller="reportController" ng-init="initWithReportNumber('{{report_number}}', '{{ URL_PREFIX }}')">
 
 {% endif %}
 {% csrf_token %}
@@ -220,9 +210,6 @@
 {% smart_selector "smart_selector_detail" %}
 {% report_lock "report_lock" %}
 {% report_publish "report_publish" %}
-{% report_remove_experiment "report_remove_experiment" %}
-{% report_remove_item "report_remove_item" %}
-{% report_saved "report_saved" %}
 
 <script type="text/javascript">
 var smart_selector = new beat.ui.SmartSelector('smart_selector');
diff --git a/beat/web/reports/templatetags/report_tags.py b/beat/web/reports/templatetags/report_tags.py
index a42ada0b8aa7e4024726ffdaed0281bf644fbb7a..4fd790f01f2f2d5f8ecb0aa0be58665eda52dc72 100644
--- a/beat/web/reports/templatetags/report_tags.py
+++ b/beat/web/reports/templatetags/report_tags.py
@@ -217,48 +217,13 @@ register.inclusion_tag('reports/dialogs/report_publish.html')(report_publish)
 #--------------------------------------------------
 
 
-def report_remove_experiment(id):
-    return { 'dialog_id': id,
-             'URL_PREFIX': settings.URL_PREFIX
-           }
-
-
-register.inclusion_tag('reports/dialogs/report_remove_experiment.html')(report_remove_experiment)
-
-
-#--------------------------------------------------
-
-
-def report_remove_item(id):
-    return { 'dialog_id': id,
-             'URL_PREFIX': settings.URL_PREFIX
-           }
-
-
-register.inclusion_tag('reports/dialogs/report_remove_item.html')(report_remove_item)
-
-
-#--------------------------------------------------
-
-
-def report_saved(id):
-    return { 'dialog_id': id,
-             'URL_PREFIX': settings.URL_PREFIX
-           }
-
-
-register.inclusion_tag('reports/dialogs/report_saved.html')(report_saved)
-
-
-#--------------------------------------------------
-
-
 @register.inclusion_tag('reports/panels/experiment_table.html', takes_context=True)
-def experiment_table(context, objects, owner, report_number, id):
+def experiment_table(context, objects, owner, report_number, id, status):
     return dict(
             request=context['request'],
             objects=objects,
             owner=owner,
             panel_id=id,
-            report_number=report_number
+            report_number=report_number,
+            report_status=status
             )
diff --git a/buildout.cfg b/buildout.cfg
index e88744cd59492da38ee5ec15a713d80b168003e0..a92f4885ede9fb46960d633b74bcdfc3b5b7b902 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -103,8 +103,8 @@ eggs = ${buildout:eggs}
 
 [node]
 recipe = gp.recipe.node
-npms = bower
-scripts = bower
+npms = bower protractor
+scripts = bower protractor webdriver-manager
 
 [bower]
 recipe = bowerrecipe
diff --git a/doc/admin/installation.rst b/doc/admin/installation.rst
index 87d4598d623cbe9576dd728b7254812db3d04a33..308e4ec86bcb207201fe23f305064d0b25d3d047 100644
--- a/doc/admin/installation.rst
+++ b/doc/admin/installation.rst
@@ -195,19 +195,11 @@ End-to-End Testing
 Setup
 =====
 
-Currently, testing the BEAT web platform with Protractor requires additional setup after successfully setting up the project locally:
-
-- Install Protractor
-
-  .. code:: bash
-
-         ./parts/buildout-node/node-*/bin/npm i -g protractor
-
-- Download/update the webdriver-manager's dependencies (Selenium & more)
+Currently, testing the BEAT web platform with Protractor requires additional setup after successfully setting up the project locally. Download/update the webdriver-manager's dependencies (Selenium & more):
 
   .. code:: bash
 
-   ./parts/buildout-node/node-*/bin/webdriver-manager update
+   ./bin/webdriver-manager update
 
 Running tests with the provided script
 ======================================
@@ -235,7 +227,7 @@ _____________________________
 
   .. code:: bash
 
-     ./parts/buildout-node/node-*/bin/webdriver-manager start
+     ./bin/webdriver-manager start
 
   .. important::
 
@@ -245,7 +237,7 @@ _____________________________
 
   .. code:: bash
 
-     ./parts/buildout-node/node-*/bin/protractor protractor-conf.js
+     ./bin/protractor protractor-conf.js
 
 - If you started your webdriver server as a background process, you can kill all webdriver processes
 
diff --git a/protractor-conf.js b/protractor-conf.js
index 00bf9926fe894fb31d4e4a27af2f3ea9dfb5934e..3a546abfb6d1b50a39b553c3108215fa109987d9 100644
--- a/protractor-conf.js
+++ b/protractor-conf.js
@@ -7,5 +7,8 @@ exports.config = {
 	],
 	allScriptsTimeout: 60000,
 
+	// disable some selenium~browser notification than can steal focus
+	notify: false,
+
 	//resultJsonOutputFile: './protractor-test-results.json'
 }
diff --git a/protractor.sh b/protractor.sh
index 87ee1bc03530f34c944da88a0c0a32b9922960ff..7799234e27a9e3e982ddf783d6a801bd0d0271f3 100755
--- a/protractor.sh
+++ b/protractor.sh
@@ -50,9 +50,9 @@ fi
 # run the web server
 beat_cmd='./bin/django runserver'
 # spin up web manager
-webdriver_cmd='./parts/buildout-node/node-*/bin/webdriver-manager start'
+webdriver_cmd='./bin/webdriver-manager start'
 # run tests
-protractor_cmd='./parts/buildout-node/node-*/bin/protractor ./protractor-conf.js'
+protractor_cmd='./bin/protractor ./protractor-conf.js'
 
 # start bg processes
 echo 'Output from the BEAT web server &' \