← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 17352: PT GIS ER plugin updates.

 

Merge authors:
  Jan Henrik Øverland (janhenrik-overland)
------------------------------------------------------------
revno: 17352 [merge]
committer: Jan Henrik Overland <janhenrik.overland@xxxxxxxxx>
branch nick: dhis2
timestamp: Mon 2014-11-03 16:21:29 +0100
message:
  PT GIS ER plugin updates.
added:
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/plugin.html
modified:
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/app.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/core.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/plugin.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/styles/style.css
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-mapping/scripts/plugin.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-pivot/scripts/core.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-pivot/scripts/plugin.js


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== added file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/plugin.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/plugin.html	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/plugin.html	2014-11-03 15:19:13 +0000
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+	<link rel="stylesheet" type="text/css" href="http://localhost:8080/dhis-web-commons/javascripts/ext/resources/css/ext-plugin-gray.css"; />
+	<script src="http://localhost:8080/dhis-web-commons/javascripts/ext/ext-all.js";></script>
+	<script src="http://localhost:8080/dhis-web-event-reports/scripts/plugin.js";></script>
+
+    <style type="text/css">
+		body {font-family:sans-serif; margin:0 0 0 60px;}
+
+		h1 {font-size:20px; margin:20px 0;}
+
+		#table1, #table2 {margin-bottom:40px;}
+    </style>
+
+	<script>
+		Ext.onReady(function() {
+			var url = 'http://localhost:8080';
+
+			DHIS.getEventReport({
+				url: url,
+				el: 'table1',
+				uid: 'BkV2dY7nCo5'
+			});
+
+			//DHIS.getTable({
+				//url: url,
+				//el: 'table2',
+				//columns: [
+					//{dimension: 'pe', items: [{id: 'LAST_3_MONTHS'}]}
+				//],
+				//rows: [
+					//{dimension: 'ou', items: [{id: 'LEVEL-4'}, {id:'C9uduqDZr9d'}]}
+				//],
+				//hideEmptyRows: true
+			//});
+		});
+	</script>
+</head>
+
+<body>
+    <h1>TABLE BY UID</h1>
+	<div id="table1"></div>
+
+    <h1>TABLE BY CONFIG</h1>
+	<div id="table2"></div>
+</body>
+</html>

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/app.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/app.js	2014-10-30 16:12:52 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/app.js	2014-11-03 15:19:13 +0000
@@ -7283,7 +7283,15 @@
 			}
 		};
 
-        init.optionSetStorage = {};
+
+        // dhis2
+        dhis2.util.namespace('dhis2.er');
+
+        dhis2.er.store = dhis2.er.store || new dhis2.storage.Store({
+            name: 'dhis2',
+            adapters: [dhis2.storage.IndexedDBAdapter, dhis2.storage.DomSessionStorageAdapter, dhis2.storage.InMemoryAdapter],
+            objectStores: ['optionSets']
+        });
 
 		// requests
 		Ext.Ajax.request({
@@ -7332,15 +7340,6 @@
 
                                         init.namePropertyUrl = namePropertyUrl;
 
-                                        // dhis2
-                                        dhis2.util.namespace('dhis2.er');
-
-                                        dhis2.er.store = dhis2.er.store || new dhis2.storage.Store({
-                                            name: 'dhis2',
-                                            adapters: [dhis2.storage.IndexedDBAdapter, dhis2.storage.DomSessionStorageAdapter, dhis2.storage.InMemoryAdapter],
-                                            objectStores: ['optionSets']
-                                        });
-
                                         // calendar
                                         (function() {
                                             var dhis2PeriodUrl = '../dhis-web-commons/javascripts/dhis2/dhis2.period.js',

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/core.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/core.js	2014-10-30 16:12:52 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/core.js	2014-11-03 15:01:20 +0000
@@ -31,7 +31,7 @@
 				dimension: {
 					data: {
 						value: 'data',
-						name: NS.i18n.data,
+						name: NS.i18n.data || 'Data',
 						dimensionName: 'dx',
 						objectName: 'dx',
 						warning: {
@@ -39,19 +39,19 @@
 						}
 					},
 					category: {
-						name: NS.i18n.categories,
+						name: NS.i18n.categories || 'Assigned categories',
 						dimensionName: 'co',
 						objectName: 'co',
 					},
 					indicator: {
 						value: 'indicators',
-						name: NS.i18n.indicators,
+						name: NS.i18n.indicators || 'Indicators',
 						dimensionName: 'dx',
 						objectName: 'in'
 					},
 					dataElement: {
 						value: 'dataElements',
-						name: NS.i18n.data_elements,
+						name: NS.i18n.data_elements || 'Data elements',
 						dimensionName: 'dx',
 						objectName: 'de'
 					},
@@ -63,13 +63,13 @@
 					},
 					dataSet: {
 						value: 'dataSets',
-						name: NS.i18n.data_sets,
+						name: NS.i18n.data_sets || 'Data sets',
 						dimensionName: 'dx',
 						objectName: 'ds'
 					},
 					period: {
 						value: 'period',
-						name: NS.i18n.periods,
+						name: NS.i18n.periods || 'Periods',
 						dimensionName: 'pe',
 						objectName: 'pe'
 					},
@@ -86,7 +86,7 @@
                     },
 					organisationUnit: {
 						value: 'organisationUnits',
-						name: NS.i18n.organisation_units,
+						name: NS.i18n.organisation_units || 'Organisation units',
 						dimensionName: 'ou',
 						objectName: 'ou'
 					},
@@ -1941,13 +1941,13 @@
 
 			web.mask.show = function(component, message) {
 				if (!Ext.isObject(component)) {
-					console.log('web.mask.show: component not an object');
+					console.log('support.gui.mask.show: component not an object');
 					return null;
 				}
 
 				message = message || 'Loading..';
 
-				if (component.mask) {
+				if (component.mask && component.mask.destroy) {
 					component.mask.destroy();
 					component.mask = null;
 				}

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/plugin.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/plugin.js	2014-06-12 11:37:11 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/scripts/plugin.js	2014-11-03 15:19:13 +0000
@@ -5,15 +5,20 @@
 	// ext config
 	Ext.Ajax.method = 'GET';
 
+    Ext.isIE = function() {
+        return /trident/.test(Ext.userAgent);
+    }();
+
 	// namespace
-	PT = {};
-
-	PT.instances = [];
-	PT.i18n = {};
-	PT.isDebug = false;
-	PT.isSessionStorage = ('sessionStorage' in window && window['sessionStorage'] !== null);
-
-	PT.getCore = function(init) {
+	ER = {};
+
+	ER.instances = [];
+	ER.i18n = {};
+	ER.isDebug = false;
+	ER.isSessionStorage = ('sessionStorage' in window && window['sessionStorage'] !== null);
+
+    // core
+	ER.getCore = function(init) {
         var conf = {},
             api = {},
             support = {},
@@ -24,34 +29,30 @@
 		// conf
 		(function() {
 			conf.finals = {
-				url: {
-					path_module: '/dhis-web-pivot/',
-					organisationunitchildren_get: 'getOrganisationUnitChildren.action'
-				},
 				dimension: {
 					data: {
 						value: 'data',
-						name: PT.i18n.data,
+						name: ER.i18n.data || 'Data',
 						dimensionName: 'dx',
 						objectName: 'dx',
 						warning: {
-							filter: '...'//PT.i18n.wm_multiple_filter_ind_de
+							filter: '...'//ER.i18n.wm_multiple_filter_ind_de
 						}
 					},
 					category: {
-						name: PT.i18n.categories,
+						name: ER.i18n.categories || 'Assigned categories',
 						dimensionName: 'co',
 						objectName: 'co',
 					},
 					indicator: {
 						value: 'indicators',
-						name: PT.i18n.indicators,
+						name: ER.i18n.indicators || 'Indicators',
 						dimensionName: 'dx',
 						objectName: 'in'
 					},
 					dataElement: {
 						value: 'dataElements',
-						name: PT.i18n.data_elements,
+						name: ER.i18n.data_elements || 'Data elements',
 						dimensionName: 'dx',
 						objectName: 'de'
 					},
@@ -63,13 +64,13 @@
 					},
 					dataSet: {
 						value: 'dataSets',
-						name: PT.i18n.data_sets,
+						name: ER.i18n.data_sets || 'Data sets',
 						dimensionName: 'dx',
 						objectName: 'ds'
 					},
 					period: {
 						value: 'period',
-						name: PT.i18n.periods,
+						name: ER.i18n.periods || 'Periods',
 						dimensionName: 'pe',
 						objectName: 'pe'
 					},
@@ -77,11 +78,16 @@
 						value: 'periods'
 					},
 					relativePeriod: {
-						value: 'relativePeriods'
+						value: 'relativePeriods',
+						name: ER.i18n.relative_periods
 					},
+                    startEndDate: {
+                        value: 'dates',
+                        name: ER.i18n.start_end_dates
+                    },
 					organisationUnit: {
 						value: 'organisationUnits',
-						name: PT.i18n.organisation_units,
+						name: ER.i18n.organisation_units || 'Organisation units',
 						dimensionName: 'ou',
 						objectName: 'ou'
 					},
@@ -113,42 +119,35 @@
 
 			conf.period = {
 				periodTypes: [
-					{id: 'Daily', name: PT.i18n.daily},
-					{id: 'Weekly', name: PT.i18n.weekly},
-					{id: 'Monthly', name: PT.i18n.monthly},
-					{id: 'BiMonthly', name: PT.i18n.bimonthly},
-					{id: 'Quarterly', name: PT.i18n.quarterly},
-					{id: 'SixMonthly', name: PT.i18n.sixmonthly},
-					{id: 'Yearly', name: PT.i18n.yearly},
-					{id: 'FinancialOct', name: PT.i18n.financial_oct},
-					{id: 'FinancialJuly', name: PT.i18n.financial_july},
-					{id: 'FinancialApril', name: PT.i18n.financial_april}
+					{id: 'Daily', name: ER.i18n.daily},
+					{id: 'Weekly', name: ER.i18n.weekly},
+					{id: 'Monthly', name: ER.i18n.monthly},
+					{id: 'BiMonthly', name: ER.i18n.bimonthly},
+					{id: 'Quarterly', name: ER.i18n.quarterly},
+					{id: 'SixMonthly', name: ER.i18n.sixmonthly},
+					{id: 'Yearly', name: ER.i18n.yearly},
+					{id: 'FinancialOct', name: ER.i18n.financial_oct},
+					{id: 'FinancialJuly', name: ER.i18n.financial_july},
+					{id: 'FinancialApril', name: ER.i18n.financial_april}
 				]
 			};
 
 			conf.layout = {
-				west_width: 424,
-				west_fieldset_width: 416,
-				west_width_padding: 4,
+				west_width: 452,
 				west_fill: 2,
-				west_fill_accordion_indicator: 59,
-				west_fill_accordion_dataelement: 59,
-				west_fill_accordion_dataset: 33,
-				west_fill_accordion_period: 296,
-				west_fill_accordion_organisationunit: 62,
-				west_maxheight_accordion_indicator: 400,
-				west_maxheight_accordion_dataelement: 400,
-				west_maxheight_accordion_dataset: 400,
-				west_maxheight_accordion_period: 513,
-				west_maxheight_accordion_organisationunit: 900,
-				west_maxheight_accordion_group: 340,
-				west_maxheight_accordion_options: 449,
-				west_scrollbarheight_accordion_indicator: 300,
-				west_scrollbarheight_accordion_dataelement: 300,
-				west_scrollbarheight_accordion_dataset: 300,
-				west_scrollbarheight_accordion_period: 450,
-				west_scrollbarheight_accordion_organisationunit: 450,
-				west_scrollbarheight_accordion_group: 300,
+                west_fill_accordion_indicator: 56,
+                west_fill_accordion_dataelement: 59,
+                west_fill_accordion_dataset: 31,
+                west_fill_accordion_period: 307,
+                west_fill_accordion_organisationunit: 58,
+                west_maxheight_accordion_indicator: 450,
+                west_maxheight_accordion_dataset: 350,
+                west_maxheight_accordion_period: 405,
+                west_maxheight_accordion_organisationunit: 500,
+                west_scrollbarheight_accordion_indicator: 300,
+                west_scrollbarheight_accordion_dataset: 250,
+                west_scrollbarheight_accordion_period: 405,
+                west_scrollbarheight_accordion_organisationunit: 350,
 				east_tbar_height: 31,
 				east_gridcolumn_height: 30,
 				form_label_width: 55,
@@ -168,7 +167,7 @@
 				multiselect_fill_reportingrates: 315
 			};
 
-			conf.pivot = {
+			conf.report = {
 				digitGroupSeparator: {
 					'comma': ',',
 					'space': ' '
@@ -184,6 +183,43 @@
 					'large': '13px'
 				}
 			};
+
+            conf.url = {
+                analysisFields: [
+                    '*',
+                    'program[id,name]',
+                    'programStage[id,name]',
+                    'columns[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
+                    'rows[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
+                    'filters[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
+                    '!lastUpdated',
+                    '!href',
+                    '!created',
+                    '!publicAccess',
+                    '!rewindRelativePeriods',
+                    '!userOrganisationUnit',
+                    '!userOrganisationUnitChildren',
+                    '!userOrganisationUnitGrandChildren',
+                    '!externalAccess',
+                    '!access',
+                    '!relativePeriods',
+                    '!columnDimensions',
+                    '!rowDimensions',
+                    '!filterDimensions',
+                    '!user',
+                    '!organisationUnitGroups',
+                    '!itemOrganisationUnitGroups',
+                    '!userGroupAccesses',
+                    '!indicators',
+                    '!dataElements',
+                    '!dataElementOperands',
+                    '!dataElementGroups',
+                    '!dataSets',
+                    '!periods',
+                    '!organisationUnitLevels',
+                    '!organisationUnits'
+                ]
+            };
 		}());
 
 		// api
@@ -206,7 +242,7 @@
 						return;
 					}
 
-					config.id = config.id.replace('.', '-');
+					//config.id = config.id.replace('.', '-');
 
 					return config;
 				}();
@@ -233,21 +269,21 @@
 					if (config.dimension !== conf.finals.dimension.category.objectName) {
 						var records = [];
 
-						if (!Ext.isArray(config.items)) {
-							console.log('Dimension: items is not an array: ' + config);
-							return;
-						}
-
-						for (var i = 0; i < config.items.length; i++) {
-							records.push(api.layout.Record(config.items[i]));
-						}
-
-						config.items = Ext.Array.clean(records);
-
-						if (!config.items.length) {
-							console.log('Dimension: has no valid items: ' + config);
-							return;
-						}
+						//if (!Ext.isArray(config.items)) {
+							//console.log('Dimension: items is not an array: ' + config);
+							//return;
+						//}
+
+						//for (var i = 0; i < config.items.length; i++) {
+							//records.push(api.layout.Record(config.items[i]));
+						//}
+
+						//config.items = Ext.Array.clean(records);
+
+						//if (!config.items.length) {
+							//console.log('Dimension: has no valid items: ' + config);
+							//return;
+						//}
 					}
 
 					return config;
@@ -266,12 +302,22 @@
 
 				// filters: [Dimension]
 
-				// showTotals: boolean (true)
-
-				// showSubTotals: boolean (true)
+				// showRowTotals: boolean (true)
+
+				// showColTotals: boolean (true)
+
+				// showColSubTotals: boolean (true)
+
+				// showRowSubTotals: boolean (true)
+
+                // showDimensionLabels: boolean (false)
 
 				// hideEmptyRows: boolean (false)
 
+                // countType: string ('events') - 'events', 'tracked_entity_instance'
+
+                // aggregationType: string ('default') - 'default', 'count', 'sum'
+
 				// showHierarchy: boolean (false)
 
 				// displayDensity: string ('normal') - 'compact', 'normal', 'comfortable'
@@ -336,19 +382,19 @@
 
 							// Indicators as filter
 							if (layout.filters[i].dimension === dimConf.indicator.objectName) {
-								web.message.alert(PT.i18n.indicators_cannot_be_specified_as_filter || 'Indicators cannot be specified as filter');
+								web.message.alert(ER.i18n.indicators_cannot_be_specified_as_filter || 'Indicators cannot be specified as filter');
 								return;
 							}
 
 							// Categories as filter
 							if (layout.filters[i].dimension === dimConf.category.objectName) {
-								web.message.alert(PT.i18n.categories_cannot_be_specified_as_filter || 'Categories cannot be specified as filter');
+								web.message.alert(ER.i18n.categories_cannot_be_specified_as_filter || 'Categories cannot be specified as filter');
 								return;
 							}
 
 							// Data sets as filter
 							if (layout.filters[i].dimension === dimConf.dataSet.objectName) {
-								web.message.alert(PT.i18n.data_sets_cannot_be_specified_as_filter || 'Data sets cannot be specified as filter');
+								web.message.alert(ER.i18n.data_sets_cannot_be_specified_as_filter || 'Data sets cannot be specified as filter');
 								return;
 							}
 						}
@@ -397,7 +443,7 @@
 
 					// at least one dimension specified as column or row
 					if (!(config.columns || config.rows)) {
-						alert(PT.i18n.at_least_one_dimension_must_be_specified_as_row_or_column);
+						alert(ER.i18n.at_least_one_dimension_must_be_specified_as_row_or_column);
 						return;
 					}
 
@@ -411,10 +457,10 @@
 					}
 
 					// at least one period
-					if (!Ext.Array.contains(objectNames, dimConf.period.objectName)) {
-						alert(PT.i18n.at_least_one_period_must_be_specified_as_column_row_or_filter);
-						return;
-					}
+					//if (!Ext.Array.contains(objectNames, dimConf.period.objectName)) {
+						//alert(ER.i18n.at_least_one_period_must_be_specified_as_column_row_or_filter);
+						//return;
+					//}
 
 					// favorite
 					if (config.id) {
@@ -430,10 +476,25 @@
 					layout.rows = config.rows;
 					layout.filters = config.filters;
 
+                    layout.dataType = Ext.isString(config.dataType) ? config.dataType : 'aggregated_values';
+                    layout.program = config.program;
+                    layout.programStage = config.programStage;
+
+                    // dates
+                    if (config.startDate && config.endDate) {
+                        layout.startDate = config.startDate;
+                        layout.endDate = config.endDate;
+                    }
+
 					// properties
-					layout.showTotals = Ext.isBoolean(config.totals) ? config.totals : (Ext.isBoolean(config.showTotals) ? config.showTotals : true);
-					layout.showSubTotals = Ext.isBoolean(config.subtotals) ? config.subtotals : (Ext.isBoolean(config.showSubTotals) ? config.showSubTotals : true);
+					layout.showColTotals = Ext.isBoolean(config.colTotals) ? config.colTotals : (Ext.isBoolean(config.showColTotals) ? config.showColTotals : true);
+					layout.showRowTotals = Ext.isBoolean(config.rowTotals) ? config.rowTotals : (Ext.isBoolean(config.showRowTotals) ? config.showRowTotals : true);
+					layout.showColSubTotals = Ext.isBoolean(config.colSubTotals) ? config.colSubTotals : (Ext.isBoolean(config.showColSubTotals) ? config.showColSubTotals : true);
+					layout.showRowSubTotals = Ext.isBoolean(config.rowSubTotals) ? config.rowSubTotals : (Ext.isBoolean(config.showRowSubTotals) ? config.showRowSubTotals : true);
+					layout.showDimensionLabels = Ext.isBoolean(config.showDimensionLabels) ? config.showDimensionLabels : (Ext.isBoolean(config.showDimensionLabels) ? config.showDimensionLabels : true);
 					layout.hideEmptyRows = Ext.isBoolean(config.hideEmptyRows) ? config.hideEmptyRows : false;
+					layout.countType = Ext.isString(config.countType) && !Ext.isEmpty(config.countType) ? config.countType : 'events';
+                    layout.aggregationType = Ext.isString(config.aggregationType) ? config.aggregationType : 'default';
 
 					layout.showHierarchy = Ext.isBoolean(config.showHierarchy) ? config.showHierarchy : false;
 
@@ -444,7 +505,7 @@
 
 					layout.parentGraphMap = Ext.isObject(config.parentGraphMap) ? config.parentGraphMap : null;
 
-					layout.sorting = Ext.isObject(config.sorting) && Ext.isString(config.sorting.id) && Ext.isString(config.sorting.direction) ? config.sorting : null;
+					layout.sorting = Ext.isObject(config.sorting) && Ext.isDefined(config.sorting.id) && Ext.isString(config.sorting.direction) ? config.sorting : null;
 
 					layout.reportingPeriod = Ext.isObject(config.reportParams) && Ext.isBoolean(config.reportParams.paramReportingPeriod) ? config.reportParams.paramReportingPeriod : (Ext.isBoolean(config.reportingPeriod) ? config.reportingPeriod : false);
 					layout.organisationUnit =  Ext.isObject(config.reportParams) && Ext.isBoolean(config.reportParams.paramOrganisationUnit) ? config.reportParams.paramOrganisationUnit : (Ext.isBoolean(config.organisationUnit) ? config.organisationUnit : false);
@@ -555,18 +616,24 @@
 			};
 
 			support.prototype.array.sort = function(array, direction, key) {
-				// accepts [number], [string], [{key: number}], [{key: string}]
+				// supports [number], [string], [{key: number}], [{key: string}], [[string]], [[number]]
 
 				if (!support.prototype.array.getLength(array)) {
 					return;
 				}
 
-				key = key || 'name';
+				key = !!key || Ext.isNumber(key) ? key : 'name';
 
 				array.sort( function(a, b) {
 
 					// if object, get the property values
-					if (Ext.isObject(a) && Ext.isObject(b) && key) {
+					if (Ext.isObject(a) && Ext.isObject(b)) {
+						a = a[key];
+						b = b[key];
+					}
+
+					// if array, get from the right index
+					if (Ext.isArray(a) && Ext.isArray(b)) {
 						a = a[key];
 						b = b[key];
 					}
@@ -595,7 +662,100 @@
 				return array;
 			};
 
-				// object
+            support.prototype.array.uniqueByProperty = function(array, property) {
+                var names = [],
+                    uniqueItems = [];
+
+                for (var i = 0, item; i < array.length; i++) {
+                    item = array[i];
+
+                    if (!Ext.Array.contains(names, item[property])) {
+                        uniqueItems.push(item);
+                        names.push(item[property]);
+                    }
+                }
+
+                return uniqueItems;
+            };
+
+            support.prototype.array.getNameById = function(array, value, idProperty, nameProperty) {
+                if (!(Ext.isArray(array) && value)) {
+                    return;
+                }
+
+                idProperty = idProperty || 'id';
+                nameProperty = nameProperty || 'name';
+
+                for (var i = 0; i < array.length; i++) {
+                    if (array[i][idProperty] === value) {
+                        return array[i][nameProperty];
+                    }
+                }
+
+                return;
+            };
+
+            support.prototype.array.cleanFalsy = function(array) {
+                if (!Ext.isArray(array)) {
+                    return [];
+                }
+
+                if (!array.length) {
+                    return array;
+                }
+
+                for (var i = 0; i < array.length; i++) {
+                    array[i] = array[i] || null;
+                }
+
+                var a = Ext.clean(array);
+                array = null;
+
+                return a;
+            };
+
+            support.prototype.array.pluckIf = function(array, pluckProperty, valueProperty, value, type) {
+                var a = [];
+
+                if (!(Ext.isArray(array) && array.length)) {
+                    return a;
+                }
+
+                pluckProperty = pluckProperty || 'name';
+                valueProperty = valueProperty || pluckProperty;
+
+                for (var i = 0; i < array.length; i++) {
+                    if (Ext.isDefined(type) && typeof array[i][valueProperty] === type) {
+                        a.push(array[i][pluckProperty]);
+                    }
+                    else if (Ext.isDefined(value) && array[i][valueProperty] === value) {
+                        a.push(array[i][pluckProperty]);
+                    }
+                }
+
+                return a;
+            };
+
+            support.prototype.array.getObjectMap = function(array, idProperty, nameProperty, namePrefix) {
+                if (!(Ext.isArray(array) && array.length)) {
+                    return {};
+                }
+
+                var o = {};
+                idProperty = idProperty || 'id';
+                nameProperty = nameProperty || 'name';
+                namePrefix = namePrefix || '';
+
+                for (var i = 0, obj; i < array.length; i++) {
+                    obj = array[i];
+
+                    o[namePrefix + obj[idProperty]] = obj[nameProperty];
+                }
+
+                return o;
+            };
+
+                // object
 			support.prototype.object = {};
 
 			support.prototype.object.getLength = function(object, suppressWarning) {
@@ -672,7 +832,7 @@
 					return number;
 				}
 
-				return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, conf.pivot.digitGroupSeparator[separator]);
+				return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, conf.report.digitGroupSeparator[separator]);
 			};
 
 			// color
@@ -798,15 +958,24 @@
 					name = '';
 
 				if (service.layout.isHierarchy(layout, response, id)) {
-					var a = Ext.Array.clean(metaData.ouHierarchy[id].split('/'));
+					var a = metaData.names[id].split('/');
+
+                    if (a.length === 1) {
+                        return a[0];
+                    }
+
 					a.shift();
 
-					for (var i = 0; i < a.length; i++) {
-						name += (isHtml ? '<span class="text-weak">' : '') + metaData.names[a[i]] + (isHtml ? '</span>' : '') + ' / ';
+					for (var i = 0, isLast; i < a.length; i++) {
+						isLast = !!(i === a.length - 1);
+
+						name += (isHtml && !isLast ? '<span class="text-weak">' : '') + a[i] + (isHtml && !isLast ? '</span>' : '') + (!isLast ? ' / ' : '');
 					}
+
+					return name;
 				}
 
-				name += metaData.names[id];
+				name += metaData.optionNames[id] || metaData.names[id];
 
 				return name;
 			};
@@ -857,16 +1026,18 @@
 					dimensionNameIdsMap: {},
 
 						// for param string
-					dimensionNameSortedIdsMap: {},
+					dimensionNameSortedIdsMap: {}
 
 					// sort table by column
-					sortableIdObjects: []
+					//sortableIdObjects: []
 				};
 
 				Ext.applyIf(xLayout, layout);
 
 				// columns, rows, filters
 				if (layout.columns) {
+                    //layout.columns = support.prototype.array.uniqueByProperty(layout.columns, 'dimension');
+
 					for (var i = 0, dim, items, xDim; i < layout.columns.length; i++) {
 						dim = layout.columns[i];
 						items = dim.items;
@@ -874,11 +1045,13 @@
 
 						xDim.dimension = dim.dimension;
 						xDim.objectName = dim.dimension;
-						xDim.dimensionName = dimConf.objectNameMap[dim.dimension].dimensionName;
+						xDim.dimensionName = dimConf.objectNameMap.hasOwnProperty(dim.dimension) ? dimConf.objectNameMap[dim.dimension].dimensionName || dim.dimension : dim.dimension;
+
+						xDim.items = [];
+						xDim.ids = [];
 
 						if (items) {
 							xDim.items = items;
-							xDim.ids = [];
 
 							for (var j = 0; j < items.length; j++) {
 								xDim.ids.push(items[j].id);
@@ -892,7 +1065,7 @@
 
 						xLayout.axisDimensions.push(xDim);
 						xLayout.axisObjectNames.push(xDim.objectName);
-						xLayout.axisDimensionNames.push(dimConf.objectNameMap[xDim.objectName].dimensionName);
+						xLayout.axisDimensionNames.push(dimConf.objectNameMap.hasOwnProperty(xDim.objectName) ? dimConf.objectNameMap[xDim.objectName].dimensionName || xDim.objectName : xDim.objectName);
 
 						xLayout.objectNameDimensionsMap[xDim.objectName] = xDim;
 						xLayout.objectNameItemsMap[xDim.objectName] = xDim.items;
@@ -901,6 +1074,8 @@
 				}
 
 				if (layout.rows) {
+                    //layout.rows = support.prototype.array.uniqueByProperty(layout.rows, 'dimension');
+
 					for (var i = 0, dim, items, xDim; i < layout.rows.length; i++) {
 						dim = Ext.clone(layout.rows[i]);
 						items = dim.items;
@@ -908,11 +1083,13 @@
 
 						xDim.dimension = dim.dimension;
 						xDim.objectName = dim.dimension;
-						xDim.dimensionName = dimConf.objectNameMap[dim.dimension].dimensionName;
+						xDim.dimensionName = dimConf.objectNameMap.hasOwnProperty(dim.dimension) ? dimConf.objectNameMap[dim.dimension].dimensionName || dim.dimension : dim.dimension;
+
+						xDim.items = [];
+						xDim.ids = [];
 
 						if (items) {
 							xDim.items = items;
-							xDim.ids = [];
 
 							for (var j = 0; j < items.length; j++) {
 								xDim.ids.push(items[j].id);
@@ -926,7 +1103,7 @@
 
 						xLayout.axisDimensions.push(xDim);
 						xLayout.axisObjectNames.push(xDim.objectName);
-						xLayout.axisDimensionNames.push(dimConf.objectNameMap[xDim.objectName].dimensionName);
+						xLayout.axisDimensionNames.push(dimConf.objectNameMap.hasOwnProperty(xDim.objectName) ? dimConf.objectNameMap[xDim.objectName].dimensionName || xDim.objectName : xDim.objectName);
 
 						xLayout.objectNameDimensionsMap[xDim.objectName] = xDim;
 						xLayout.objectNameItemsMap[xDim.objectName] = xDim.items;
@@ -935,6 +1112,8 @@
 				}
 
 				if (layout.filters) {
+                    //layout.filters = support.prototype.array.uniqueByProperty(layout.filters, 'dimension');
+
 					for (var i = 0, dim, items, xDim; i < layout.filters.length; i++) {
 						dim = layout.filters[i];
 						items = dim.items;
@@ -942,11 +1121,13 @@
 
 						xDim.dimension = dim.dimension;
 						xDim.objectName = dim.dimension;
-						xDim.dimensionName = dimConf.objectNameMap[dim.dimension].dimensionName;
+						xDim.dimensionName = dimConf.objectNameMap.hasOwnProperty(dim.dimension) ? dimConf.objectNameMap[dim.dimension].dimensionName || dim.dimension : dim.dimension;
+
+						xDim.items = [];
+						xDim.ids = [];
 
 						if (items) {
 							xDim.items = items;
-							xDim.ids = [];
 
 							for (var j = 0; j < items.length; j++) {
 								xDim.ids.push(items[j].id);
@@ -957,7 +1138,7 @@
 
 						xLayout.filterDimensions.push(xDim);
 						xLayout.filterObjectNames.push(xDim.objectName);
-						xLayout.filterDimensionNames.push(dimConf.objectNameMap[xDim.objectName].dimensionName);
+						xLayout.filterDimensionNames.push(dimConf.objectNameMap.hasOwnProperty(xDim.objectName) ? dimConf.objectNameMap[xDim.objectName].dimensionName || xDim.objectName : xDim.objectName);
 
 						xLayout.objectNameDimensionsMap[xDim.objectName] = xDim;
 						xLayout.objectNameItemsMap[xDim.objectName] = xDim.items;
@@ -968,7 +1149,7 @@
 				// legend set
 				xLayout.legendSet = layout.legendSet ? init.idLegendSetMap[layout.legendSet.id] : null;
 
-				if (layout.legendSet) {
+				if (layout.legendSet && layout.legendSet.mapLegends) {
 					xLayout.legendSet = init.idLegendSetMap[layout.legendSet.id];
 					support.prototype.array.sort(xLayout.legendSet.mapLegends, 'ASC', 'startValue');
 				}
@@ -1020,10 +1201,11 @@
 				return xLayout;
 			};
 
-			service.layout.getSyncronizedXLayout = function(xLayout, response) {
+			service.layout.getSyncronizedXLayout = function(layout, xLayout, xResponse) {
 				var removeDimensionFromXLayout,
 					getHeaderNames,
-					dimensions = Ext.Array.clean([].concat(xLayout.columns || [], xLayout.rows || [], xLayout.filters || []));
+					dimensions = Ext.Array.clean([].concat(xLayout.columns || [], xLayout.rows || [], xLayout.filters || [])),
+                    originalDimensions = Ext.Array.clean([].concat(layout.columns || [], layout.rows || [], layout.filters || []));
 
 				removeDimensionFromXLayout = function(objectName) {
 					var getUpdatedAxis;
@@ -1059,168 +1241,80 @@
 				getHeaderNames = function() {
 					var headerNames = [];
 
-					for (var i = 0; i < response.headers.length; i++) {
-						headerNames.push(response.headers[i].name);
+					for (var i = 0; i < xResponse.headers.length; i++) {
+						headerNames.push(xResponse.headers[i].name);
 					}
 
 					return headerNames;
 				};
 
-				return function() {
-					var headerNames = getHeaderNames(),
-						xOuDimension = xLayout.objectNameDimensionsMap[dimConf.organisationUnit.objectName],
-						isUserOrgunit = xOuDimension && Ext.Array.contains(xOuDimension.ids, 'USER_ORGUNIT'),
-						isUserOrgunitChildren = xOuDimension && Ext.Array.contains(xOuDimension.ids, 'USER_ORGUNIT_CHILDREN'),
-						isUserOrgunitGrandChildren = xOuDimension && Ext.Array.contains(xOuDimension.ids, 'USER_ORGUNIT_GRANDCHILDREN'),
-						isLevel = function() {
-							if (xOuDimension && Ext.isArray(xOuDimension.ids)) {
-								for (var i = 0; i < xOuDimension.ids.length; i++) {
-									if (xOuDimension.ids[i].substr(0,5) === 'LEVEL') {
-										return true;
-									}
-								}
-							}
-
-							return false;
-						}(),
-						isGroup = function() {
-							if (xOuDimension && Ext.isArray(xOuDimension.ids)) {
-								for (var i = 0; i < xOuDimension.ids.length; i++) {
-									if (xOuDimension.ids[i].substr(0,8) === 'OU_GROUP') {
-										return true;
-									}
-								}
-							}
-
-							return false;
-						}(),
-						co = dimConf.category.objectName,
-						ou = dimConf.organisationUnit.objectName,
-						layout;
-
-					// Set items from init/metaData/xLayout
-					for (var i = 0, dim, metaDataDim, items; i < dimensions.length; i++) {
-						dim = dimensions[i];
-						dim.items = [];
-						metaDataDim = response.metaData[dim.objectName];
-
-						// If ou and children
-						if (dim.dimensionName === ou) {
-							if (isUserOrgunit || isUserOrgunitChildren || isUserOrgunitGrandChildren) {
-								var userOu,
-									userOuc,
-									userOugc;
-
-								if (isUserOrgunit) {
-									userOu = [{
-										id: init.user.ou,
-										name: service.layout.getItemName(xLayout, response, init.user.ou, false)
-									}];
-								}
-								if (isUserOrgunitChildren) {
-									userOuc = [];
-
-									for (var j = 0; j < init.user.ouc.length; j++) {
-										userOuc.push({
-											id: init.user.ouc[j],
-											name: service.layout.getItemName(xLayout, response, init.user.ouc[j], false)
-										});
-									}
-
-									support.prototype.array.sort(userOuc);
-								}
-								if (isUserOrgunitGrandChildren) {
-									var userOuOuc = [].concat(init.user.ou, init.user.ouc),
-										responseOu = response.metaData[ou];
-
-									userOugc = [];
-
-									for (var j = 0, id; j < responseOu.length; j++) {
-										id = responseOu[j];
-
-										if (!Ext.Array.contains(userOuOuc, id)) {
-											userOugc.push({
-												id: id,
-												name: service.layout.getItemName(xLayout, response, id, false)
-											});
-										}
-									}
-
-									support.prototype.array.sort(userOugc);
-								}
-
-								dim.items = [].concat(userOu || [], userOuc || [], userOugc || []);
-							}
-							else if (isLevel || isGroup) {
-								for (var j = 0, responseOu = response.metaData[ou], id; j < responseOu.length; j++) {
-									id = responseOu[j];
-
-									dim.items.push({
-										id: id,
-										name: service.layout.getItemName(xLayout, response, id, false)
-									});
-								}
-
-								support.prototype.array.sort(dim.items);
-							}
-							else {
-								dim.items = Ext.clone(xLayout.dimensionNameItemsMap[dim.dimensionName]);
-							}
-						}
-						else {
-							// Items: get ids from metadata -> items
-							if (Ext.isArray(metaDataDim) && metaDataDim.length) {
-								var ids = Ext.clone(response.metaData[dim.dimensionName]);
-								for (var j = 0; j < ids.length; j++) {
-									dim.items.push({
-										id: ids[j],
-										name: response.metaData.names[ids[j]]
-									});
-								}
-							}
-							// Items: get items from xLayout
-							else {
-								dim.items = Ext.clone(xLayout.objectNameItemsMap[dim.objectName]);
-							}
-						}
-					}
-
-					// Remove dimensions from layout that do not exist in response
-					for (var i = 0, dimensionName; i < xLayout.axisDimensionNames.length; i++) {
-						dimensionName = xLayout.axisDimensionNames[i];
-						if (!Ext.Array.contains(headerNames, dimensionName)) {
-							removeDimensionFromXLayout(dimensionName);
-						}
-					}
-
-					// Re-layout
-					layout = api.layout.Layout(xLayout);
-
-					if (layout) {
-						dimensions = Ext.Array.clean([].concat(layout.columns || [], layout.rows || [], layout.filters || []));
-
-						for (var i = 0, idNameMap = response.metaData.names, dimItems; i < dimensions.length; i++) {
-							dimItems = dimensions[i].items;
-
-							if (Ext.isArray(dimItems) && dimItems.length) {
-								for (var j = 0, item; j < dimItems.length; j++) {
-									item = dimItems[j];
-
-									if (Ext.isObject(item) && Ext.isString(idNameMap[item.id]) && !Ext.isString(item.name)) {
-										item.name = idNameMap[item.id] || '';
-									}
-								}
-							}
-						}
-
-						return service.layout.getExtendedLayout(layout);
-					}
-
-					return null;
-				}();
+                // items
+                for (var i = 0, dim, header; i < dimensions.length; i++) {
+                    dim = dimensions[i];
+                    dim.items = [];
+                    header = xResponse.nameHeaderMap[dim.dimension];
+                    optionMap = {};
+
+                    if (header) {
+                        for (var j = 0, id; j < header.ids.length; j++) {
+                            id = header.ids[j];
+// TODO, items used?
+                            dim.items.push({
+                                id: id,
+                                name: xResponse.metaData.optionNames[id] || xResponse.metaData.names[id] || id
+                            });
+                        }
+                    }
+                }
+
+                // restore order for options
+                for (var i = 0, orgDim; i < originalDimensions.length; i++) {
+                    orgDim = originalDimensions[i];
+
+                    // if sorting and row dim, dont restore order
+                    if (layout.sorting && Ext.Array.contains(xLayout.rowDimensionNames, orgDim.dimension)) {
+                        continue;
+                    }
+
+                    if (Ext.isString(orgDim.filter)) {
+                        var a = orgDim.filter.split(':');
+
+                        if (a[0] === 'IN' && a.length > 1 && Ext.isString(a[1])) {
+                            var options = a[1].split(';');
+
+                            for (var j = 0, dim, items; j < dimensions.length; j++) {
+                                dim = dimensions[j];
+
+                                if (dim.dimension === orgDim.dimension && dim.items && dim.items.length) {
+                                    items = [];
+
+                                    for (var k = 0, option; k < options.length; k++) {
+                                        option = options[k];
+
+                                        for (var l = 0, item; l < dim.items.length; l++) {
+                                            item = dim.items[l];
+                                            if (item.id === option || item.id === (dim.dimension + option)) {
+                                                items.push(item);
+                                            }
+                                        }
+                                    }
+
+                                    dim.items = items;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // Re-layout
+                layout = api.layout.Layout(xLayout);
+
+                if (layout) {
+                    return service.layout.getExtendedLayout(layout);
+                }
 			};
 
-			service.layout.getExtendedAxis = function(xLayout, xResponse, type) {
+			service.layout.getExtendedAxis = function(xLayout, type) {
 				var dimensionNames,
 					spanType,
 					aDimensions = [],
@@ -1265,7 +1359,7 @@
 					var a = [];
 
 					for (var i = 0; i < aDimensions.length; i++) {
-						a.push(xResponse.nameHeaderMap[aDimensions[i].dimensionName].ids);
+						a.push(xLayout.dimensionNameIdsMap[aDimensions[i].dimensionName]);
 					}
 
 					return a;
@@ -1397,10 +1491,13 @@
 				}
 
 				// add span and children
-				for (var i = 0; i < aaAllFloorObjects.length; i++) {
+				for (var i = 0, aAboveFloorObjects, doorIds, uniqueDoorIds; i < aaAllFloorObjects.length; i++) {
+                    doorIds = [];
+
 					for (var j = 0, obj, doorCount = 0, oldestObj; j < aaAllFloorObjects[i].length; j++) {
 
 						obj = aaAllFloorObjects[i][j];
+                        doorIds.push(obj.id);
 
 						if (doorCount === 0) {
 
@@ -1408,8 +1505,9 @@
 							obj[spanType] = aFloorSpan[i];
 
 							// children
-							//obj.children = Ext.isDefined(aFloorSpan[i + 1]) ? aFloorSpan[i] / aFloorSpan[i + 1] : 0;
-							obj.children = obj.leaf ? 0 : aFloorSpan[i];
+                            if (obj.leaf) {
+                                obj.children = 0;
+                            }
 
 							// first sibling
 							obj.oldest = true;
@@ -1429,16 +1527,26 @@
 							doorCount = 0;
 						}
 					}
+
+                    // set above floor door children to number of unique door ids on this floor
+                    if (i > 0) {
+                        aAboveFloorObjects = aaAllFloorObjects[i-1];
+                        uniqueDoorIds = Ext.Array.unique(doorIds);
+
+                        for (var j = 0; j < aAboveFloorObjects.length; j++) {
+                            aAboveFloorObjects[j].children = uniqueDoorIds.length;
+                        }
+                    }
 				}
 
 				// add parents if more than 1 floor
 				if (nAxisHeight > 1) {
-					for (var i = 1, allFloor; i < nAxisHeight; i++) {
-						allFloor = aaAllFloorObjects[i];
+					for (var i = 1, aAllFloor; i < nAxisHeight; i++) {
+						aAllFloor = aaAllFloorObjects[i];
 
-						//for (var j = 0, obj, doorCount = 0, span = aFloorSpan[i - 1], parentObj = aaAllFloorObjects[i - 1][0]; j < allFloor.length; j++) {
-						for (var j = 0, doorCount = 0, span = aFloorSpan[i - 1]; j < allFloor.length; j++) {
-							allFloor[j].parent = aaAllFloorObjects[i - 1][j];
+						//for (var j = 0, obj, doorCount = 0, span = aFloorSpan[i - 1], parentObj = aaAllFloorObjects[i - 1][0]; j < aAllFloor.length; j++) {
+						for (var j = 0, doorCount = 0, span = aFloorSpan[i - 1]; j < aAllFloor.length; j++) {
+							aAllFloor[j].parent = aaAllFloorObjects[i - 1][j];
 
 							//doorCount++;
 
@@ -1454,11 +1562,11 @@
 				if (aaAllFloorObjects.length) {
 
 					// set span to second lowest span number: if aFloorSpan == [15,3,15,1], set span to 3
-					var span = nAxisHeight > 1 ? support.prototype.array.sort(Ext.clone(aFloorSpan))[1] : nAxisWidth,
-						allFloorObjectsLast = aaAllFloorObjects[aaAllFloorObjects.length - 1];
+					var nSpan = nAxisHeight > 1 ? support.prototype.array.sort(Ext.clone(aFloorSpan))[1] : nAxisWidth,
+						aAllFloorObjectsLast = aaAllFloorObjects[aaAllFloorObjects.length - 1];
 
-					for (var i = 0, leaf, parentUuids, obj, leafUuids = []; i < allFloorObjectsLast.length; i++) {
-						leaf = allFloorObjectsLast[i];
+					for (var i = 0, leaf, parentUuids, obj, leafUuids = []; i < aAllFloorObjectsLast.length; i++) {
+						leaf = aAllFloorObjectsLast[i];
 						leafUuids.push(leaf.uuid);
 						parentUuids = [];
 						obj = leaf;
@@ -1473,9 +1581,9 @@
 						leaf.uuids = Ext.clone(parentUuids);
 
 						// add uuid for all leaves
-						if (leafUuids.length === span) {
-							for (var j = (i - span) + 1, leaf; j <= i; j++) {
-								leaf = allFloorObjectsLast[j];
+						if (leafUuids.length === nSpan) {
+							for (var j = (i - nSpan) + 1, leaf; j <= i; j++) {
+								leaf = aAllFloorObjectsLast[j];
 								leaf.uuids = leaf.uuids.concat(Ext.clone(leafUuids));
 							}
 
@@ -1518,10 +1626,34 @@
 				return layout.showHierarchy && Ext.isObject(response.metaData.ouHierarchy) && response.metaData.ouHierarchy.hasOwnProperty(id);
 			};
 
-			service.layout.layout2plugin = function(layout) {
+            service.layout.getHierarchyName = function(ouHierarchy, names, id) {
+                var graph = ouHierarchy[id],
+                    ids = Ext.Array.clean(graph.split('/')),
+                    hierarchyName = '';
+
+                if (ids.length < 2) {
+                    return names[id];
+                }
+
+                for (var i = 0; i < ids.length; i++) {
+                    hierarchyName += names[ids[i]] + ' / ';
+                }
+
+                hierarchyName += names[id];
+
+                return hierarchyName;
+            };
+
+			service.layout.layout2plugin = function(layout, el) {
 				var layout = Ext.clone(layout),
 					dimensions = Ext.Array.clean([].concat(layout.columns || [], layout.rows || [], layout.filters || []));
 
+				layout.url = init.contextPath;
+
+				if (el) {
+					layout.el = el;
+				}
+
 				if (Ext.isString(layout.id)) {
 					return {id: layout.id};
 				}
@@ -1542,6 +1674,7 @@
 						delete item.code;
 						delete item.created;
 						delete item.lastUpdated;
+						delete item.value;
 					}
 				}
 
@@ -1593,92 +1726,212 @@
 				return layout;
 			};
 
+            service.layout.getDataDimensionsFromLayout = function(layout) {
+                var dimensions = Ext.Array.clean([].concat(layout.columns || [], layout.rows || [], layout.filters || [])),
+                    ignoreKeys = ['pe', 'ou'],
+                    dataDimensions = [];
+
+                for (var i = 0; i < dimensions.length; i++) {
+                    if (!Ext.Array.contains(ignoreKeys, dimensions[i].dimension)) {
+                        dataDimensions.push(dimensions[i]);
+                    }
+                }
+
+                return dataDimensions;
+            };
+
 			// response
 			service.response = {};
 
-			service.response.getExtendedResponse = function(xLayout, response) {
-				var ids = [];
-
+				// aggregate
+			service.response.aggregate = {};
+
+			service.response.aggregate.getExtendedResponse = function(xLayout, response) {
+				var emptyId = '[N/A]',
+                    meta = ['ou', 'pe'],
+                    ouHierarchy,
+                    names,
+					headers;
+
+				response = Ext.clone(response);
+				headers = response.headers;
+                ouHierarchy = response.metaData.ouHierarchy,
+                names = response.metaData.names;
+                names[emptyId] = emptyId;
+
+                response.metaData.optionNames = {};
 				response.nameHeaderMap = {};
 				response.idValueMap = {};
 
-				// extend headers
-				(function() {
-
-					// extend headers: index, ids, size
-					for (var i = 0, header; i < response.headers.length; i++) {
-						header = response.headers[i];
-
-						// index
-						header.index = i;
-
-						if (header.meta) {
-
-							// ids
-							header.ids = Ext.clone(xLayout.dimensionNameIdsMap[header.name]) || [];
-
-							// size
-							header.size = header.ids.length;
-
-							// collect ids, used by extendMetaData
-							ids = ids.concat(header.ids);
-						}
-					}
-
-					// nameHeaderMap (headerName: header)
-					for (var i = 0, header; i < response.headers.length; i++) {
-						header = response.headers[i];
-
-						response.nameHeaderMap[header.name] = header;
-					}
-				}());
-
-				// extend metadata
-				(function() {
-					for (var i = 0, id, splitId ; i < ids.length; i++) {
-						id = ids[i];
-
-						if (id.indexOf('-') !== -1) {
-							splitId = id.split('-');
-							response.metaData.names[id] = response.metaData.names[splitId[0]] + ' ' + response.metaData.names[splitId[1]];
-						}
-					}
-				}());
-
-				// create value id map
-				(function() {
-					var valueHeaderIndex = response.nameHeaderMap[conf.finals.dimension.value.value].index,
-						coHeader = response.nameHeaderMap[conf.finals.dimension.category.dimensionName],
-						dx = dimConf.data.dimensionName,
-						co = dimConf.category.dimensionName,
-						axisDimensionNames = xLayout.axisDimensionNames,
-						idIndexOrder = [];
-
-					// idIndexOrder
-					for (var i = 0; i < axisDimensionNames.length; i++) {
-						idIndexOrder.push(response.nameHeaderMap[axisDimensionNames[i]].index);
-
-						// If co exists in response and is not added in layout, add co after dx
-						if (coHeader && !Ext.Array.contains(axisDimensionNames, co) && axisDimensionNames[i] === dx) {
-							idIndexOrder.push(coHeader.index);
-						}
-					}
-
-					// idValueMap
-					for (var i = 0, row, id; i < response.rows.length; i++) {
-						row = response.rows[i];
-						id = '';
-
-						for (var j = 0; j < idIndexOrder.length; j++) {
-							id += row[idIndexOrder[j]];
-						}
-
-						response.idValueMap[id] = row[valueHeaderIndex];
-					}
-				}());
+				// add to headers: size, index, response ids
+				for (var i = 0, header, isMeta; i < headers.length; i++) {
+					header = headers[i];
+                    header.ids = [];
+                    isMeta = Ext.Array.contains(meta, header.name);
+
+                    // overwrite row ids, update metadata, set unique header ids
+                    if (header.meta) {
+                        if (header.type === 'java.lang.Double') {
+                            var objects = [];
+
+                            for (var j = 0, id, fullId, parsedId, displayId; j < response.rows.length; j++) {
+                                id = response.rows[j][i] || emptyId;
+                                fullId = header.name + id;
+                                parsedId = parseFloat(id);
+
+                                displayId = Ext.isNumber(parsedId) ? parsedId : (names[id] || id);
+
+								// update names
+                                names[fullId] = (isMeta ? '' : header.column + ' ') + displayId;
+
+								// update rows
+                                response.rows[j][i] = fullId;
+
+								// number sorting
+                                objects.push({
+                                    id: fullId,
+                                    sortingId: Ext.isNumber(parsedId) ? parsedId : Number.MAX_VALUE
+                                });
+                            }
+
+                            support.prototype.array.sort(objects, 'ASC', 'sortingId');
+                            header.ids = Ext.Array.pluck(objects, 'id');
+                        }
+                        else {
+							var objects = [];
+
+                            for (var j = 0, id, fullId, name, isHierarchy; j < response.rows.length; j++) {
+                                id = response.rows[j][i] || emptyId;
+                                fullId = header.name + id;
+                                isHierarchy = service.layout.isHierarchy(xLayout, response, id);
+
+                                // add dimension name prefix if not pe/ou
+                                name = isMeta ? '' : header.column + ' ';
+
+                                // add hierarchy if ou and showHierarchy
+                                name = isHierarchy ? service.layout.getHierarchyName(ouHierarchy, names, id) : (names[id] || id);
+
+                                names[fullId] = name;
+
+                                // update rows
+                                response.rows[j][i] = fullId;
+
+                                // update ou hierarchy
+                                if (isHierarchy) {
+									ouHierarchy[fullId] = ouHierarchy[id];
+								}
+
+								objects.push({
+									id: fullId,
+									sortingId: header.name === 'pe' ? fullId : name
+								});
+                            }
+
+                            support.prototype.array.sort(objects, 'ASC', 'sortingId');
+                            header.ids = Ext.Array.pluck(objects, 'id');
+                        }
+                    }
+
+					header.ids = Ext.Array.unique(header.ids);
+
+					header.size = header.ids.length;
+					header.index = i;
+
+					response.nameHeaderMap[header.name] = header;
+				}
+
+				// idValueMap: vars
+				var valueHeaderIndex = response.nameHeaderMap[conf.finals.dimension.value.value].index,
+					dx = dimConf.data.dimensionName,
+					axisDimensionNames = xLayout.axisDimensionNames,
+					idIndexOrder = [];
+
+				// idValueMap: idIndexOrder
+				for (var i = 0; i < axisDimensionNames.length; i++) {
+					idIndexOrder.push(response.nameHeaderMap[axisDimensionNames[i]].index);
+				}
+
+				// idValueMap
+				for (var i = 0, row, id; i < response.rows.length; i++) {
+					row = response.rows[i];
+					id = '';
+
+					for (var j = 0; j < idIndexOrder.length; j++) {
+						id += row[idIndexOrder[j]];
+					}
+
+					response.idValueMap[id] = row[valueHeaderIndex];
+				}
 
 				return response;
 			};
+
+				// query
+			service.response.query = {};
+
+			service.response.query.getExtendedResponse = function(layout, response) {
+				var xResponse = Ext.clone(response),
+					metaData = xResponse.metaData,
+                    dimensionNames = Ext.Array.unique(Ext.Array.pluck(layout.columns, 'dimension')),
+                    dimensionHeaders = [],
+					headers = xResponse.headers,
+					nameHeaderMap = {},
+                    nameMap = {},
+                    ouIndex;
+
+                nameMap['pe'] = 'eventdate';
+                nameMap['ou'] = 'ouname';
+
+                // get ou index
+                for (var i = 0, header; i < headers.length; i++) {
+					if (headers[i].name === 'ou') {
+						ouIndex = i;
+						break;
+					}
+				}
+
+				// update rows
+				for (var i = 0, header; i < headers.length; i++) {
+					header = headers[i];
+					header.index = i;
+
+					nameHeaderMap[header.name] = header;
+
+					if (header.type === 'java.lang.Double') {
+						for (var j = 0, value; j < xResponse.rows.length; j++) {
+                            value = xResponse.rows[j][i];
+							xResponse.rows[j][i] = value ? parseFloat(value) : value;
+						}
+					}
+
+					if (header.name === 'eventdate') {
+						for (var j = 0; j < xResponse.rows.length; j++) {
+							xResponse.rows[j][i] = xResponse.rows[j][i].substr(0,10);
+						}
+					}
+
+					// TODO, using descendants -> missing orgunits in ouHierarchy
+
+					//else if (header.name === 'ouname' && layout.showHierarchy && metaData.ouHierarchy) {
+						//for (var j = 0, ouId; j < xResponse.rows.length; j++) {
+							//ouId = xResponse.rows[j][ouIndex];
+							//xResponse.rows[j][i] = service.layout.getHierarchyName(metaData.ouHierarchy, metaData.names, ouId);
+						//}
+					//}
+				}
+
+				// dimension headers
+                for (var i = 0, name; i < dimensionNames.length; i++) {
+                    name = nameMap[dimensionNames[i]] || dimensionNames[i];
+
+                    dimensionHeaders.push(nameHeaderMap[name]);
+                }
+
+				xResponse.dimensionHeaders = dimensionHeaders;
+				xResponse.nameHeaderMap = nameHeaderMap;
+
+				return xResponse;
+			};
 		}());
 
 		// web
@@ -1695,14 +1948,14 @@
 
 				message = message || 'Loading..';
 
-				if (Ext.isObject(component.mask) && component.mask.destroy) {
+				if (component.mask && component.mask.destroy) {
 					component.mask.destroy();
 					component.mask = null;
 				}
 
 				component.mask = new Ext.create('Ext.LoadMask', component, {
 					shadow: false,
-					message: message,
+					msg: message,
 					style: 'box-shadow:0',
 					bodyStyle: 'box-shadow:0'
 				});
@@ -1732,63 +1985,108 @@
 			// analytics
 			web.analytics = {};
 
-			web.analytics.getParamString = function(xLayout, isSorted) {
-				var axisDimensionNames = isSorted ? xLayout.sortedAxisDimensionNames : xLayout.axisDimensionNames,
-					filterDimensions = isSorted ? xLayout.sortedFilterDimensions : xLayout.filterDimensions,
-					dimensionNameIdsMap = isSorted ? xLayout.dimensionNameSortedIdsMap : xLayout.dimensionNameIdsMap,
-					paramString = '?',
-					addCategoryDimension = false,
-					map = xLayout.dimensionNameItemsMap,
-					dx = dimConf.indicator.dimensionName,
-					co = dimConf.category.dimensionName;
-
-				for (var i = 0, dimName, items; i < axisDimensionNames.length; i++) {
-					dimName = axisDimensionNames[i];
-
-					paramString += 'dimension=' + dimName;
-
-					items = Ext.clone(dimensionNameIdsMap[dimName]);
-
-					if (dimName === dx) {
-						for (var j = 0, index; j < items.length; j++) {
-							index = items[j].indexOf('-');
-
-							if (index > 0) {
-								addCategoryDimension = true;
-								items[j] = items[j].substr(0, index);
+			web.analytics.getParamString = function(view, format, skipPaging) {
+                var paramString,
+                    dimensions = Ext.Array.clean([].concat(view.columns || [], view.rows || [])),
+                    ignoreKeys = ['longitude', 'latitude'],
+                    dataTypeMap = {
+                        'aggregated_values': 'aggregate',
+                        'individual_cases': 'query'
+                    },
+                    nameItemsMap;
+
+                format = format || 'json';
+
+                paramString = '/api/analytics/events/' + dataTypeMap[view.dataType] + '/' + view.program.id + '.' + format + '?';
+
+				// stage
+				paramString += 'stage=' + view.programStage.id;
+
+                // dimensions
+                if (dimensions) {
+					for (var i = 0, dim; i < dimensions.length; i++) {
+						dim = dimensions[i];
+
+						if (Ext.Array.contains(ignoreKeys, dim.dimension) || (dim.dimension === 'pe' && !dim.items && !dim.filter)) {
+							continue;
+						}
+
+						paramString += '&dimension=' + dim.dimension;
+
+						if (dim.items && dim.items.length) {
+							paramString += ':';
+
+							for (var j = 0, item; j < dim.items.length; j++) {
+								item = dim.items[j];
+
+								paramString += encodeURIComponent(item.id) + ((j < (dim.items.length - 1)) ? ';' : '');
 							}
 						}
-
-						items = Ext.Array.unique(items);
-					}
-
-					if (dimName !== co) {
-						paramString += ':' + items.join(';');
-					}
-
-					if (i < (axisDimensionNames.length - 1)) {
-						paramString += '&';
-					}
-				}
-
-				if (addCategoryDimension) {
-					paramString += '&dimension=' + conf.finals.dimension.category.dimensionName;
-				}
-
-				if (Ext.isArray(filterDimensions) && filterDimensions.length) {
-					for (var i = 0, dim; i < filterDimensions.length; i++) {
-						dim = filterDimensions[i];
-
-						paramString += '&filter=' + dim.dimensionName + ':' + dim.ids.join(';');
-					}
-				}
-
-				if (xLayout.showHierarchy) {
-					paramString += '&hierarchyMeta=true';
-				}
-
-				return paramString;
-			};
+						else {
+							paramString += dim.filter ? ':' + encodeURIComponent(dim.filter) : '';
+						}
+					}
+				}
+
+                // filters
+                if (view.filters) {
+					for (var i = 0, dim; i < view.filters.length; i++) {
+						dim = view.filters[i];
+
+                        paramString += '&filter=' + dim.dimension;
+
+                        if (Ext.isArray(dim.items) && dim.items.length) {
+                            paramString += ':';
+
+                            for (var j = 0; j < dim.items.length; j++) {
+                                paramString += encodeURIComponent(dim.items[j].id);
+                                paramString += j < dim.items.length - 1 ? ';' : '';
+                            }
+                        }
+                        else {
+                            paramString += dim.filter ? ':' + encodeURIComponent(dim.filter) : '';
+                        }
+					}
+				}
+
+                // dates
+                if (view.startDate && view.endDate) {
+                    paramString += '&startDate=' + view.startDate + '&endDate=' + view.endDate;
+                }
+
+				// hierarchy
+				paramString += view.showHierarchy ? '&hierarchyMeta=true' : '';
+
+                // limit
+                if (view.dataType === 'aggregated_values' && (view.sortOrder && view.topLimit)) {
+                    paramString += '&limit=' + view.topLimit + '&sortOrder=' + (view.sortOrder < 0 ? 'ASC' : 'DESC');
+                }
+
+                // count type
+                if (view.dataType === 'aggregated_values' && view.countType) {
+                    if (view.countType === 'tracked_entity_instances') {
+                        paramString += '&uniqueInstances=true';
+                    }
+                }
+
+                // sorting
+                if (view.dataType === 'individual_cases' && view.sorting) {
+                    if (view.sorting.id && view.sorting.direction) {
+                        paramString += '&' + view.sorting.direction.toLowerCase() + '=' + view.sorting.id;
+                    }
+                }
+
+                // paging
+                if (view.dataType === 'individual_cases' && view.paging && !skipPaging) {
+                    paramString += view.paging.pageSize ? '&pageSize=' + view.paging.pageSize : '';
+                    paramString += view.paging.page ? '&page=' + view.paging.page : '';
+                }
+
+                // display property
+                paramString += '&displayProperty=' + init.userAccount.settings.keyAnalysisDisplayProperty.toUpperCase();
+
+                return paramString;
+            };
 
 			web.analytics.validateUrl = function(url) {
 				var msg;
@@ -1806,14 +2104,63 @@
                 alert(msg);
 			};
 
-			// pivot
-			web.pivot = {};
-
-			web.pivot.getHtml = function(xLayout, xResponse, xColAxis, xRowAxis) {
+			// report
+			web.report = {};
+
+				// aggregate
+			web.report.aggregate = {};
+
+			web.report.aggregate.sort = function(xLayout, xResponse, xColAxis) {
+				var condoId = xLayout.sorting.id,
+					name = xLayout.rows[0].dimension,
+					ids = xResponse.nameHeaderMap[name].ids,
+					valueMap = xResponse.idValueMap,
+					direction = xLayout.sorting ? xLayout.sorting.direction : 'DESC',
+					objects = [],
+					layout;
+
+				// relative id?
+				if (Ext.isString(condoId)) {
+					condoId = condoId.toLowerCase() === 'total' ? 'total_' : condoId;
+				}
+				else if (Ext.isNumber(condoId)) {
+					if (condoId === 0) {
+						condoId = 'total_';
+					}
+					else {
+						condoId = xColAxis.ids[parseInt(condoId) - 1];
+					}
+				}
+				else {
+					return xResponse;
+				}
+
+				// collect values
+				for (var i = 0, key, value; i < ids.length; i++) {
+					key = condoId + ids[i];
+					value = parseFloat(valueMap[key]);
+
+					objects.push({
+						id: ids[i],
+						value: Ext.isNumber(value) ? value : (Number.MAX_VALUE * -1)
+					});
+				}
+
+				support.prototype.array.sort(objects, direction, 'value');
+
+				// new id order
+				xResponse.nameHeaderMap[name].ids = Ext.Array.pluck(objects, 'id');
+
+				return xResponse;
+			};
+
+			web.report.aggregate.getHtml = function(xLayout, xResponse, xColAxis, xRowAxis) {
 				var getRoundedHtmlValue,
 					getTdHtml,
 					doSubTotals,
-					doTotals,
+					doRowTotals,
+                    doColTotals,
+                    doSortableColumnHeaders,
 					getColAxisHtmlArray,
 					getRowHtmlArray,
 					rowAxisHtmlArray,
@@ -1841,8 +2188,11 @@
 					totalColObjects = [],
 					uuidDimUuidsMap = {},
 					isLegendSet = Ext.isObject(xLayout.legendSet) && Ext.isArray(xLayout.legendSet.mapLegends) && xLayout.legendSet.mapLegends.length,
+                    tdCount = 0,
 					htmlArray;
 
+				xResponse.sortableIdObjects = [];
+
 				getRoundedHtmlValue = function(value, dec) {
 					dec = dec || 2;
 					return parseFloat(support.prototype.number.roundIf(value, 2)).toString();
@@ -1865,7 +2215,14 @@
 						return '';
 					}
 
-					// Background color from legend set
+					if (config.hidden || config.collapsed) {
+						return '';
+					}
+
+                    // number of cells
+                    tdCount = tdCount + 1;
+
+					// background color from legend set
 					if (isNumeric && xLayout.legendSet) {
 						var value = parseFloat(config.value);
 						mapLegends = xLayout.legendSet.mapLegends;
@@ -1881,35 +2238,35 @@
 					rowSpan = config.rowSpan ? 'rowspan="' + config.rowSpan + '" ' : '';
 					htmlValue = config.collapsed ? '' : config.htmlValue || config.value || '';
 					htmlValue = config.type !== 'dimension' ? support.prototype.number.prettyPrint(htmlValue, xLayout.digitGroupSeparator) : htmlValue;
-					displayDensity = conf.pivot.displayDensity[config.displayDensity] || conf.pivot.displayDensity[xLayout.displayDensity];
-					fontSize = conf.pivot.fontSize[config.fontSize] || conf.pivot.fontSize[xLayout.fontSize];
+					displayDensity = conf.report.displayDensity[config.displayDensity] || conf.report.displayDensity[xLayout.displayDensity];
+					fontSize = conf.report.fontSize[config.fontSize] || conf.report.fontSize[xLayout.fontSize];
 
 					cls += config.hidden ? ' td-hidden' : '';
 					cls += config.collapsed ? ' td-collapsed' : '';
-					cls += isValue ? ' pointer' : '';
+					//cls += isValue ? ' pointer' : '';
 					cls += bgColor ? ' legend' : (config.cls ? ' ' + config.cls : '');
 
 					// sorting
 					if (Ext.isString(metaDataId)) {
 						cls += ' td-sortable';
 
-						xLayout.sortableIdObjects.push({
+						xResponse.sortableIdObjects.push({
 							id: metaDataId,
 							uuid: config.uuid
 						});
 					}
 
 					html += '<td ' + (config.uuid ? ('id="' + config.uuid + '" ') : '');
-					html += ' class="' + cls + '" ' + colSpan + rowSpan
-
-
-					if (bgColor) {
-						html += '>';
-						html += '<div class="legendCt">';
-						html += '<div class="number ' + config.cls + '" style="padding:' + displayDensity + '; padding-right:3px; font-size:' + fontSize + '">' + htmlValue + '</div>';
-						html += '<div class="arrowCt ' + config.cls + '">';
-						html += '<div class="arrow" style="border-bottom:8px solid transparent; border-right:8px solid ' + bgColor + '">&nbsp;</div>';
-						html += '</div></div></div></td>';
+					html += ' class="' + cls + '" ' + colSpan + rowSpan;
+
+
+					//if (bgColor) {
+						//html += '>';
+						//html += '<div class="legendCt">';
+						//html += '<div class="number ' + config.cls + '" style="padding:' + displayDensity + '; padding-right:3px; font-size:' + fontSize + '">' + htmlValue + '</div>';
+						//html += '<div class="arrowCt ' + config.cls + '">';
+						//html += '<div class="arrow" style="border-bottom:8px solid transparent; border-right:8px solid ' + bgColor + '">&nbsp;</div>';
+						//html += '</div></div></div></td>';
 
 						//cls = 'legend';
 						//cls += config.hidden ? ' td-hidden' : '';
@@ -1923,37 +2280,29 @@
 						//html += htmlValue + '</div>';
 						//html += '<div class="legendColor" style="background-color:' + bgColor + '">&nbsp;</div>';
 						//html += '</div></td>';
-					}
-					else {
-						html += 'style="padding:' + displayDensity + '; font-size:' + fontSize + ';"' + '>' + htmlValue + '</td>';
-					}
+					//}
+					//else {
+						//html += 'style="padding:' + displayDensity + '; font-size:' + fontSize + ';"' + '>' + htmlValue + '</td>';
+                        html += 'style="' + (bgColor && isValue ? 'color:' + bgColor + '; ' : '') + 'padding:' + displayDensity + '; font-size:' + fontSize + ';"' + '>' + htmlValue + '</td>';
+					//}
 
 					return html;
 				};
 
-				doSubTotals = function(xAxis) {
-					return !!xLayout.showSubTotals && xAxis && xAxis.dims > 1;
-
-					//var multiItemDimension = 0,
-						//unique;
-
-					//if (!(xLayout.showSubTotals && xAxis && xAxis.dims > 1)) {
-						//return false;
-					//}
-
-					//unique = xAxis.xItems.unique;
-
-					//for (var i = 0; i < unique.length; i++) {
-						//if (unique[i].length > 1) {
-							//multiItemDimension++;
-						//}
-					//}
-
-					//return (multiItemDimension > 1);
-				};
-
-				doTotals = function() {
-					return !!xLayout.showTotals;
+				doRowTotals = function() {
+					return !!xLayout.showRowTotals;
+				};
+
+                doColTotals = function() {
+					return !!xLayout.showColTotals;
+				};
+
+				doColSubTotals = function() {
+					return !!xLayout.showColSubTotals && xRowAxis && xRowAxis.dims > 1;
+				};
+
+				doRowSubTotals = function() {
+					return !!xLayout.showRowSubTotals && xColAxis && xColAxis.dims > 1;
 				};
 
 				doSortableColumnHeaders = function() {
@@ -1964,14 +2313,52 @@
 					var a = [],
 						getEmptyHtmlArray;
 
-					getEmptyHtmlArray = function() {
-						return (xColAxis && xRowAxis) ? getTdHtml({
-							cls: 'pivot-dim-empty cursor-default',
-							colSpan: xRowAxis.dims,
-							rowSpan: xColAxis.dims,
-							htmlValue: '&nbsp;'
-						}) : '';
-					};
+                    getEmptyNameTdConfig = function(config) {
+                        config = config || {};
+
+                        return getTdHtml({
+                            cls: config.cls ? ' ' + config.cls : 'pivot-empty',
+                            colSpan: config.colSpan ? config.colSpan : 1,
+                            rowSpan: config.rowSpan ? config.rowSpan : 1,
+                            htmlValue: config.htmlValue ? config.htmlValue : '&nbsp;'
+                        });
+                    };
+
+                    getEmptyHtmlArray = function(i) {
+                        var a = [];
+
+                        if (i < xColAxis.dims - 1) {
+                            if (xRowAxis && xRowAxis.dims) {
+                                for (var j = 0; j < xRowAxis.dims - 1; j++) {
+                                    a.push(getEmptyNameTdConfig({
+                                        cls: 'pivot-dim-label'
+                                    }));
+                                }
+                            }
+
+                            a.push(getEmptyNameTdConfig({
+                                cls: 'pivot-dim-label',
+                                htmlValue: dimConf.objectNameMap[xLayout.columnObjectNames[i]].name
+                            }));
+                        }
+                        else {
+                            if (xRowAxis && xRowAxis.dims) {
+                                for (var j = 0; j < xRowAxis.dims - 1; j++) {
+                                    a.push(getEmptyNameTdConfig({
+                                        cls: 'pivot-dim-label',
+                                        htmlValue: dimConf.objectNameMap[xLayout.rowObjectNames[j]].name
+                                    }));
+                                }
+                            }
+
+                            a.push(getEmptyNameTdConfig({
+                                cls: 'pivot-dim-label',
+                                htmlValue: dimConf.objectNameMap[xLayout.rowObjectNames[j]].name + ' / ' + dimConf.objectNameMap[xLayout.columnObjectNames[i]].name
+                            }));
+                        }
+
+                        return a;
+                    };
 
 					if (!(xColAxis && Ext.isObject(xColAxis))) {
 						return a;
@@ -1981,12 +2368,20 @@
 					for (var i = 0, dimHtml; i < xColAxis.dims; i++) {
 						dimHtml = [];
 
-						if (i === 0) {
-							dimHtml.push(getEmptyHtmlArray());
+                        if (xLayout.showDimensionLabels) {
+                            dimHtml = dimHtml.concat(getEmptyHtmlArray(i));
+                        }
+                        else if (i === 0) {
+							dimHtml.push(xColAxis && xRowAxis ? getEmptyNameTdConfig({
+                                colSpan: xRowAxis.dims,
+                                rowSpan: xColAxis.dims
+                            }) : '');
 						}
 
 						for (var j = 0, obj, spanCount = 0, condoId, totalId; j < xColAxis.size; j++) {
 							spanCount++;
+							condoId = null;
+							totalId = null;
 
 							obj = xColAxis.objects.all[i][j];
 							obj.type = 'dimension';
@@ -1997,12 +2392,13 @@
 
 							// sortable column headers. last dim only.
 							if (i === xColAxis.dims - 1 && doSortableColumnHeaders()) {
-								condoId = xColAxis.ids[j].split('-').join('');
+								//condoId = xColAxis.ids[j].split('-').join('');
+								condoId = xColAxis.ids[j];
 							}
 
 							dimHtml.push(getTdHtml(obj, condoId));
 
-							if (i === 0 && spanCount === xColAxis.span[i] && doSubTotals(xColAxis) ) {
+							if (i === 0 && spanCount === xColAxis.span[i] && doRowSubTotals() ) {
 								dimHtml.push(getTdHtml({
 									type: 'dimensionSubtotal',
 									cls: 'pivot-dim-subtotal cursor-default',
@@ -2013,7 +2409,7 @@
 								spanCount = 0;
 							}
 
-							if (i === 0 && (j === xColAxis.size - 1) && doTotals()) {
+							if (i === 0 && (j === xColAxis.size - 1) && doRowTotals()) {
 								totalId = doSortableColumnHeaders() ? 'total_' : null;
 
 								dimHtml.push(getTdHtml({
@@ -2059,22 +2455,43 @@
 
 					// dimension
 					if (xRowAxis) {
+						var aLineBreak = new Array(xRowAxis.dims);
+
 						for (var i = 0, row; i < xRowAxis.size; i++) {
 							row = [];
 
 							for (var j = 0, obj, newObj; j < xRowAxis.dims; j++) {
 								obj = xRowAxis.objects.all[j][i];
 								obj.type = 'dimension';
-								obj.cls = 'pivot-dim td-nobreak' + (service.layout.isHierarchy(xLayout, xResponse, obj.id) ? ' align-left' : '');
+								obj.cls = 'pivot-dim ' + (service.layout.isHierarchy(xLayout, xResponse, obj.id) ? ' align-left' : '');
 								obj.noBreak = true;
 								obj.hidden = !(obj.rowSpan || obj.colSpan);
 								obj.htmlValue = service.layout.getItemName(xLayout, xResponse, obj.id, true);
 
 								row.push(obj);
+
+								// allow line break for this dim?
+								if (obj.htmlValue.length > 50) {
+									aLineBreak[j] = true;
+								}
 							}
 
 							axisAllObjects.push(row);
 						}
+
+						// add nowrap line break cls
+						for (var i = 0, dim; i < aLineBreak.length; i++) {
+							dim = aLineBreak[i];
+
+							if (!dim) {
+								for (var j = 0, obj; j < xRowAxis.size; j++) {
+									obj = axisAllObjects[j][i];
+
+									obj.cls += ' td-nobreak';
+									obj.noBreak = true;
+								}
+							}
+						}
 					}
 	//axisAllObjects = [ [ dim, dim ]
 	//				     [ dim, dim ]
@@ -2082,7 +2499,7 @@
 	//				     [ dim, dim ] ];
 
 					// value
-					for (var i = 0, valueItemsRow, valueObjectsRow, idValueMap = Ext.clone(xResponse.idValueMap); i < rowAxisSize; i++) {
+					for (var i = 0, valueItemsRow, valueObjectsRow, idValueMap = xResponse.idValueMap; i < rowAxisSize; i++) {
 						valueItemsRow = [];
 						valueObjectsRow = [];
 
@@ -2091,7 +2508,8 @@
 							uuids = [];
 
 							// meta data uid
-							id = (xColAxis ? support.prototype.str.replaceAll(xColAxis.ids[j], '-', '') : '') + (xRowAxis ? support.prototype.str.replaceAll(xRowAxis.ids[i], '-', '') : '');
+							//id = (xColAxis ? support.prototype.str.replaceAll(xColAxis.ids[j], '-', '') : '') + (xRowAxis ? support.prototype.str.replaceAll(xRowAxis.ids[i], '-', '') : '');
+							id = (xColAxis ? xColAxis.ids[j] : '') + (xRowAxis ? xRowAxis.ids[i] : '');
 
 							// value html element id
 							uuid = Ext.data.IdGenerator.get('uuid').generate();
@@ -2134,7 +2552,7 @@
 					}
 
 					// totals
-					if (xColAxis && doTotals()) {
+					if (xColAxis && doRowTotals()) {
 						for (var i = 0, empty = [], total = 0; i < valueObjects.length; i++) {
 							for (j = 0, obj; j < valueObjects[i].length; j++) {
 								obj = valueObjects[i][j];
@@ -2175,17 +2593,17 @@
 								// if value row is empty
 								if (isValueRowEmpty) {
 
-									// Hide values by adding collapsed = true to all items
+									// hide values by adding collapsed = true to all items
 									for (var j = 0; j < valueRow.length; j++) {
 										valueRow[j].collapsed = true;
 									}
 
-									// Hide totals by adding collapsed = true to all items
-									if (doTotals()) {
+									// hide totals by adding collapsed = true to all items
+									if (doRowTotals()) {
 										totalValueObjects[i].collapsed = true;
 									}
 
-									// Hide/reduce parent dim span
+									// hide/reduce parent dim span
 									dimLeaf = axisAllObjects[i][xRowAxis.dims-1];
 									recursiveReduce(dimLeaf);
 								}
@@ -2193,10 +2611,10 @@
 						}
 					}
 
-					xValueObjects = Ext.clone(valueObjects);
+					xValueObjects = valueObjects;
 
 					// col subtotals
-					if (doSubTotals(xColAxis)) {
+					if (doRowSubTotals()) {
 						var tmpValueObjects = [];
 
 						for (var i = 0, row, rowSubTotal, colCount; i < xValueObjects.length; i++) {
@@ -2238,7 +2656,7 @@
 					}
 
 					// row subtotals
-					if (doSubTotals(xRowAxis)) {
+					if (doColSubTotals()) {
 						var tmpAxisAllObjects = [],
 							tmpValueObjects = [],
 							tmpTotalValueObjects = [],
@@ -2382,7 +2800,7 @@
 				getColTotalHtmlArray = function() {
 					var a = [];
 
-					if (xRowAxis && doTotals()) {
+					if (xRowAxis && doColTotals()) {
 						var xTotalColObjects;
 
 						// Total col items
@@ -2407,9 +2825,9 @@
 							empty = [];
 						}
 
-						xTotalColObjects = Ext.clone(totalColObjects);
+						xTotalColObjects = totalColObjects;
 
-						if (xColAxis && doSubTotals(xColAxis)) {
+						if (xColAxis && doRowSubTotals()) {
 							var tmp = [];
 
 							for (var i = 0, item, subTotal = 0, empty = [], colCount = 0; i < xTotalColObjects.length; i++) {
@@ -2450,7 +2868,7 @@
 						empty = [],
 						a = [];
 
-					if (doTotals()) {
+					if (doRowTotals() && doColTotals()) {
 						for (var i = 0, obj; i < totalColObjects.length; i++) {
 							obj = totalColObjects[i];
 
@@ -2479,7 +2897,7 @@
 						row,
 						a = [];
 
-					if (doTotals()) {
+					if (doColTotals()) {
 						if (xRowAxis) {
 							dimTotalArray = [getTdHtml({
 								type: 'dimensionSubtotal',
@@ -2489,7 +2907,7 @@
 							})];
 						}
 
-						row = [].concat(dimTotalArray || [], Ext.clone(colTotal) || [], Ext.clone(grandTotal) || []);
+						row = [].concat(dimTotalArray || [], colTotal || [], grandTotal || []);
 
 						a.push(row);
 					}
@@ -2513,11 +2931,112 @@
 
 					return {
 						html: getHtml(htmlArray),
-						uuidDimUuidsMap: uuidDimUuidsMap
+						uuidDimUuidsMap: uuidDimUuidsMap,
+						xColAxis: xColAxis,
+						xRowAxis: xRowAxis,
+                        tdCount: tdCount
 					};
 				}();
 			};
 
+				// query
+			web.report.query = {};
+
+			web.report.query.sort = function(layout, xResponse) {
+				var id = layout.sorting.id,
+					direction = layout.sorting ? layout.sorting.direction : 'DESC',
+					index = xResponse.nameHeaderMap[id].index,
+					rows = xResponse.rows;
+
+				support.prototype.array.sort(rows, direction, index);
+
+				return xResponse;
+			};
+
+			web.report.query.format = function(str) {
+				var n = parseFloat(str);
+
+                // return string if
+                // - parsefloat(string) is not a number
+                // - string is just starting with a number
+                // - string is a valid date
+				if (!Ext.isNumber(n) || n != str || new Date(str).toString() !== 'Invalid Date') {
+					return str;
+				}
+
+                return n;
+			};
+
+			web.report.query.getHtml = function(layout, xResponse) {
+				var dimensionHeaders = xResponse.dimensionHeaders,
+					rows = xResponse.rows,
+                    names = xResponse.metaData.names,
+                    optionNames = xResponse.metaData.optionNames,
+                    pager = xResponse.metaData.pager,
+                    count = pager.page * pager.pageSize - pager.pageSize
+					tableCls = 'pivot',
+					html = '';
+
+				xResponse.sortableIdObjects = [];
+
+				tableCls += layout.displayDensity ? ' ' + layout.displayDensity : '';
+				tableCls += layout.fontSize ? ' ' + layout.fontSize : '';
+
+				html += '<table class="' + tableCls + '"><tr>';
+                html += '<td class="pivot-dim pivot-dim-subtotal">' + '#' + '</td>';
+
+				// get header indexes
+				for (var i = 0, header, uuid; i < dimensionHeaders.length; i++) {
+					header = dimensionHeaders[i];
+					uuid = Ext.data.IdGenerator.get('uuid').generate();
+
+					html += '<td id="' + uuid + '" class="pivot-dim td-sortable">' + header.column + '</td>';
+
+					xResponse.sortableIdObjects.push({
+						id: header.name,
+						uuid: uuid
+					});
+				}
+
+				html += '</tr>';
+
+				// rows
+				for (var i = 0, row; i < rows.length; i++) {
+					row = rows[i];
+					html += '<tr>';
+                    html += '<td class="pivot-value align-right">' + (count + (i + 1)) + '</td>';
+
+					for (var j = 0, str, header, name; j < dimensionHeaders.length; j++) {
+						header = dimensionHeaders[j];
+						str = row[header.index];
+                        //str = names.hasOwnProperty(str) ? names[str] : str;
+                        str = optionNames[header.name + str] || optionNames[str] || names[str] || str;
+						name = web.report.query.format(str);
+
+						//if (header.name === 'ouname' && layout.showHierarchy) {
+							//var a = Ext.Array.clean(name.split('/'));
+							//name = '';
+
+							//for (var k = 0, isLast; k < a.length; k++) {
+								//isLast = !!(i === a.length - 1);
+
+								//name += (!isLast ? '<span class="text-weak">' : '') + a[i] + (!isLast ? '</span>' : '') + (!isLast ? ' / ' : '');
+							//}
+						//}
+
+						html += '<td class="pivot-value align-left">' + name + '</td>';
+					}
+
+					html += '</tr>';
+				}
+
+				html += '</table>';
+
+				return {
+					html: html
+				};
+			};
+
 		}());
 
 		// extend init
@@ -2536,15 +3055,17 @@
 			}
 
 			// sort ouc
-			support.prototype.array.sort(init.user.ouc);
+			if (init.user && init.user.ouc) {
+				support.prototype.array.sort(init.user.ouc);
+			}
 
 			// legend set map
-			init.idLegendSetMap = {};
+			//init.idLegendSetMap = {};
 
-			for (var i = 0, set; i < init.legendSets.length; i++) {
-				set = init.legendSets[i];
-				init.idLegendSetMap[set.id] = set;
-			}
+			//for (var i = 0, set; i < init.legendSets.length; i++) {
+				//set = init.legendSets[i];
+				//init.idLegendSetMap[set.id] = set;
+			//}
 		}());
 
 		// instance
@@ -2579,10 +3100,15 @@
 	css += '.pivot-value-total { \n background-color: #e4e4e4; \n white-space: nowrap; \n text-align: right; \n } \n';
 	css += '.pivot-value-total-subgrandtotal { \n background-color: #d8d8d8; \n white-space: nowrap; \n text-align: right; \n } \n';
 	css += '.pivot-value-grandtotal { \n background-color: #c8c8c8; \n white-space: nowrap; \n text-align: right; \n } \n';
+    css += '.pivot-dim-label { \n background-color: #cddaed; \n white-space: nowrap; \n text-align: center; \n } \n';
+    css += '.pivot-empty { \n background-color: #cddaed; \n } \n';
+    css += '.pivot-transparent-column { \n background-color: #fff; \n border-top-color: #fff !important; \n border-right-color: #fff !important; \n } \n';
+    css += '.pivot-transparent-row { \n background-color: #fff; \n border-bottom-color: #fff !important; \n border-left-color: #fff !important; \n } \n';
 
-	css += '.x-mask-msg { \n padding: 0; \n	border: 0 none; \n background-image: none; \n background-color: transparent; \n } \n';
+    css += '.x-mask-msg { \n padding: 0; \n	border: 0 none; \n background-image: none; \n background-color: transparent; \n } \n';
 	css += '.x-mask-msg div { \n background-position: 11px center; \n } \n';
 	css += '.x-mask-msg .x-mask-loading { \n border: 0 none; \n	background-color: #000; \n color: #fff; \n border-radius: 2px; \n padding: 12px 14px 12px 30px; \n opacity: 0.65; \n } \n';
+    css += '.x-mask { opacity: 0 } \n';
 
 	css += '.pivot td.legend { \n padding: 0; \n } \n';
 	css += '.pivot div.legendCt { \n display: table; \n float: right; \n width: 100%; \n } \n';
@@ -2592,14 +3118,15 @@
 	css += '.pivot div.legendColor { \n display: table-cell; \n width: 2px; \n } \n';
 
 	css += '.pointer { \n cursor: pointer; \n } \n';
-	css += '.td-sortable { \n background-image: url("http://dhis2-cdn.org/v214/plugin/images/arrowupdown.png";); \n background-repeat: no-repeat; \n background-position: right center; \n padding-right: 15px !important; \n } \n';
+	css += '.td-sortable { \n background-image: url("http://dhis2-cdn.org/v217/plugin/images/arrowupdown.png";); \n background-repeat: no-repeat; \n background-position: right center; \n padding-right: 15px !important; \n } \n';
 
 	Ext.util.CSS.createStyleSheet(css);
 
-	PT.plugin = {};
+	ER.plugin = {};
 
 	var init = {
-			user: {}
+			user: {},
+            systemInfo: {}
 		},
 		configs = [],
 		isInitStarted = false,
@@ -2607,14 +3134,16 @@
 		getInit,
 		execute;
 
-	getInit = function(url) {
+	getInit = function(contextPath) {
 		var isInit = false,
 			requests = [],
-			callbacks = 0,
+			callbackCount = 0,
 			fn;
 
+        init.contextPath = contextPath;
+
 		fn = function() {
-			if (++callbacks === requests.length) {
+			if (++callbackCount === requests.length) {
 				isInitComplete = true;
 
 				for (var i = 0; i < configs.length; i++) {
@@ -2625,16 +3154,120 @@
 			}
 		};
 
-		requests.push({
-			url: url + '/api/system/context.jsonp',
-			success: function(r) {
-				init.contextPath = r.contextPath;
-				fn();
-			}
-		});
-
-		requests.push({
-			url: url + '/api/organisationUnits.jsonp?userOnly=true&viewClass=detailed&paging=false&links=false',
+        // date, calendar
+        requests.push({
+            url: contextPath + '/api/systemSettings.jsonp?key=keyCalendar&key=keyDateFormat',
+            success: function(r) {
+                var systemSettings = r;
+                init.systemInfo.dateFormat = Ext.isString(systemSettings.keyDateFormat) ? systemSettings.keyDateFormat.toLowerCase() : 'yyyy-mm-dd';
+                init.systemInfo.calendar = systemSettings.keyCalendar;
+
+                // user-account
+                Ext.data.JsonP.request({
+                    url: contextPath + '/api/me/user-account.jsonp',
+                    success: function(r) {
+                        init.userAccount = r;
+
+                        Ext.Loader.injectScriptElement(contextPath + '/dhis-web-commons/javascripts/jQuery/jquery.min.js', function() {
+                            Ext.Loader.injectScriptElement(contextPath + '/dhis-web-commons/javascripts/dhis2/dhis2.util.js', function() {
+                                Ext.Loader.injectScriptElement(contextPath + '/dhis-web-commons/javascripts/dhis2/dhis2.storage.js', function() {
+                                    Ext.Loader.injectScriptElement(contextPath + '/dhis-web-commons/javascripts/dhis2/dhis2.storage.idb.js', function() {
+                                        Ext.Loader.injectScriptElement(contextPath + '/dhis-web-commons/javascripts/dhis2/dhis2.storage.ss.js', function() {
+                                            Ext.Loader.injectScriptElement(contextPath + '/dhis-web-commons/javascripts/dhis2/dhis2.storage.memory.js', function() {
+
+                                                // init
+                                                var defaultKeyUiLocale = 'en',
+                                                    defaultKeyAnalysisDisplayProperty = 'name',
+                                                    namePropertyUrl,
+                                                    contextPath,
+                                                    keyUiLocale,
+                                                    dateFormat;
+
+                                                init.userAccount.settings.keyUiLocale = init.userAccount.settings.keyUiLocale || defaultKeyUiLocale;
+                                                init.userAccount.settings.keyAnalysisDisplayProperty = init.userAccount.settings.keyAnalysisDisplayProperty || defaultKeyAnalysisDisplayProperty;
+
+                                                // local vars
+                                                contextPath = init.contextPath;
+                                                keyUiLocale = init.userAccount.settings.keyUiLocale;
+                                                keyAnalysisDisplayProperty = init.userAccount.settings.keyAnalysisDisplayProperty;
+                                                namePropertyUrl = keyAnalysisDisplayProperty === defaultKeyAnalysisDisplayProperty ? keyAnalysisDisplayProperty : keyAnalysisDisplayProperty + '|rename(' + defaultKeyAnalysisDisplayProperty + ')';
+                                                dateFormat = init.systemInfo.dateFormat;
+
+                                                init.namePropertyUrl = namePropertyUrl;
+
+                                                // dhis2
+                                                dhis2.util.namespace('dhis2.er');
+
+                                                dhis2.er.store = dhis2.er.store || new dhis2.storage.Store({
+                                                    name: 'dhis2',
+                                                    adapters: [dhis2.storage.IndexedDBAdapter, dhis2.storage.DomSessionStorageAdapter, dhis2.storage.InMemoryAdapter],
+                                                    objectStores: ['optionSets']
+                                                });
+
+                                                // option sets
+                                                Ext.data.JsonP.request({
+                                                    url: contextPath + '/api/optionSets.jsonp?fields=id,version&paging=false',
+                                                    success: function(r) {
+                                                        var optionSets = r.optionSets || [],
+                                                            store = dhis2.er.store,
+                                                            ids = [],
+                                                            url = '',
+                                                            callbacks = 0,
+                                                            checkOptionSet,
+                                                            updateStore;
+
+                                                        updateStore = function() {
+                                                            if (++callbacks === optionSets.length) {
+                                                                if (!ids.length) {
+                                                                    fn();
+                                                                    return;
+                                                                }
+
+                                                                for (var i = 0; i < ids.length; i++) {
+                                                                    url += '&filter=id:eq:' + ids[i];
+                                                                }
+
+                                                                Ext.data.JsonP.request({
+                                                                    url: contextPath + '/api/optionSets.jsonp?fields=id,name,version,options[code,name]&paging=false' + url,
+                                                                    success: function(r) {
+                                                                        var sets = r.optionSets;
+
+                                                                        store.setAll('optionSets', sets).done(fn);
+                                                                    }
+                                                                });
+                                                            }
+                                                        };
+
+                                                        registerOptionSet = function(optionSet) {
+                                                            store.get('optionSets', optionSet.id).done( function(obj) {
+                                                                if (!Ext.isObject(obj) || obj.version !== optionSet.version) {
+                                                                    ids.push(optionSet.id);
+                                                                }
+
+                                                                updateStore();
+                                                            });
+                                                        };
+
+                                                        store.open().done( function() {
+                                                            for (var i = 0; i < optionSets.length; i++) {
+                                                                registerOptionSet(optionSets[i]);
+                                                            }
+                                                        });
+                                                    }
+                                                });
+                                            });
+                                        });
+                                    });
+                                });
+                            });
+                        });
+                    }
+                });
+            }
+        });
+
+		requests.push({
+			url: contextPath + '/api/organisationUnits.jsonp?userOnly=true&fields=id,name,children[id,name]&paging=false',
 			success: function(r) {
 				var organisationUnits = r.organisationUnits || [],
                     ou = [],
@@ -2651,10 +3284,9 @@
                         }
                     }
 
-                    init.user = {
-                        ou: ou,
-                        ouc: ouc
-                    }
+                    init.user = init.user || {};
+                    init.user.ou = ou;
+                    init.user.ouc = ouc;
                 }
                 else {
                     alert('User is not assigned to any organisation units');
@@ -2664,22 +3296,19 @@
 			}
 		});
 
-		requests.push({
-			url: url + '/api/mapLegendSets.jsonp?viewClass=detailed&links=false&paging=false',
-			success: function(r) {
-				init.legendSets = r.mapLegendSets;
-				fn();
-			}
-		});
+        init.legendSets = [];
 
 		requests.push({
-			url: url + '/api/dimensions.jsonp?links=false&paging=false',
+			url: contextPath + '/api/dimensions.jsonp?links=false&paging=false',
 			success: function(r) {
 				init.dimensions = r.dimensions;
 				fn();
 			}
 		});
 
+        // option sets
+        requests.push();
+
 		for (var i = 0; i < requests.length; i++) {
 			Ext.data.JsonP.request(requests[i]);
 		}
@@ -2697,7 +3326,7 @@
 
 		validateConfig = function(config) {
 			if (!Ext.isObject(config)) {
-				console.log('Report table configuration is not an object');
+				console.log('Event report configuration is not an object');
 				return;
 			}
 
@@ -2713,46 +3342,54 @@
 
         extendInstance = function(pt) {
             var init = ns.core.init,
+                conf = ns.core.conf,
 				api = ns.core.api,
 				support = ns.core.support,
 				service = ns.core.service,
-				web = ns.core.web;
+				web = ns.core.web,
+                dimConf = conf.finals.dimension;
 
 			// mouse events
 			web.events = web.events || {};
 
-			web.events.setColumnHeaderMouseHandlers = function(xLayout, response) {
-				if (Ext.isArray(xLayout.sortableIdObjects)) {
-					for (var i = 0, obj, el; i < xLayout.sortableIdObjects.length; i++) {
-						obj = xLayout.sortableIdObjects[i];
+			web.events.setColumnHeaderMouseHandlers = function(layout, response, xResponse) {
+				if (Ext.isArray(xResponse.sortableIdObjects)) {
+					for (var i = 0, obj, el; i < xResponse.sortableIdObjects.length; i++) {
+						obj = xResponse.sortableIdObjects[i];
 						el = Ext.get(obj.uuid);
 
-						el.dom.xLayout = xLayout;
+						el.dom.layout = layout;
 						el.dom.response = response;
+						el.dom.xResponse = xResponse;
 						el.dom.metaDataId = obj.id;
 						el.dom.onColumnHeaderMouseClick = web.events.onColumnHeaderMouseClick;
 						el.dom.onColumnHeaderMouseOver = web.events.onColumnHeaderMouseOver;
 						el.dom.onColumnHeaderMouseOut = web.events.onColumnHeaderMouseOut;
 
-						el.dom.setAttribute('onclick', 'this.onColumnHeaderMouseClick(this.xLayout, this.response, this.metaDataId)');
+						el.dom.setAttribute('onclick', 'this.onColumnHeaderMouseClick(this.layout, this.response, this.metaDataId)');
 						el.dom.setAttribute('onmouseover', 'this.onColumnHeaderMouseOver(this)');
 						el.dom.setAttribute('onmouseout', 'this.onColumnHeaderMouseOut(this)');
 					}
 				}
 			};
 
-			web.events.onColumnHeaderMouseClick = function(xLayout, response, id) {
-				if (xLayout.sorting && xLayout.sorting.id === id) {
-					xLayout.sorting.direction = support.prototype.str.toggleDirection(xLayout.sorting.direction);
+			web.events.onColumnHeaderMouseClick = function(layout, response, id) {
+				if (layout.sorting && layout.sorting.id === id) {
+					layout.sorting.direction = support.prototype.str.toggleDirection(layout.sorting.direction);
 				}
 				else {
-					xLayout.sorting = {
+					layout.sorting = {
 						id: id,
-						direction: 'DESC'
+						direction: 'ASC'
 					};
 				}
 
-				ns.core.web.pivot.sort(xLayout, response, id);
+                if (layout.dataType === 'aggregated_values') {
+                    web.report.createReport(layout, response);
+                }
+                else if (layout.dataType === 'individual_cases') {
+                    web.report.getData(layout);
+                }
 			};
 
 			web.events.onColumnHeaderMouseOver = function(el) {
@@ -2763,116 +3400,247 @@
 				Ext.get(el).removeCls('pointer highlighted');
 			};
 
-			// pivot
-			web.pivot = web.pivot || {};
+			// report
+			web.report = web.report || {};
 
-            web.pivot.loadTable = function(id) {
+			web.report.loadReport = function(id) {
 				if (!Ext.isString(id)) {
-					alert('Invalid report table id');
+					alert('Invalid event report id');
 					return;
 				}
 
 				Ext.data.JsonP.request({
-					url: init.contextPath + '/api/eventReports/' + id + '.jsonp?viewClass=dimensional&links=false',
+					url: init.contextPath + '/api/eventReports/' + id + '.jsonp?fields=' + conf.url.analysisFields.join(','),
 					failure: function(r) {
-						window.open(init.contextPath + '/api/eventReports/' + id + '.json?viewClass=dimensional&links=false', '_blank');
+						window.open(init.contextPath + '/api/eventReports/' + id + '.json?fields=' + conf.url.analysisFields.join(','), '_blank');
 					},
 					success: function(r) {
 						var layout = api.layout.Layout(r);
 
 						if (layout) {
-							web.pivot.getData(layout, true);
+							web.report.getData(layout, true);
 						}
 					}
 				});
 			};
 
-			web.pivot.getData = function(layout, isUpdateGui) {
-				var xLayout,
-					paramString;
-
-				if (!layout) {
-					return;
-				}
-
-				xLayout = service.layout.getExtendedLayout(layout);
-				paramString = web.analytics.getParamString(xLayout, true);
+			web.report.getData = function(view, isUpdateGui) {
+				var paramString = web.analytics.getParamString(view, 'jsonp');
 
 				// show mask
 				web.mask.show(ns.app.centerRegion);
 
 				Ext.data.JsonP.request({
-					url: init.contextPath + '/api/analytics.jsonp' + paramString,
-					timeout: 60000,
-					headers: {
-						'Content-Type': 'application/json',
-						'Accepts': 'application/json'
-					},
+					url: ns.core.init.contextPath + paramString,
 					disableCaching: false,
+					scope: this,
 					failure: function(r) {
 						web.mask.hide(ns.app.centerRegion);
 
-						window.open(init.contextPath + '/api/analytics.json' + paramString, '_blank');
+                        console.log(r.status + '\n' + r.statusText + '\n' + r.responseText);
 					},
 					success: function(r) {
-						var response = api.response.Response(r);
-
-						if (!response) {
-							web.mask.hide(ns.app.centerRegion);
-							return;
-						}
-
-						// sync xLayout with response
-						xLayout = service.layout.getSyncronizedXLayout(xLayout, response);
-
-						if (!xLayout) {
-							web.mask.hide(ns.app.centerRegion);
-							return;
-						}
-
-						ns.app.paramString = paramString;
-
-						web.pivot.createTable(layout, xLayout, response, isUpdateGui);
+                        var response = api.response.Response(r);
+
+                        if (!response) {
+							web.mask.hide(ns.app.centerRegion);
+							return;
+						}
+
+                        // add to dimConf, TODO
+                        for (var i = 0, map = dimConf.objectNameMap, header; i < response.headers.length; i++) {
+                            header = response.headers[i];
+                            map[header.name] = map[header.name] || {
+                                id: header.name,
+                                dimensionName: header.name,
+                                name: header.column
+                            };
+                        }
+
+                        ns.app.paramString = paramString;
+
+                        web.report.createReport(view, response, isUpdateGui);
 					}
 				});
 			};
 
-			web.pivot.createTable = function(layout, xLayout, response, isUpdateGui) {
-				var xResponse,
-					xColAxis,
-					xRowAxis,
-					config;
-
-				if (!xLayout) {
-					xLayout = service.layout.getExtendedLayout(layout);
-				}
-
-				// extend response
-				xResponse = service.response.getExtendedResponse(xLayout, response);
-
-				// extended axes
-				xColAxis = service.layout.getExtendedAxis(xLayout, xResponse, 'col');
-				xRowAxis = service.layout.getExtendedAxis(xLayout, xResponse, 'row');
-
-				// update viewport
-				config = web.pivot.getHtml(xLayout, xResponse, xColAxis, xRowAxis);
-				ns.app.centerRegion.update(config.html);
-
-				// after render
-				ns.app.layout = layout;
-				ns.app.xLayout = xLayout;
-				ns.app.response = response;
-				ns.app.xResponse = xResponse;
-				ns.app.uuidDimUuidsMap = config.uuidDimUuidsMap;
-				ns.app.uuidObjectMap = Ext.applyIf((xColAxis ? xColAxis.uuidObjectMap : {}), (xRowAxis ? xRowAxis.uuidObjectMap : {}));
-
-				// sorting
-				web.events.setColumnHeaderMouseHandlers(xLayout, response);
-
-				web.mask.hide(ns.app.centerRegion);
+			web.report.createReport = function(layout, response, isUpdateGui) {
+				var map = {},
+                    getOptionSets;
+
+                getOptionSets = function(xResponse, callbackFn) {
+                    var optionSetHeaders = [];
+
+                    for (var i = 0; i < xResponse.headers.length; i++) {
+                        if (Ext.isString(xResponse.headers[i].optionSet)) {
+                            optionSetHeaders.push(xResponse.headers[i]);
+                        }
+                    }
+
+                    if (optionSetHeaders.length) {
+                        var callbacks = 0,
+                            optionMap = {},
+                            getOptions,
+                            fn;
+
+                        fn = function() {
+                            if (++callbacks === optionSetHeaders.length) {
+                                xResponse.metaData.optionNames = optionMap;
+                                callbackFn();
+                            }
+                        };
+
+                        getOptions = function(optionSetId, dataElementId) {
+                            dhis2.er.store.get('optionSets', optionSetId).done( function(obj) {
+                                Ext.apply(optionMap, support.prototype.array.getObjectMap(obj.options, 'code', 'name', dataElementId));
+                                fn();
+                            });
+                        };
+
+                        // execute
+                        for (var i = 0, header, optionSetId, dataElementId; i < optionSetHeaders.length; i++) {
+                            header = optionSetHeaders[i];
+                            optionSetId = header.optionSet;
+                            dataElementId = header.name;
+
+                            getOptions(optionSetId, dataElementId);
+                        }
+                    }
+                    else {
+                        callbackFn();
+                    }
+                };
+
+				map['aggregated_values'] = function() {
+					var xLayout,
+                        xResponse,
+						xColAxis,
+						xRowAxis,
+						table,
+						getSXLayout,
+						getXResponse,
+                        getReport;
+
+                    getReport = function() {
+                        var getHtml = function(xLayout, xResponse) {
+                            xColAxis = service.layout.getExtendedAxis(xLayout, 'col');
+                            xRowAxis = service.layout.getExtendedAxis(xLayout, 'row');
+
+                            return web.report.aggregate.getHtml(xLayout, xResponse, xColAxis, xRowAxis);
+                        };
+
+                        table = getHtml(xLayout, xResponse);
+
+                        if (table.tdCount > 20000 || (layout.hideEmptyRows && table.tdCount > 10000)) {
+                            alert('Table has too many cells. Please reduce the table and try again.');
+                            web.mask.hide(ns.app.centerRegion);
+                            return;
+                        }
+
+                        if (layout.sorting) {
+                            xResponse = web.report.aggregate.sort(xLayout, xResponse, xColAxis);
+                            xLayout = service.layout.getSyncronizedXLayout(layout, xLayout, xResponse);
+                            table = getHtml(xLayout, xResponse);
+                        }
+
+                        //ns.app.centerRegion.removeAll(true);
+                        ns.app.centerRegion.update(table.html);
+
+                        Ext.defer( function() {
+                            Ext.get(ns.core.init.el).fadeIn({
+                                duration: 400
+                            });
+                        }, 300 );
+
+                        // after render
+                        ns.app.layout = layout;
+                        ns.app.xLayout = xLayout;
+                        ns.app.response = response;
+                        ns.app.xResponse = xResponse;
+                        ns.app.xColAxis = xColAxis;
+                        ns.app.xRowAxis = xRowAxis;
+                        ns.app.uuidDimUuidsMap = table.uuidDimUuidsMap;
+                        ns.app.uuidObjectMap = Ext.applyIf((xColAxis ? xColAxis.uuidObjectMap : {}), (xRowAxis ? xRowAxis.uuidObjectMap : {}));
+
+                        if (ER.isSessionStorage) {
+                            web.events.setColumnHeaderMouseHandlers(layout, response, xResponse);
+                        }
+
+                        web.mask.hide(ns.app.centerRegion);
+
+                        if (ER.isDebug) {
+                            console.log("Number of cells", table.tdCount);
+                            console.log("layout", layout);
+                            console.log("response", response);
+                            console.log("xResponse", xResponse);
+                            console.log("xLayout", xLayout);
+                            console.log("core", ns.core);
+                            console.log("app", ns.app);
+                        }
+                    };
+
+                    getSXLayout = function() {
+                        xLayout = service.layout.getSyncronizedXLayout(layout, xLayout, xResponse);
+
+                        getReport();
+                    };
+
+                    getXResponse = function() {
+                        xLayout = service.layout.getExtendedLayout(layout);
+                        xResponse = service.response.aggregate.getExtendedResponse(xLayout, response);
+
+                        getOptionSets(xResponse, getSXLayout);
+                    };
+
+                    // execute
+					response = response || ns.app.response;
+
+                    getXResponse();
+				};
+
+				map['individual_cases'] = function() {
+					var xResponse,
+                        getReport;
+
+                    getReport = function() {
+                        table = web.report.query.getHtml(layout, xResponse);
+
+                        //if (layout.sorting) {
+                            //xResponse = web.report.query.sort(layout, xResponse);
+                            //table = web.report.query.getHtml(layout, xResponse);
+                        //}
+
+                        ns.app.centerRegion.removeAll(true);
+                        ns.app.centerRegion.update(table.html);
+
+                        Ext.defer( function() {
+                            Ext.get(ns.core.init.el).fadeIn({
+                                duration: 400
+                            });
+                        }, 300 );
+
+                        // after render
+                        ns.app.layout = layout;
+                        ns.app.response = response;
+                        ns.app.xResponse = xResponse;
+
+                        if (ER.isSessionStorage) {
+                            web.events.setColumnHeaderMouseHandlers(layout, response, xResponse);
+                        }
+
+                        web.mask.hide(ns.app.centerRegion);
+                    };
+
+                    // execute
+                    xResponse = service.response.query.getExtendedResponse(layout, response);
+
+                    getOptionSets(xResponse, getReport);
+				};
+
+				map[layout.dataType]();
 			};
 
-			web.pivot.sort = function(xLayout, response, id) {
+			web.report.sort = function(xLayout, response, id) {
 				var xLayout = Ext.clone(xLayout),
 					response = Ext.clone(response),
 					dim = xLayout.rows[0],
@@ -2903,9 +3671,9 @@
 				layout = api.layout.Layout(xLayout);
 
 				// re-create table
-				web.pivot.createTable(layout, null, response, false);
+				web.report.createReport(layout, null, response, false);
 			};
-		};
+        };
 
 		createViewport = function() {
 			return {
@@ -2918,14 +3686,16 @@
 				return;
 			}
 
-			ns.core = PT.getCore(Ext.clone(init));
+			ns.core = ER.getCore(Ext.clone(init));
+            ns.core.init.el = config.el;
+            Ext.get(ns.core.init.el).setStyle('opacity', 0);
 			extendInstance(ns);
 
 			ns.app.viewport = createViewport();
 			ns.app.centerRegion = ns.app.viewport.centerRegion;
 
 			if (config.id) {
-				ns.core.web.pivot.loadTable(config.id);
+				ns.core.web.report.loadReport(config.id);
 			}
 			else {
 				layout = ns.core.api.layout.Layout(config);
@@ -2934,12 +3704,12 @@
 					return;
 				}
 
-				ns.core.web.pivot.getData(layout);
+				ns.core.web.report.getData(layout);
 			}
 		}();
 	};
 
-	PT.plugin.getTable = function(config) {
+	ER.plugin.getEventReport = function(config) {
 		if (Ext.isString(config.url) && config.url.split('').pop() === '/') {
 			config.url = config.url.substr(0, config.url.length - 1);
 		}
@@ -2958,5 +3728,5 @@
 	};
 
 	DHIS = Ext.isObject(window['DHIS']) ? DHIS : {};
-	DHIS.getTable = PT.plugin.getTable;
+	DHIS.getEventReport = ER.plugin.getEventReport;
 });

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/styles/style.css'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/styles/style.css	2014-10-22 16:41:32 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-reports/styles/style.css	2014-11-03 11:47:50 +0000
@@ -259,9 +259,18 @@
 	white-space: nowrap;
     text-align: center;
 }
-
 .pivot-empty {
-	background-color: #dae6f8;
+	background-color: #cddaed;
+}
+.pivot-transparent-column {
+	background-color: #fff;
+    border-top-color: #fff !important;
+    border-right-color: #fff !important;
+}
+.pivot-transparent-row {
+	background-color: #fff;
+    border-bottom-color: #fff !important;
+    border-left-color: #fff !important;
 }
 
 .pivot-value {

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-mapping/scripts/plugin.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-mapping/scripts/plugin.js	2014-09-01 19:20:31 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-mapping/scripts/plugin.js	2014-11-03 11:47:50 +0000
@@ -389,12 +389,15 @@
 	Ext.define("GeoExt.data.LayerModel",{alternateClassName:"GeoExt.data.LayerRecord",extend:"Ext.data.Model",requires:["Ext.data.proxy.Memory","Ext.data.reader.Json"],alias:"model.gx_layer",statics:{createFromLayer:function(a){return this.proxy.reader.readRecords([a]).records[0]}},fields:["id",{name:"title",type:"string",mapping:"name"},{name:"legendURL",type:"string",mapping:"metadata.legendURL"},{name:"hideTitle",type:"bool",mapping:"metadata.hideTitle"},{name:"hideInLegend",type:"bool",mapping:"metadata.hideInLegend"}],proxy:{type:"memory",reader:{type:"json"}},getLayer:function(){return this.raw}});Ext.define("GeoExt.data.LayerStore",{requires:["GeoExt.data.LayerModel"],extend:"Ext.data.Store",model:"GeoExt.data.LayerModel",statics:{MAP_TO_STORE:1,STORE_TO_MAP:2},map:null,constructor:function(b){var c=this;b=Ext.apply({},b);var d=(GeoExt.MapPanel&&b.map instanceof GeoExt.MapPanel)?b.map.map:b.map;delete b.map;if(b.layers){b.data=b.layers}delete b.layers;var a={initDir:b.initDir};delete b.initDir;c.callParent([b]);if(d){this.bind(d,a)}},bind:function(e,a){var b=this;if(b.map){return}b.map=e;a=Ext.apply({},a);var c=a.initDir;if(a.initDir==undefined){c=GeoExt.data.LayerStore.MAP_TO_STORE|GeoExt.data.LayerStore.STORE_TO_MAP}var d=e.layers.slice(0);if(c&GeoExt.data.LayerStore.STORE_TO_MAP){b.each(function(f){b.map.addLayer(f.getLayer())},b)}if(c&GeoExt.data.LayerStore.MAP_TO_STORE){b.loadRawData(d,true)}e.events.on({changelayer:b.onChangeLayer,addlayer:b.onAddLayer,removelayer:b.onRemoveLayer,scope:b});b.on({load:b.onLoad,clear:b.onClear,add:b.onAdd,remove:b.onRemove,update:b.onUpdate,scope:b});b.data.on({replace:b.onReplace,scope:b});b.fireEvent("bind",b,e)},unbind:function(){var a=this;if(a.map){a.map.events.un({changelayer:a.onChangeLayer,addlayer:a.onAddLayer,removelayer:a.onRemoveLayer,scope:a});a.un("load",a.onLoad,a);a.un("clear",a.onClear,a);a.un("add",a.onAdd,a);a.un("remove",a.onRemove,a);a.data.un("replace",a.onReplace,a);a.map=null}},onChangeLayer:function(b){var e=b.layer;var c=this.findBy(function(f,g){return f.getLayer()===e});if(c>-1){var a=this.getAt(c);if(b.property==="order"){if(!this._adding&&!this._removing){var d=this.map.getLayerIndex(e);if(d!==c){this._removing=true;this.remove(a);delete this._removing;this._adding=true;this.insert(d,[a]);delete this._adding}}}else{if(b.property==="name"){a.set("title",e.name)}else{this.fireEvent("update",this,a,Ext.data.Record.EDIT)}}}},onAddLayer:function(b){var c=this;if(!c._adding){c._adding=true;var a=c.proxy.reader.read(b.layer);c.add(a.records);delete c._adding}},onRemoveLayer:function(a){if(this.map.unloadDestroy){if(!this._removing){var b=a.layer;this._removing=true;this.remove(this.getByLayer(b));delete this._removing}}else{this.unbind()}},onLoad:function(c,b,g){if(g){if(!Ext.isArray(b)){b=[b]}if(!this._addRecords){this._removing=true;for(var e=this.map.layers.length-1;e>=0;e--){this.map.removeLayer(this.map.layers[e])}delete this._removing}var a=b.length;if(a>0){var f=new Array(a);for(var d=0;d<a;d++){f[d]=b[d].getLayer()}this._adding=true;this.map.addLayers(f);delete this._adding}}delete this._addRecords},onClear:function(a){this._removing=true;for(var b=this.map.layers.length-1;b>=0;b--){this.map.removeLayer(this.map.layers[b])}delete this._removing},onAdd:function(b,a,c){if(!this._adding){this._adding=true;var e;for(var d=a.length-1;d>=0;--d){e=a[d].getLayer();this.map.addLayer(e);if(c!==this.map.layers.length-1){this.map.setLayerIndex(e,c)}}delete this._adding}},onRemove:function(b,a,c){if(!this._removing){var d=a.getLayer();if(this.map.getLayer(d.id)!=null){this._removing=true;this.removeMapLayer(a);delete this._removing}}},onUpdate:function(c,a,b){if(b===Ext.data.Record.EDIT){if(a.modified&&a.modified.title){var d=a.getLayer();var e=a.get("title");if(e!==d.name){d.setName(e)}}}},removeMapLayer:function(a){this.map.removeLayer(a.getLayer())},onReplace:function(c,a,b){this.removeMapLayer(a)},getByLayer:function(b){var a=this.findBy(function(c){return c.getLayer()===b});if(a>-1){return this.getAt(a)}},destroy:function(){var a=this;a.unbind();a.callParent()},loadRecords:function(a,b){if(b&&b.addRecords){this._addRecords=true}this.callParent(arguments)}});Ext.define("GeoExt.panel.Map",{extend:"Ext.panel.Panel",requires:["GeoExt.data.LayerStore"],alias:"widget.gx_mappanel",alternateClassName:"GeoExt.MapPanel",statics:{guess:function(){var a=Ext.ComponentQuery.query("gx_mappanel");return((a&&a.length>0)?a[0]:null)}},center:null,zoom:null,extent:null,prettyStateKeys:false,map:null,layers:null,stateEvents:["aftermapmove","afterlayervisibilitychange","afterlayeropacitychange","afterlayerorderchange","afterlayernamechange","afterlayeradd","afterlayerremove"],initComponent:function(){if(!(this.map instanceof OpenLayers.Map)){this.map=new OpenLayers.Map(Ext.applyIf(this.map||{},{allOverlays:true}))}var a=this.layers;if(!a||a instanceof Array){this.layers=Ext.create("GeoExt.data.LayerStore",{layers:a,map:this.map.layers.length>0?this.map:null})}if(Ext.isString(this.center)){this.center=OpenLayers.LonLat.fromString(this.center)}else{if(Ext.isArray(this.center)){this.center=new OpenLayers.LonLat(this.center[0],this.center[1])}}if(Ext.isString(this.extent)){this.extent=OpenLayers.Bounds.fromString(this.extent)}else{if(Ext.isArray(this.extent)){this.extent=OpenLayers.Bounds.fromArray(this.extent)}}this.callParent(arguments);this.on("resize",this.onResize,this);this.on("afterlayout",function(){if(typeof this.map.getViewport==="function"){this.items.each(function(b){if(typeof b.addToMapPanel==="function"){b.getEl().appendTo(this.map.getViewport())}},this)}},this);this.map.events.on({moveend:this.onMoveend,changelayer:this.onChangelayer,addlayer:this.onAddlayer,removelayer:this.onRemovelayer,scope:this})},onMoveend:function(a){this.fireEvent("aftermapmove",this,this.map,a)},onChangelayer:function(b){var a=this.map;if(b.property){if(b.property==="visibility"){this.fireEvent("afterlayervisibilitychange",this,a,b)}else{if(b.property==="order"){this.fireEvent("afterlayerorderchange",this,a,b)}else{if(b.property==="nathis"){this.fireEvent("afterlayernathischange",this,a,b)}else{if(b.property==="opacity"){this.fireEvent("afterlayeropacitychange",this,a,b)}}}}}},onAddlayer:function(){this.fireEvent("afterlayeradd")},onRemovelayer:function(){this.fireEvent("afterlayerremove")},onResize:function(){var a=this.map;if(this.body.dom!==a.div){a.render(this.body.dom);this.layers.bind(a);if(a.layers.length>0){this.setInitialExtent()}else{this.layers.on("add",this.setInitialExtent,this,{single:true})}}else{a.updateSize()}},setInitialExtent:function(){var a=this.map;if(!a.getCenter()){if(this.center||this.zoom){a.setCenter(this.center,this.zoom)}else{if(this.extent instanceof OpenLayers.Bounds){a.zoomToExtent(this.extent,true)}else{a.zoomToMaxExtent()}}}},getState:function(){var c=this,e=c.map,d=c.callParent(arguments)||{},b;if(!e){return}var a=e.getCenter();a&&Ext.applyIf(d,{x:a.lon,y:a.lat,zoom:e.getZoom()});c.layers.each(function(f){b=f.getLayer();layerId=this.prettyStateKeys?f.get("title"):f.get("id");d=c.addPropertyToState(d,"visibility_"+layerId,b.getVisibility());d=c.addPropertyToState(d,"opacity_"+layerId,(b.opacity===null)?1:b.opacity)},c);return d},applyState:function(a){var j=this;map=j.map;j.center=new OpenLayers.LonLat(a.x,a.y);j.zoom=a.zoom;var f,c,g,d,b,h;var e=map.layers;for(f=0,c=e.length;f<c;f++){g=e[f];d=j.prettyStateKeys?g.name:g.id;b=a["visibility_"+d];if(b!==undefined){b=(/^true$/i).test(b);if(g.isBaseLayer){if(b){map.setBaseLayer(g)}}else{g.setVisibility(b)}}h=a["opacity_"+d];if(h!==undefined){g.setOpacity(h)}}},onBeforeAdd:function(a){if(Ext.isFunction(a.addToMapPanel)){a.addToMapPanel(this)}this.callParent(arguments)},beforeDestroy:function(){if(this.map&&this.map.events){this.map.events.un({moveend:this.onMoveend,changelayer:this.onChangelayer,scope:this})}if(!this.initialConfig.map||!(this.initialConfig.map instanceof OpenLayers.Map)){if(this.map&&this.map.destroy){this.map.destroy()}}delete this.map;this.callParent(arguments)}});Ext.define("GeoExt.tree.Column",{extend:"Ext.tree.Column",alias:"widget.gx_treecolumn",initComponent:function(){var b=this;b.callParent();var a=b.renderer;this.renderer=function(i,e,d,h,j,f,c){var g=[a(i,e,d,h,j,f,c)];if(d.get("checkedGroup")){g[0]=g[0].replace(/class="([^-]+)-tree-checkbox([^"]+)?"/,'class="$1-tree-checkbox$2 gx-tree-radio"')}g.push('<div class="gx-tree-component gx-tree-component-off" id="tree-record-'+d.id+'"></div>');if(d.uiProvider&&d.uiProvider instanceof "string"){}return g.join("")}},defaultRenderer:function(a){return a}});Ext.define("GeoExt.tree.View",{extend:"Ext.tree.View",alias:"widget.gx_treeview",initComponent:function(){var a=this;a.on("itemupdate",this.onItem,this);a.on("itemadd",this.onItem,this);a.on("createchild",this.createChild,this);return a.callParent(arguments)},onItem:function(a,c,f,b){var e=this;if(!(a instanceof Array)){a=[a]}for(var d=0;d<a.length;d++){this.onNodeRendered(a[d])}},onNodeRendered:function(c){var b=this;var a=Ext.get("tree-record-"+c.id);if(!a){return}if(c.get("layer")){b.fireEvent("createchild",a,c)}if(c.hasChildNodes()){c.eachChild(function(d){b.onNodeRendered(d)},b)}},createChild:function(b,c){var a=c.get("component");if(a){cmpObj=Ext.ComponentManager.create(a);cmpObj.render(b);b.removeCls("gx-tree-component-off")}}});Ext.define("GeoExt.tree.LayerNode",{extend:"Ext.AbstractPlugin",alias:"plugin.gx_layer",init:function(b){this.target=b;var a=b.get("layer");b.set("checked",a.getVisibility());if(!b.get("checkedGroup")&&a.isBaseLayer){b.set("checkedGroup","gx_baselayer")}b.set("fixedText",!!b.text);b.set("leaf",true);if(!b.get("iconCls")){b.set("iconCls","gx-tree-layer-icon")}b.on("afteredit",this.onAfterEdit,this);a.events.on({visibilitychanged:this.onLayerVisibilityChanged,scope:this})},onAfterEdit:function(c,a){var b=this;if(~Ext.Array.indexOf(a,"checked")){b.onCheckChange()}},onLayerVisibilityChanged:function(){if(!this._visibilityChanging){this.target.set("checked",this.target.get("layer").getVisibility())}},onCheckChange:function(){var c=this.target,b=this.target.get("checked");if(b!=c.get("layer").getVisibility()){c._visibilityChanging=true;var a=c.get("layer");if(b&&a.isBaseLayer&&a.map){a.map.setBaseLayer(a)}else{a.setVisibility(b)}delete c._visibilityChanging}}});Ext.define("GeoExt.tree.LayerLoader",{extend:"Ext.util.Observable",requires:["GeoExt.tree.LayerNode"],store:null,filter:function(a){return a.getLayer().displayInLayerSwitcher===true},baseAttrs:null,load:function(a){if(this.fireEvent("beforeload",this,a)){this.removeStoreHandlers();while(a.firstChild){a.removeChild(a.firstChild)}if(!this.store){this.store=GeoExt.MapPanel.guess().layers}this.store.each(function(b){this.addLayerNode(a,b)},this);this.addStoreHandlers(a);this.fireEvent("load",this,a)}},onStoreAdd:function(b,a,c,f){if(!this._reordering){var g=f.get("container").recordIndexToNodeIndex(c+a.length-1,f);for(var d=0,e=a.length;d<e;++d){this.addLayerNode(f,a[d],g)}}},onStoreRemove:function(a,b){if(!this._reordering){this.removeLayerNode(b,a)}},addLayerNode:function(d,a,b){b=b||0;if(this.filter(a)===true){var c=a.getLayer();var e=this.createNode({plugins:[{ptype:"gx_layer"}],layer:c,text:c.name,listeners:{move:this.onChildMove,scope:this}});if(b!==undefined){d.insertChild(b,e)}else{d.appendChild(e)}d.getChildAt(b).on("move",this.onChildMove,this)}},removeLayerNode:function(b,a){if(this.filter(a)===true){var c=b.findChildBy(function(d){return d.get("layer")==a.getLayer()});if(c){c.un("move",this.onChildMove,this);c.remove()}}},onChildMove:function(c,k,l,h){var i=this,g=i.store.getByLayer(c.get("layer")),b=l.get("container"),f=b.loader;i._reordering=true;if(f instanceof i.self&&i.store===f.store){f._reordering=true;i.store.remove(g);var a;if(l.childNodes.length>1){var j=(h===0)?h+1:h-1;a=i.store.findBy(function(m){return l.childNodes[j].get("layer")===m.getLayer()});if(h===0){a++}}else{if(k.parentNode===l.parentNode){var d=l;do{d=d.previousSibling}while(d&&!(d.get("container") instanceof b.self&&d.lastChild));if(d){a=i.store.findBy(function(m){return d.lastChild.get("layer")===m.getLayer()})}else{var e=l;do{e=e.nextSibling}while(e&&!(e.get("container") instanceof b.self&&e.firstChild));if(e){a=i.store.findBy(function(m){return e.firstChild.get("layer")===m.getLayer()})}a++}}}if(a!==undefined){i.store.insert(a,[g])}else{i.store.insert(oldRecordIndex,[g])}delete f._reordering}delete i._reordering},addStoreHandlers:function(b){if(!this._storeHandlers){this._storeHandlers={add:function(c,e,d){this.onStoreAdd(c,e,d,b)},remove:function(c,d){this.onStoreRemove(d,b)}};for(var a in this._storeHandlers){this.store.on(a,this._storeHandlers[a],this)}}},removeStoreHandlers:function(){if(this._storeHandlers){for(var a in this._storeHandlers){this.store.un(a,this._storeHandlers[a],this)}delete this._storeHandlers}},createNode:function(a){if(this.baseAttrs){Ext.apply(a,this.baseAttrs)}return a},destroy:function(){this.removeStoreHandlers()}});Ext.define("GeoExt.tree.LayerContainer",{extend:"Ext.AbstractPlugin",requires:["GeoExt.tree.LayerLoader"],alias:"plugin.gx_layercontainer",defaultText:"Layers",init:function(c){var b=this;var a=b.loader;b.loader=(a&&a instanceof GeoExt.tree.LayerLoader)?a:new GeoExt.tree.LayerLoader(a);c.set("container",b);if(!c.get("text")){c.set("text",b.defaultText);c.commit()}b.loader.load(c)},recordIndexToNodeIndex:function(c,g){var f=this;var b=f.loader.store;var e=b.getCount();var a=g.childNodes.length;var h=-1;for(var d=e-1;d>=0;--d){if(f.loader.filter(b.getAt(d))===true){++h;if(c===d||h>a-1){break}}}return h}});Ext.define("GeoExt.tree.BaseLayerContainer",{extend:"GeoExt.tree.LayerContainer",alias:"plugin.gx_baselayercontainer",defaultText:"Base Layers",init:function(c){var b=this;var a=b.loader;b.loader=Ext.applyIf(a||{},{baseAttrs:Ext.applyIf((a&&a.baseAttrs)||{},{iconCls:"gx-tree-baselayer-icon",checkedGroup:"baselayer"}),filter:function(d){var e=d.getLayer();return e.displayInLayerSwitcher===true&&e.isBaseLayer===true}});b.callParent(arguments)}});Ext.define("GeoExt.tree.Panel",{extend:"Ext.tree.Panel",alias:"widget.gx_treepanel",requires:["GeoExt.tree.Column","GeoExt.tree.View"],viewType:"gx_treeview",initComponent:function(){var a=this;if(!a.columns){if(a.initialConfig.hideHeaders===undefined){a.hideHeaders=true}a.addCls(Ext.baseCSSPrefix+"autowidth-table");a.columns=[{xtype:"gx_treecolumn",text:"Name",width:Ext.isIE6?null:10000,dataIndex:a.displayField}]}a.callParent()}});
 
 
-
 	// GIS CORE
 
 	// ext config
 	Ext.Ajax.method = 'GET';
 
+    Ext.isIE = function() {
+        return /trident/.test(Ext.userAgent);
+    }();
+
 	// gis
 	GIS = {
 		core: {
@@ -1089,45 +1092,6 @@
 		selectHandlers.activate();
 	};
 
-	GIS.core.OrganisationUnitLevelStore = function(gis) {
-        var isPlugin = GIS.plugin && !GIS.app;
-
-		return Ext.create('Ext.data.Store', {
-			fields: ['id', 'name', 'level'],
-			proxy: {
-				type: isPlugin ? 'jsonp' : 'ajax',
-				url: gis.init.contextPath + '/api/organisationUnitLevels.' + (isPlugin ? 'jsonp' : 'json') + '?fields=id,name,level&paging=false',
-				reader: {
-					type: 'json',
-					root: 'organisationUnitLevels'
-				}
-			},
-			autoLoad: true,
-			cmp: [],
-			isLoaded: false,
-			loadFn: function(fn) {
-				if (this.isLoaded) {
-					fn.call();
-				}
-				else {
-					this.load(fn);
-				}
-			},
-			getRecordByLevel: function(level) {
-				return this.getAt(this.findExact('level', level));
-			},
-			listeners: {
-				load: function() {
-					if (!this.isLoaded) {
-						this.isLoaded = true;
-						gis.util.gui.combo.setQueryMode(this.cmp, 'local');
-					}
-					this.sort('level', 'ASC');
-				}
-			}
-		});
-	};
-
 	GIS.core.StyleMap = function(labelConfig) {
 		var defaults = {
 				fillOpacity: 1,
@@ -1136,7 +1100,7 @@
                 pointRadius: 8,
                 labelAlign: 'cr',
                 labelYOffset: 13,
-                fontFamily: 'arial,sans-serif,roboto,helvetica neue,helvetica,consolas'
+                fontFamily: '"Arial","Sans-serif","Roboto","Helvetica","Consolas"'
 			},
 			select = {
 				fillOpacity: 0.9,
@@ -1299,10 +1263,15 @@
                 setMap();
             };
 
-            failure = function() {
+            failure = function(r) {
                 gis.olmap.mask.hide();
-                alert('Map id not recognized' + (gis.el ? ' (' + gis.el + ')' : ''));
-                return;
+
+                if (Ext.Array.contains([403], r.status)) {
+                    alert(GIS.i18n.you_do_not_have_access_to_all_items_in_this_favorite);
+                }
+                else {
+                    alert(r.status + '\n' + r.statusText + '\n' + r.responseText);
+                }
             };
 
             if (isPlugin) {
@@ -1734,11 +1703,14 @@
                 isPlugin = GIS.plugin && !GIS.app,
                 url = function() {
                     var params = '?ou=ou:';
+
                     for (var i = 0; i < items.length; i++) {
                         params += items[i].id;
                         params += i !== items.length - 1 ? ';' : '';
                     }
 
+                    params += '&displayProperty=' + gis.init.userAccount.settings.keyAnalysisDisplayProperty.toUpperCase();
+
                     return gis.init.contextPath + '/api/geoFeatures.' + (isPlugin ? 'jsonp' : 'json') + params + '&viewClass=detailed';
                 }(),
                 success,
@@ -2003,10 +1975,14 @@
                 isPlugin = GIS.plugin && !GIS.app,
                 url = function() {
                     var params = '?ou=ou:';
+
                     for (var i = 0; i < items.length; i++) {
                         params += items[i].id;
                         params += i !== items.length - 1 ? ';' : '';
                     }
+
+                    params += '&displayProperty=' + gis.init.userAccount.settings.keyAnalysisDisplayProperty.toUpperCase();
+
                     return gis.init.contextPath + '/api/geoFeatures.' + (isPlugin ? 'jsonp' : 'json') + params;
                 }(),
                 success,
@@ -2347,10 +2323,14 @@
                 isPlugin = GIS.plugin && !GIS.app,
                 url = function() {
                     var params = '?ou=ou:';
+
                     for (var i = 0; i < items.length; i++) {
                         params += items[i].id;
                         params += i !== items.length - 1 ? ';' : '';
                     }
+
+                    params += '&displayProperty=' + gis.init.userAccount.settings.keyAnalysisDisplayProperty.toUpperCase();
+
                     return gis.init.contextPath + '/api/geoFeatures.' + (isPlugin ? 'jsonp' : 'json') + params;
                 }(),
                 success,
@@ -2445,6 +2425,9 @@
 				paramString += i < peItems.length - 1 ? ';' : '';
 			}
 
+            // display property
+            paramString += '&displayProperty=' + gis.init.userAccount.settings.keyAnalysisDisplayProperty.toUpperCase();
+
 			success = function(json) {
 				var response = gis.api.response.Response(json),
 					featureMap = {},
@@ -2721,8 +2704,6 @@
 		(function() {
 			conf.finals = {
 				url: {
-					path_api: '/api/',
-					path_module: '/dhis-web-mapping/',
 					path_commons: '/dhis-web-commons-ajax-json/'
 				},
 				layer: {
@@ -2893,9 +2874,9 @@
 
             conf.url.analysisFields = [
                 '*',
-                'columns[dimension,filter,items[id,name]]',
-                'rows[dimension,filter,items[id,name]]',
-                'filters[dimension,filter,items[id,name]]',
+                'columns[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
+                'rows[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
+                'filters[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
                 '!lastUpdated',
                 '!href',
                 '!created',
@@ -3596,11 +3577,6 @@
 			};
 		}());
 
-		// store
-		(function() {
-			store.organisationUnitLevels = GIS.core.OrganisationUnitLevelStore(gis);
-		}());
-
 		gis.api = api;
 		gis.store = store;
 
@@ -5135,6 +5111,74 @@
 			}
 		});
 
+        // date, calendar
+        requests.push({
+            url: url + '/api/systemSettings.jsonp?key=keyCalendar&key=keyDateFormat',
+            success: function(r) {
+                var systemSettings = Ext.decode(r.responseText);
+                init.systemInfo.dateFormat = Ext.isString(systemSettings.keyDateFormat) ? systemSettings.keyDateFormat.toLowerCase() : 'yyyy-mm-dd';
+                init.systemInfo.calendar = systemSettings.keyCalendar;
+
+                // user-account
+                Ext.Ajax.request({
+                    url: init.contextPath + '/api/me/user-account.json',
+                    success: function(r) {
+                        init.userAccount = Ext.decode(r.responseText);
+
+                        // init
+                        var defaultKeyUiLocale = 'en',
+                            defaultKeyAnalysisDisplayProperty = 'name',
+                            namePropertyUrl,
+                            contextPath,
+                            keyUiLocale,
+                            dateFormat;
+
+                        init.userAccount.settings.keyUiLocale = init.userAccount.settings.keyUiLocale || defaultKeyUiLocale;
+                        init.userAccount.settings.keyAnalysisDisplayProperty = init.userAccount.settings.keyAnalysisDisplayProperty || defaultKeyAnalysisDisplayProperty;
+
+                        // local vars
+                        contextPath = init.contextPath;
+                        keyUiLocale = init.userAccount.settings.keyUiLocale;
+                        keyAnalysisDisplayProperty = init.userAccount.settings.keyAnalysisDisplayProperty;
+                        namePropertyUrl = keyAnalysisDisplayProperty === defaultKeyAnalysisDisplayProperty ? keyAnalysisDisplayProperty : keyAnalysisDisplayProperty + '|rename(' + defaultKeyAnalysisDisplayProperty + ')';
+                        dateFormat = init.systemInfo.dateFormat;
+
+                        init.namePropertyUrl = namePropertyUrl;
+
+                        // calendar
+                        (function() {
+                            var dhis2PeriodUrl = '../dhis-web-commons/javascripts/dhis2/dhis2.period.js',
+                                defaultCalendarId = 'gregorian',
+                                calendarIdMap = {'iso8601': defaultCalendarId},
+                                calendarId = calendarIdMap[init.systemInfo.calendar] || init.systemInfo.calendar || defaultCalendarId,
+                                calendarIds = ['coptic', 'ethiopian', 'islamic', 'julian', 'nepali', 'thai'],
+                                calendarScriptUrl,
+                                createGenerator;
+
+                            // calendar
+                            createGenerator = function() {
+                                init.calendar = $.calendars.instance(calendarId);
+                                init.periodGenerator = new dhis2.period.PeriodGenerator(init.calendar, init.systemInfo.dateFormat);
+                            };
+
+                            if (Ext.Array.contains(calendarIds, calendarId)) {
+                                calendarScriptUrl = '../dhis-web-commons/javascripts/jQuery/calendars/jquery.calendars.' + calendarId + '.min.js';
+
+                                Ext.Loader.injectScriptElement(calendarScriptUrl, function() {
+                                    Ext.Loader.injectScriptElement(dhis2PeriodUrl, createGenerator);
+                                });
+                            }
+                            else {
+                                Ext.Loader.injectScriptElement(dhis2PeriodUrl, createGenerator);
+                            }
+                        }());
+
+                        fn();
+                    }
+                });
+            }
+        });
+
 		requests.push({
 			url: url + '/api/organisationUnits.jsonp?userOnly=true&fields=id,name,children[id,name]&paging=false',
 			success: function(r) {

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-pivot/scripts/core.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-pivot/scripts/core.js	2014-10-29 00:56:31 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-pivot/scripts/core.js	2014-11-03 10:54:40 +0000
@@ -34,27 +34,27 @@
 				dimension: {
 					data: {
 						value: 'data',
-						name: NS.i18n.data,
+						name: PT.i18n.data || 'Data',
 						dimensionName: 'dx',
 						objectName: 'dx',
 						warning: {
-							filter: '...'//NS.i18n.wm_multiple_filter_ind_de
+							filter: '...'//PT.i18n.wm_multiple_filter_ind_de
 						}
 					},
 					category: {
-						name: NS.i18n.assigned_categories,
+						name: PT.i18n.assigned_categories || 'Assigned categories',
 						dimensionName: 'co',
 						objectName: 'co',
 					},
 					indicator: {
 						value: 'indicators',
-						name: NS.i18n.indicators,
+						name: PT.i18n.indicators || 'Indicators',
 						dimensionName: 'dx',
 						objectName: 'in'
 					},
 					dataElement: {
 						value: 'dataElements',
-						name: NS.i18n.data_elements,
+						name: PT.i18n.data_elements || 'Data elements',
 						dimensionName: 'dx',
 						objectName: 'de'
 					},
@@ -66,13 +66,13 @@
 					},
 					dataSet: {
 						value: 'dataSets',
-						name: NS.i18n.data_sets,
+						name: PT.i18n.data_sets || 'Data sets',
 						dimensionName: 'dx',
 						objectName: 'ds'
 					},
 					period: {
 						value: 'period',
-						name: NS.i18n.periods,
+						name: PT.i18n.periods || 'Periods',
 						dimensionName: 'pe',
 						objectName: 'pe'
 					},
@@ -84,7 +84,7 @@
 					},
 					organisationUnit: {
 						value: 'organisationUnits',
-						name: NS.i18n.organisation_units,
+						name: PT.i18n.organisation_units || 'Organisation units',
 						dimensionName: 'ou',
 						objectName: 'ou'
 					},
@@ -2287,7 +2287,7 @@
 
                             a.push(dimLabelHtml);
                         }
-                        
+
 						return a;
 					}
 
@@ -2408,8 +2408,8 @@
                             }]);
                         }
                     }
-                        
-                        
+
+
 	//axisAllObjects = [ [ dim, dim ]
 	//				     [ dim, dim ]
 	//				     [ dim, dim ]

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-pivot/scripts/plugin.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-pivot/scripts/plugin.js	2014-08-10 20:04:11 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-pivot/scripts/plugin.js	2014-11-03 12:33:16 +0000
@@ -5,6 +5,10 @@
 	// ext config
 	Ext.Ajax.method = 'GET';
 
+    Ext.isIE = function() {
+        return /trident/.test(Ext.userAgent);
+    }();
+
 	// namespace
 	PT = {};
 
@@ -31,7 +35,7 @@
 				dimension: {
 					data: {
 						value: 'data',
-						name: PT.i18n.data,
+						name: PT.i18n.data || 'Data',
 						dimensionName: 'dx',
 						objectName: 'dx',
 						warning: {
@@ -39,19 +43,19 @@
 						}
 					},
 					category: {
-						name: PT.i18n.categories,
+						name: PT.i18n.assigned_categories || 'Assigned categories',
 						dimensionName: 'co',
 						objectName: 'co',
 					},
 					indicator: {
 						value: 'indicators',
-						name: PT.i18n.indicators,
+						name: PT.i18n.indicators || 'Indicators',
 						dimensionName: 'dx',
 						objectName: 'in'
 					},
 					dataElement: {
 						value: 'dataElements',
-						name: PT.i18n.data_elements,
+						name: PT.i18n.data_elements || 'Data elements',
 						dimensionName: 'dx',
 						objectName: 'de'
 					},
@@ -63,13 +67,13 @@
 					},
 					dataSet: {
 						value: 'dataSets',
-						name: PT.i18n.data_sets,
+						name: PT.i18n.data_sets || 'Data sets',
 						dimensionName: 'dx',
 						objectName: 'ds'
 					},
 					period: {
 						value: 'period',
-						name: PT.i18n.periods,
+						name: PT.i18n.periods || 'Periods',
 						dimensionName: 'pe',
 						objectName: 'pe'
 					},
@@ -81,7 +85,7 @@
 					},
 					organisationUnit: {
 						value: 'organisationUnits',
-						name: PT.i18n.organisation_units,
+						name: PT.i18n.organisation_units || 'Organisation units',
 						dimensionName: 'ou',
 						objectName: 'ou'
 					},
@@ -139,7 +143,7 @@
 				west_fill_accordion_indicator: 56,
 				west_fill_accordion_dataelement: 59,
 				west_fill_accordion_dataset: 31,
-				west_fill_accordion_period: 293,
+				west_fill_accordion_period: 284,
 				west_fill_accordion_organisationunit: 58,
 				west_maxheight_accordion_indicator: 400,
 				west_maxheight_accordion_dataelement: 400,
@@ -195,9 +199,9 @@
                     '*',
                     'program[id,name]',
                     'programStage[id,name]',
-                    'columns[dimension,filter,items[id,name]]',
-                    'rows[dimension,filter,items[id,name]]',
-                    'filters[dimension,filter,items[id,name]]',
+                    'columns[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
+                    'rows[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
+                    'filters[dimension,filter,items[id,' + init.namePropertyUrl + ']]',
                     '!lastUpdated',
                     '!href',
                     '!created',
@@ -248,7 +252,7 @@
 						return;
 					}
 
-					config.id = config.id.replace('#', '.');
+					config.id = config.id.replace('.', '#');
 
 					return config;
 				}();
@@ -311,7 +315,11 @@
 
 				// showColTotals: boolean (true)
 
-				// showSubTotals: boolean (true)
+				// showColSubTotals: boolean (true)
+
+				// showRowSubTotals: boolean (true)
+
+                // showDimensionLabels: boolean (false)
 
 				// hideEmptyRows: boolean (false)
 
@@ -419,7 +427,7 @@
 
 					// dc and co
 					if (objectNameDimensionMap[dimConf.operand.objectName] && objectNameDimensionMap[dimConf.category.objectName]) {
-						web.message.alert('Categories and detailed data elements cannot be specified together');
+						web.message.alert('Assigned categories and detailed data elements cannot be specified together');
 						return;
 					}
 
@@ -476,9 +484,11 @@
 					layout.filters = config.filters;
 
 					// properties
+					layout.showColTotals = Ext.isBoolean(config.colTotals) ? config.colTotals : (Ext.isBoolean(config.showColTotals) ? config.showColTotals : true);
 					layout.showRowTotals = Ext.isBoolean(config.rowTotals) ? config.rowTotals : (Ext.isBoolean(config.showRowTotals) ? config.showRowTotals : true);
-					layout.showColTotals = Ext.isBoolean(config.colTotals) ? config.colTotals : (Ext.isBoolean(config.showColTotals) ? config.showColTotals : true);
-					layout.showSubTotals = Ext.isBoolean(config.subtotals) ? config.subtotals : (Ext.isBoolean(config.showSubTotals) ? config.showSubTotals : true);
+					layout.showColSubTotals = Ext.isBoolean(config.colSubTotals) ? config.colSubTotals : (Ext.isBoolean(config.showColSubTotals) ? config.showColSubTotals : true);
+					layout.showRowSubTotals = Ext.isBoolean(config.rowSubTotals) ? config.rowSubTotals : (Ext.isBoolean(config.showRowSubTotals) ? config.showRowSubTotals : true);
+					layout.showDimensionLabels = Ext.isBoolean(config.showDimensionLabels) ? config.showDimensionLabels : (Ext.isBoolean(config.showDimensionLabels) ? config.showDimensionLabels : true);
 					layout.hideEmptyRows = Ext.isBoolean(config.hideEmptyRows) ? config.hideEmptyRows : false;
                     layout.aggregationType = Ext.isString(config.aggregationType) ? config.aggregationType : 'default';
 
@@ -1628,8 +1638,12 @@
 					delete layout.showColTotals;
 				}
 
-				if (layout.showSubTotals) {
-					delete layout.showSubTotals;
+				if (layout.showColSubTotals) {
+					delete layout.showColSubTotals;
+				}
+
+				if (layout.showRowSubTotals) {
+					delete layout.showRowSubTotals;
 				}
 
 				if (!layout.hideEmptyRows) {
@@ -1720,8 +1734,8 @@
 					for (var i = 0, id, splitId ; i < ids.length; i++) {
 						id = ids[i];
 
-						if (id.indexOf('.') !== -1) {
-							splitId = id.split('.');
+						if (id.indexOf('#') !== -1) {
+							splitId = id.split('#');
 							response.metaData.names[id] = response.metaData.names[splitId[0]] + ' ' + response.metaData.names[splitId[1]];
 						}
 					}
@@ -1754,7 +1768,7 @@
 						for (var j = 0, index; j < idIndexOrder.length; j++) {
 							index = idIndexOrder[j];
 
-							id += response.headers[index].name === co ? '.' : '';
+							//id += response.headers[index].name === co ? '.' : '';
 							id += row[index];
 						}
 
@@ -1820,6 +1834,25 @@
 
                 return response;
             };
+
+            service.response.getValue = function(str) {
+				var n = parseFloat(str);
+
+                if (Ext.isBoolean(str)) {
+                    return 1;
+                }
+
+                // return string if
+                // - parsefloat(string) is not a number
+                // - string is just starting with a number
+                // - string is a valid date
+				//if (!Ext.isNumber(n) || n != str || new Date(str).toString() !== 'Invalid Date') {
+				if (!Ext.isNumber(n) || n != str) {
+					return 0;
+				}
+
+                return n;
+			};
         }());
 
 		// web
@@ -1900,7 +1933,7 @@
 
 					if (dimName === dx) {
 						for (var j = 0, index; j < items.length; j++) {
-							index = items[j].indexOf('.');
+							index = items[j].indexOf('#');
 
 							if (index > 0) {
 								addCategoryDimension = true;
@@ -1940,6 +1973,9 @@
                     paramString += '&aggregationType=' + aggTypes[xLayout.aggregationType];
                 }
 
+                // display property
+                paramString += '&displayProperty=' + init.userAccount.settings.keyAnalysisDisplayProperty.toUpperCase();
+
 				return paramString;
 			};
 
@@ -2017,7 +2053,9 @@
 				var getRoundedHtmlValue,
 					getTdHtml,
 					doSubTotals,
-					doTotals,
+					doRowTotals,
+                    doColTotals,
+                    doSortableColumnHeaders,
 					getColAxisHtmlArray,
 					getRowHtmlArray,
 					rowAxisHtmlArray,
@@ -2068,7 +2106,32 @@
 						isNumeric = Ext.isObject(config) && Ext.isString(config.type) && config.type.substr(0,5) === 'value' && !config.empty,
 						isValue = isNumeric && config.type === 'value',
 						cls = '',
-						html = '';
+						html = '',
+                        getHtmlValue;
+
+                    getHtmlValue = function(config) {
+                        var str = config.htmlValue,
+                            n = parseFloat(config.htmlValue);
+
+                        if (config.collapsed) {
+                            return '';
+                        }
+
+                        if (isValue) {
+                            if (Ext.isBoolean(str)) {
+                                return str;
+                            }
+
+                            //if (!Ext.isNumber(n) || n != str || new Date(str).toString() !== 'Invalid Date') {
+                            if (!Ext.isNumber(n) || n != str) {
+                                return str;
+                            }
+
+                            return n;
+                        }
+
+                        return str || '';
+                    }
 
 					if (!Ext.isObject(config)) {
 						return '';
@@ -2095,7 +2158,7 @@
 
 					colSpan = config.colSpan ? 'colspan="' + config.colSpan + '" ' : '';
 					rowSpan = config.rowSpan ? 'rowspan="' + config.rowSpan + '" ' : '';
-					htmlValue = config.collapsed ? '' : config.htmlValue || config.value || '';
+                    htmlValue = getHtmlValue(config);
 					htmlValue = config.type !== 'dimension' ? support.prototype.number.prettyPrint(htmlValue, xLayout.digitGroupSeparator) : htmlValue;
 					displayDensity = conf.pivot.displayDensity[config.displayDensity] || conf.pivot.displayDensity[xLayout.displayDensity];
 					fontSize = conf.pivot.fontSize[config.fontSize] || conf.pivot.fontSize[xLayout.fontSize];
@@ -2135,16 +2198,20 @@
 					return html;
 				};
 
-				doSubTotals = function(xAxis) {
-					return !!xLayout.showSubTotals && xAxis && xAxis.dims > 1;
+                doColTotals = function() {
+					return !!xLayout.showColTotals;
 				};
 
 				doRowTotals = function() {
 					return !!xLayout.showRowTotals;
 				};
 
-                doColTotals = function() {
-					return !!xLayout.showColTotals;
+				doColSubTotals = function() {
+					return !!xLayout.showColSubTotals && xRowAxis && xRowAxis.dims > 1;
+				};
+
+				doRowSubTotals = function() {
+					return !!xLayout.showRowSubTotals && xColAxis && xColAxis.dims > 1;
 				};
 
 				doSortableColumnHeaders = function() {
@@ -2155,16 +2222,73 @@
 					var a = [],
 						getEmptyHtmlArray;
 
-					getEmptyHtmlArray = function() {
-						return (xColAxis && xRowAxis) ? getTdHtml({
-							cls: 'pivot-dim-empty cursor-default',
-							colSpan: xRowAxis.dims,
-							rowSpan: xColAxis.dims,
-							htmlValue: '&nbsp;'
-						}) : '';
-					};
-
-					if (!(xColAxis && Ext.isObject(xColAxis))) {
+                    getEmptyNameTdConfig = function(config) {
+                        config = config || {};
+
+                        return getTdHtml({
+                            cls: config.cls ? ' ' + config.cls : 'pivot-empty',
+                            colSpan: config.colSpan ? config.colSpan : 1,
+                            rowSpan: config.rowSpan ? config.rowSpan : 1,
+                            htmlValue: config.htmlValue ? config.htmlValue : '&nbsp;'
+                        });
+                    };
+
+                    getEmptyHtmlArray = function(i) {
+                        var a = [];
+
+                        // if not the intersection cell
+                        if (i < xColAxis.dims - 1) {
+                            if (xRowAxis && xRowAxis.dims) {
+                                for (var j = 0; j < xRowAxis.dims - 1; j++) {
+                                    a.push(getEmptyNameTdConfig({
+                                        cls: 'pivot-dim-label'
+                                    }));
+                                }
+                            }
+
+                            a.push(getEmptyNameTdConfig({
+                                cls: 'pivot-dim-label',
+                                htmlValue: dimConf.objectNameMap[xLayout.columnObjectNames[i]].name
+                            }));
+                        }
+                        else {
+                            if (xRowAxis && xRowAxis.dims) {
+                                for (var j = 0; j < xRowAxis.dims - 1; j++) {
+                                    a.push(getEmptyNameTdConfig({
+                                        cls: 'pivot-dim-label',
+                                        htmlValue: dimConf.objectNameMap[xLayout.rowObjectNames[j]].name
+                                    }));
+                                }
+                            }
+
+                            a.push(getEmptyNameTdConfig({
+                                cls: 'pivot-dim-label',
+                                htmlValue: (xRowAxis ? dimConf.objectNameMap[xLayout.rowObjectNames[j]].name : '') + (xColAxis && xRowAxis ? '&nbsp;/&nbsp;' : '') + (xColAxis ? dimConf.objectNameMap[xLayout.columnObjectNames[i]].name : '')
+                            }));
+                        }
+
+                        return a;
+                    };
+
+					if (!xColAxis) {
+
+                        // show row dimension labels
+                        if (xRowAxis && xLayout.showDimensionLabels) {
+                            var dimLabelHtml = [];
+
+                            // labels from row object names
+                            for (var i = 0; i < xLayout.rowObjectNames.length; i++) {
+                                dimLabelHtml.push(getEmptyNameTdConfig({
+                                    cls: 'pivot-dim-label',
+                                    htmlValue: dimConf.objectNameMap[xLayout.rowObjectNames[i]].name
+                                }));
+                            }
+
+                            // pivot-transparent-column unnecessary
+
+                            a.push(dimLabelHtml);
+                        }
+
 						return a;
 					}
 
@@ -2172,8 +2296,14 @@
 					for (var i = 0, dimHtml; i < xColAxis.dims; i++) {
 						dimHtml = [];
 
-						if (i === 0) {
-							dimHtml.push(getEmptyHtmlArray());
+                        if (xLayout.showDimensionLabels) {
+                            dimHtml = dimHtml.concat(getEmptyHtmlArray(i));
+                        }
+                        else if (i === 0) {
+							dimHtml.push(xColAxis && xRowAxis ? getEmptyNameTdConfig({
+                                colSpan: xRowAxis.dims,
+                                rowSpan: xColAxis.dims
+                            }) : '');
 						}
 
 						for (var j = 0, obj, spanCount = 0, condoId, totalId; j < xColAxis.size; j++) {
@@ -2197,7 +2327,7 @@
 
 							dimHtml.push(getTdHtml(obj, condoId));
 
-							if (i === 0 && spanCount === xColAxis.span[i] && doSubTotals(xColAxis) ) {
+							if (i === 0 && spanCount === xColAxis.span[i] && doRowSubTotals() ) {
 								dimHtml.push(getTdHtml({
 									type: 'dimensionSubtotal',
 									cls: 'pivot-dim-subtotal cursor-default',
@@ -2271,6 +2401,16 @@
 							axisAllObjects.push(row);
 						}
 					}
+                    else {
+                        if (xLayout.showDimensionLabels) {
+                            axisAllObjects.push([{
+                                type: 'transparent',
+                                cls: 'pivot-transparent-row'
+                            }]);
+                        }
+                    }
+
+
 	//axisAllObjects = [ [ dim, dim ]
 	//				     [ dim, dim ]
 	//				     [ dim, dim ]
@@ -2281,13 +2421,12 @@
 						valueItemsRow = [];
 						valueObjectsRow = [];
 
-						for (var j = 0, id, value, htmlValue, empty, uuid, uuids; j < colAxisSize; j++) {
+						for (var j = 0, id, value, responseValue, htmlValue, empty, uuid, uuids; j < colAxisSize; j++) {
 							empty = false;
 							uuids = [];
 
 							// meta data uid
-							//id = (xColAxis ? support.prototype.str.replaceAll(xColAxis.ids[j], '-', '') : '') + (xRowAxis ? support.prototype.str.replaceAll(xRowAxis.ids[i], '-', '') : '');
-							id = (xColAxis ? xColAxis.ids[j] : '') + (xRowAxis ? xRowAxis.ids[i] : '');
+							id = ((xColAxis ? xColAxis.ids[j] : '') + (xRowAxis ? xRowAxis.ids[i] : '')).replace('#', '');
 
                             // value html element id
 							uuid = Ext.data.IdGenerator.get('uuid').generate();
@@ -2300,9 +2439,12 @@
 								uuids = uuids.concat(xRowAxis.objects.all[xRowAxis.dims - 1][i].uuids);
 							}
 
-							if (idValueMap[id]) {
-								value = parseFloat(idValueMap[id]);
-								htmlValue = value.toString();
+                            // value, htmlValue
+                            responseValue = idValueMap[id];
+
+							if (Ext.isDefined(responseValue)) {
+                                value = service.response.getValue(responseValue);
+                                htmlValue = responseValue;
 							}
 							else {
 								value = 0;
@@ -2392,7 +2534,7 @@
                     xValueObjects = valueObjects;
 
 					// col subtotals
-					if (doSubTotals(xColAxis)) {
+					if (doRowSubTotals()) {
 						var tmpValueObjects = [];
 
 						for (var i = 0, row, rowSubTotal, colCount; i < xValueObjects.length; i++) {
@@ -2434,7 +2576,7 @@
 					}
 
 					// row subtotals
-					if (doSubTotals(xRowAxis)) {
+					if (doColSubTotals()) {
 						var tmpAxisAllObjects = [],
 							tmpValueObjects = [],
 							tmpTotalValueObjects = [],
@@ -2548,9 +2690,9 @@
 					for (var i = 0, row; i < xValueObjects.length; i++) {
 						row = [];
 
-						if (xRowAxis) {
+						//if (xRowAxis) {
 							row = row.concat(axisAllObjects[i]);
-						}
+						//}
 
 						row = row.concat(xValueObjects[i]);
 
@@ -2605,7 +2747,7 @@
 
 						xTotalColObjects = totalColObjects;
 
-						if (xColAxis && doSubTotals(xColAxis)) {
+						if (xColAxis && doRowSubTotals()) {
 							var tmp = [];
 
 							for (var i = 0, item, subTotal = 0, empty = [], colCount = 0; i < xTotalColObjects.length; i++) {
@@ -2780,8 +2922,12 @@
 	css += '.pivot-value-total { \n background-color: #e4e4e4; \n white-space: nowrap; \n text-align: right; \n } \n';
 	css += '.pivot-value-total-subgrandtotal { \n background-color: #d8d8d8; \n white-space: nowrap; \n text-align: right; \n } \n';
 	css += '.pivot-value-grandtotal { \n background-color: #c8c8c8; \n white-space: nowrap; \n text-align: right; \n } \n';
+    css += '.pivot-dim-label { \n background-color: #cddaed; \n white-space: nowrap; \n text-align: center; \n } \n';
+    css += '.pivot-empty { \n background-color: #cddaed; \n } \n';
+    css += '.pivot-transparent-column { \n background-color: #fff; \n border-top-color: #fff !important; \n border-right-color: #fff !important; \n } \n';
+    css += '.pivot-transparent-row { \n background-color: #fff; \n border-bottom-color: #fff !important; \n border-left-color: #fff !important; \n } \n';
 
-	css += '.x-mask-msg { \n padding: 0; \n	border: 0 none; \n background-image: none; \n background-color: transparent; \n } \n';
+    css += '.x-mask-msg { \n padding: 0; \n	border: 0 none; \n background-image: none; \n background-color: transparent; \n } \n';
 	css += '.x-mask-msg div { \n background-position: 11px center; \n } \n';
 	css += '.x-mask-msg .x-mask-loading { \n border: 0 none; \n	background-color: #000; \n color: #fff; \n border-radius: 2px; \n padding: 12px 14px 12px 30px; \n opacity: 0.65; \n } \n';
     css += '.x-mask { opacity: 0 } \n';
@@ -2835,6 +2981,74 @@
 			}
 		});
 
+        // date, calendar
+        requests.push({
+            url: url + '/api/systemSettings.jsonp?key=keyCalendar&key=keyDateFormat',
+            success: function(r) {
+                var systemSettings = Ext.decode(r.responseText);
+                init.systemInfo.dateFormat = Ext.isString(systemSettings.keyDateFormat) ? systemSettings.keyDateFormat.toLowerCase() : 'yyyy-mm-dd';
+                init.systemInfo.calendar = systemSettings.keyCalendar;
+
+                // user-account
+                Ext.Ajax.request({
+                    url: init.contextPath + '/api/me/user-account.jsonp',
+                    success: function(r) {
+                        init.userAccount = Ext.decode(r.responseText);
+
+                        // init
+                        var defaultKeyUiLocale = 'en',
+                            defaultKeyAnalysisDisplayProperty = 'name',
+                            namePropertyUrl,
+                            contextPath,
+                            keyUiLocale,
+                            dateFormat;
+
+                        init.userAccount.settings.keyUiLocale = init.userAccount.settings.keyUiLocale || defaultKeyUiLocale;
+                        init.userAccount.settings.keyAnalysisDisplayProperty = init.userAccount.settings.keyAnalysisDisplayProperty || defaultKeyAnalysisDisplayProperty;
+
+                        // local vars
+                        contextPath = init.contextPath;
+                        keyUiLocale = init.userAccount.settings.keyUiLocale;
+                        keyAnalysisDisplayProperty = init.userAccount.settings.keyAnalysisDisplayProperty;
+                        namePropertyUrl = keyAnalysisDisplayProperty === defaultKeyAnalysisDisplayProperty ? keyAnalysisDisplayProperty : keyAnalysisDisplayProperty + '|rename(' + defaultKeyAnalysisDisplayProperty + ')';
+                        dateFormat = init.systemInfo.dateFormat;
+
+                        init.namePropertyUrl = namePropertyUrl;
+
+                        // calendar
+                        (function() {
+                            var dhis2PeriodUrl = '../dhis-web-commons/javascripts/dhis2/dhis2.period.js',
+                                defaultCalendarId = 'gregorian',
+                                calendarIdMap = {'iso8601': defaultCalendarId},
+                                calendarId = calendarIdMap[init.systemInfo.calendar] || init.systemInfo.calendar || defaultCalendarId,
+                                calendarIds = ['coptic', 'ethiopian', 'islamic', 'julian', 'nepali', 'thai'],
+                                calendarScriptUrl,
+                                createGenerator;
+
+                            // calendar
+                            createGenerator = function() {
+                                init.calendar = $.calendars.instance(calendarId);
+                                init.periodGenerator = new dhis2.period.PeriodGenerator(init.calendar, init.systemInfo.dateFormat);
+                            };
+
+                            if (Ext.Array.contains(calendarIds, calendarId)) {
+                                calendarScriptUrl = '../dhis-web-commons/javascripts/jQuery/calendars/jquery.calendars.' + calendarId + '.min.js';
+
+                                Ext.Loader.injectScriptElement(calendarScriptUrl, function() {
+                                    Ext.Loader.injectScriptElement(dhis2PeriodUrl, createGenerator);
+                                });
+                            }
+                            else {
+                                Ext.Loader.injectScriptElement(dhis2PeriodUrl, createGenerator);
+                            }
+                        }());
+
+                        fn();
+                    }
+                });
+            }
+        });
+
 		requests.push({
 			url: url + '/api/organisationUnits.jsonp?userOnly=true&fields=id,name,children[id,name]&paging=false',
 			success: function(r) {