← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 13932: DV, paging and filtering implemented.

 

Merge authors:
  Jan Henrik Øverland (janhenrik-overland)
------------------------------------------------------------
revno: 13932 [merge]
committer: Jan Henrik Overland <janhenrik.overland@xxxxxxxxx>
branch nick: dhis2
timestamp: Tue 2014-02-04 15:45:22 +0100
message:
  DV, paging and filtering implemented.
added:
  dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/clear-over_20.png
  dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/clear_20.png
  dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/search_14.png
modified:
  dhis-2/dhis-web/dhis-web-pivot/src/main/webapp/dhis-web-pivot/app/scripts/app.js
  dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/scripts/app.js
  dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/styles/style.css


--
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
=== modified file 'dhis-2/dhis-web/dhis-web-pivot/src/main/webapp/dhis-web-pivot/app/scripts/app.js'
--- dhis-2/dhis-web/dhis-web-pivot/src/main/webapp/dhis-web-pivot/app/scripts/app.js	2014-01-29 12:34:16 +0000
+++ dhis-2/dhis-web/dhis-web-pivot/src/main/webapp/dhis-web-pivot/app/scripts/app.js	2014-02-04 14:14:27 +0000
@@ -2389,6 +2389,7 @@
 	createViewport = function() {
         var indicatorAvailableStore,
 			indicatorSelectedStore,
+            indicatorGroupStore,
 			dataElementAvailableStore,
 			dataElementSelectedStore,
 			dataElementGroupStore,
@@ -2402,50 +2403,61 @@
 			organisationUnitGroupStore,
 			legendSetStore,
 
+            isScrolled,
+            indicatorLabel,
+            indicatorSearch,
+            indicatorFilter,
+            indicatorGroup,
             indicatorAvailable,
-			indicatorSelected,
-			indicator,
-			dataElementAvailable,
-			dataElementSelected,
-			dataElement,
-			dataSetAvailable,
-			dataSetSelected,
-			dataSet,
+            indicatorSelected,
+            indicator,
+			dataElementLabel,
+            dataElementSearch,
+            dataElementFilter,
+            dataElementAvailable,
+            dataElementSelected,
+            dataElementGroup,
+            dataElementDetailLevel,
+            dataElement,
+            dataSetAvailable,
+            dataSetSelected,
+            dataSet,
 			rewind,
-			relativePeriod,
-			fixedPeriodAvailable,
-			fixedPeriodSelected,
-			period,
-			treePanel,
-			userOrganisationUnit,
-			userOrganisationUnitChildren,
-			userOrganisationUnitGrandChildren,
-			userOrganisationUnitPanel,
-			organisationUnitLevel,
-			tool,
-			toolPanel,
-			organisationUnit,
+            relativePeriod,
+            fixedPeriodAvailable,
+            fixedPeriodSelected,
+            period,
+            treePanel,
+            userOrganisationUnit,
+            userOrganisationUnitChildren,
+            userOrganisationUnitGrandChildren,
+            organisationUnitLevel,
+            organisationUnitGroup,
+            toolMenu,
+            tool,
+            toolPanel,
+            organisationUnit,
 			dimensionIdAvailableStoreMap = {},
 			dimensionIdSelectedStoreMap = {},
 			getGroupSetPanels,
 			update,
 
-			layoutButton,
-			optionsButton,
-			favoriteButton,
-			openTableLayoutTab,
-			downloadButton,
-			interpretationItem,
-			pluginItem,
-			shareButton,
-
 			accordionBody,
-			accordion,
-			westRegion,
-			centerRegion,
-
-			setGui,
-			viewport,
+            accordion,
+            westRegion,
+            layoutButton,
+            optionsButton,
+            favoriteButton,
+            getParamString,
+            openTableLayoutTab,
+            downloadButton,
+            interpretationItem,
+            pluginItem,
+            shareButton,
+            defaultButton,
+            centerRegion,
+            setGui,
+            viewport,
 
 			accordionPanels = [];
 
@@ -3518,6 +3530,7 @@
 		};
 
 		// period
+
 		rewind = Ext.create('Ext.form.field.Checkbox', {
 			relativePeriodId: 'rewind',
 			boxLabel: 'Rewind one period',
@@ -4073,6 +4086,7 @@
 		};
 
 		// organisation unit
+
 		treePanel = Ext.create('Ext.tree.Panel', {
 			cls: 'ns-tree',
 			style: 'border-top: 1px solid #ddd; padding-top: 1px',
@@ -4536,6 +4550,7 @@
         };
 
 		// dimensions
+
 		getDimensionPanels = function(dimensions, iconCls) {
 			var	getAvailableStore,
 				getSelectedStore,
@@ -4761,6 +4776,7 @@
 		};
 
 		// viewport
+
 		update = function() {
 			var config = ns.core.web.pivot.getLayoutConfig(),
 				layout = ns.core.api.layout.Layout(config);

=== added file 'dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/clear-over_20.png'
Binary files dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/clear-over_20.png	1970-01-01 00:00:00 +0000 and dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/clear-over_20.png	2014-02-04 14:44:24 +0000 differ
=== added file 'dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/clear_20.png'
Binary files dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/clear_20.png	1970-01-01 00:00:00 +0000 and dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/clear_20.png	2014-02-04 14:44:24 +0000 differ
=== added file 'dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/search_14.png'
Binary files dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/search_14.png	1970-01-01 00:00:00 +0000 and dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/images/search_14.png	2014-02-04 14:44:24 +0000 differ
=== modified file 'dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/scripts/app.js'
--- dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/scripts/app.js	2014-01-23 19:55:42 +0000
+++ dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/scripts/app.js	2014-02-04 14:39:04 +0000
@@ -1396,36 +1396,48 @@
 			web.multiSelect.unselect = function(a, s) {
 				var selected = s.getValue();
 				if (selected.length) {
-					Ext.Array.each(selected, function(item) {
-						s.store.remove(s.store.getAt(s.store.findExact('id', item)));
+					Ext.Array.each(selected, function(id) {
+						a.store.add(s.store.getAt(s.store.findExact('id', id)));
+						s.store.remove(s.store.getAt(s.store.findExact('id', id)));
 					});
 					this.filterAvailable(a, s);
+                    a.store.sortStore();
 				}
 			};
 
 			web.multiSelect.unselectAll = function(a, s) {
+				a.store.add(s.store.getRange());
 				s.store.removeAll();
-				a.store.clearFilter();
 				this.filterAvailable(a, s);
+                a.store.sortStore();
 			};
 
 			web.multiSelect.filterAvailable = function(a, s) {
-				a.store.filterBy( function(r) {
-					var keep = true;
-					s.store.each( function(r2) {
-						if (r.data.id == r2.data.id) {
-							keep = false;
+				if (a.store.getRange().length && s.store.getRange().length) {
+					var recordsToRemove = [];
+
+					a.store.each( function(ar) {
+						var removeRecord = false;
+
+						s.store.each( function(sr) {
+							if (sr.data.id === ar.data.id) {
+								removeRecord = true;
+							}
+						});
+
+						if (removeRecord) {
+							recordsToRemove.push(ar);
 						}
-
 					});
-					return keep;
-				});
-				a.store.sortStore();
+
+					a.store.remove(recordsToRemove);
+				}
 			};
 
 			web.multiSelect.setHeight = function(ms, panel, fill) {
-				for (var i = 0; i < ms.length; i++) {
-					ms[i].setHeight(panel.getHeight() - fill);
+				for (var i = 0, height; i < ms.length; i++) {
+					height = panel.getHeight() - fill - (ms[i].hasToolbar ? 25 : 0);
+					ms[i].setHeight(height);
 				}
 			};
 
@@ -1787,8 +1799,9 @@
             filter,
             layout,
 
-			indicatorAvailableStore,
+            indicatorAvailableStore,
 			indicatorSelectedStore,
+            indicatorGroupStore,
 			dataElementAvailableStore,
 			dataElementSelectedStore,
 			dataElementGroupStore,
@@ -1797,23 +1810,30 @@
 			periodTypeStore,
 			fixedPeriodAvailableStore,
 			fixedPeriodSelectedStore,
-			chartStore,
+			reportTableStore,
 			organisationUnitLevelStore,
 			organisationUnitGroupStore,
 
+            isScrolled,
+            indicatorLabel,
+            indicatorSearch,
+            indicatorFilter,
+            indicatorGroup,
             indicatorAvailable,
             indicatorSelected,
             indicator,
+			dataElementLabel,
+            dataElementSearch,
+            dataElementFilter,
             dataElementAvailable,
             dataElementSelected,
-            dataElementGroupStore,
-            dataElementGroupComboBox,
+            dataElementGroup,
             dataElementDetailLevel,
             dataElement,
             dataSetAvailable,
             dataSetSelected,
             dataSet,
-            rewind,
+			rewind,
             relativePeriod,
             fixedPeriodAvailable,
             fixedPeriodSelected,
@@ -1821,26 +1841,31 @@
             treePanel,
             userOrganisationUnit,
             userOrganisationUnitChildren,
-            userOrganisationUnitPanel,
+            userOrganisationUnitGrandChildren,
             organisationUnitLevel,
+            organisationUnitGroup,
+            toolMenu,
             tool,
             toolPanel,
             organisationUnit,
-            dimensionIdAvailableStoreMap = {},
-            dimensionIdSelectedStoreMap = {},
-            getDimensionPanels,
-            validateSpecialCases,
-            update,
+			dimensionIdAvailableStoreMap = {},
+			dimensionIdSelectedStoreMap = {},
+			getGroupSetPanels,
+			update,
 
+			accordionBody,
+            accordion,
+            westRegion,
             optionsButton,
             favoriteButton,
+            getParamString,
+            openTableLayoutTab,
             downloadButton,
-
-            accordionBody,
-            accordion,
-            westRegion,
+            interpretationItem,
+            pluginItem,
+            shareButton,
+            defaultButton,
             centerRegion,
-
             setGui,
             viewport,
 
@@ -2171,23 +2196,82 @@
 
 		indicatorAvailableStore = Ext.create('Ext.data.Store', {
 			fields: ['id', 'name'],
-			proxy: {
-				type: 'ajax',
-				reader: {
-					type: 'json',
-					root: 'indicators'
-				}
-			},
+            lastPage: null,
+            nextPage: 1,
+            isPending: false,
+            reset: function() {
+                this.removeAll();
+                this.lastPage = null;
+                this.nextPage = 1;
+                this.isPending = false;
+                indicatorSearch.hideFilter();
+            },
+            loadPage: function(uid, filter, append) {
+                var store = this,
+                    filterPath = filter ? '/query/' + filter : '',
+                    path;
+
+                uid = (Ext.isString(uid) || Ext.isNumber(uid)) ? uid : indicatorGroup.getValue();
+                filter = filter || indicatorFilter.getValue() || null;
+
+                if (!append) {
+                    this.lastPage = null;
+                    this.nextPage = 1;
+                }
+
+                if (store.nextPage === store.lastPage) {
+                    return;
+                }
+
+				if (Ext.isString(uid)) {
+					path = '/indicatorGroups/' + uid + '/members' + filterPath + '.json';
+				}
+				else if (uid === 0) {
+					path = '/indicators' + filterPath + '.json';
+				}
+
+				if (!path) {
+					console.log('Available indicators: invalid id');
+					return;
+				}
+
+                store.isPending = true;
+
+                Ext.Ajax.request({
+                    url: ns.core.init.contextPath + '/api' + path,
+                    params: {
+                        viewClass: 'basic',
+                        links: 'false',
+                        page: store.nextPage,
+                        pageSize: 50
+                    },
+                    failure: function() {
+                        store.isPending = false;
+                    },
+                    success: function(r) {
+                        var response = Ext.decode(r.responseText),
+                            data = response.indicators || [],
+                            pager = response.pager;
+
+                        store.loadStore(data, pager, append);
+                    }
+                });
+            },
+            loadStore: function(data, pager, append) {
+                this.loadData(data, append);
+                this.lastPage = this.nextPage;
+
+                if (pager.pageCount > this.nextPage) {
+                    this.nextPage++;
+                }
+
+                this.isPending = false;
+                ns.core.web.multiSelect.filterAvailable({store: indicatorAvailableStore}, {store: indicatorSelectedStore});
+            },
 			storage: {},
 			parent: null,
 			sortStore: function() {
 				this.sort('name', 'ASC');
-			},
-			listeners: {
-				load: function(s) {
-					ns.core.web.storage.internal.add(s);
-					ns.core.web.multiSelect.filterAvailable({store: s}, {store: indicatorSelectedStore});
-				}
 			}
 		});
 		ns.app.stores.indicatorAvailable = indicatorAvailableStore;
@@ -2232,80 +2316,135 @@
 
 		dataElementAvailableStore = Ext.create('Ext.data.Store', {
 			fields: ['id', 'name'],
-			proxy: {
-				type: 'ajax',
-				reader: {
-					type: 'json',
-					root: 'dataElements'
-				}
+            lastPage: null,
+            nextPage: 1,
+            isPending: false,
+            reset: function() {
+                this.removeAll();
+                this.lastPage = null;
+                this.nextPage = 1;
+                this.isPending = false;
+                dataElementSearch.hideFilter();
+            },
+            loadPage: function(uid, filter, append) {
+                uid = (Ext.isString(uid) || Ext.isNumber(uid)) ? uid : dataElementGroup.getValue();
+                filter = filter || dataElementFilter.getValue() || null;
+
+                if (!append) {
+                    this.lastPage = null;
+                    this.nextPage = 1;
+                }
+
+                if (dataElementDetailLevel.getValue() === dimConf.dataElement.objectName) {
+                    this.loadTotalsPage(uid, filter, append);
+                }
+                else if (dataElementDetailLevel.getValue() === dimConf.operand.objectName) {
+                    this.loadDetailsPage(uid, filter, append);
+                }
+            },
+            loadTotalsPage: function(uid, filter, append) {
+                var store = this,
+                    filterPath = filter ? '/query/' + filter : '',
+                    path;
+
+                if (store.nextPage === store.lastPage) {
+                    return;
+                }
+
+				if (Ext.isString(uid)) {
+					path = '/dataElementGroups/' + uid + '/members' + filterPath + '.json';
+				}
+				else if (uid === 0) {
+					path = '/dataElements' + filterPath + '.json?domainType=aggregate';
+				}
+
+				if (!path) {
+					alert('Available data elements: invalid id');
+					return;
+				}
+
+                store.isPending = true;
+
+                Ext.Ajax.request({
+                    url: ns.core.init.contextPath + '/api' + path,
+                    params: {
+                        viewClass: 'basic',
+                        links: 'false',
+                        page: store.nextPage,
+                        pageSize: 50
+                    },
+                    failure: function() {
+                        store.isPending = false;
+                    },
+                    success: function(r) {
+                        var response = Ext.decode(r.responseText),
+                            data = response.dataElements || [],
+                            pager = response.pager;
+
+                        store.loadStore(data, pager, append);
+                    }
+                });
+            },
+			loadDetailsPage: function(uid, filter, append) {
+                var store = this,
+                    filterPath = filter ? '/query/' + filter : '',
+                    path;
+
+                if (store.nextPage === store.lastPage) {
+                    return;
+                }
+
+				if (Ext.isString(uid)) {
+					path = '/dataElementGroups/' + uid + '/operands' + filterPath + '.json';
+				}
+				else if (uid === 0) {
+					path = '/generatedDataElementOperands' + filterPath + '.json';
+				}
+
+				if (!path) {
+					alert('Available data elements: invalid id');
+					return;
+				}
+
+                store.isPending = true;
+
+                Ext.Ajax.request({
+                    url: ns.core.init.contextPath + '/api' + path,
+                    params: {
+                        viewClass: 'basic',
+                        links: 'false',
+                        page: store.nextPage,
+                        pageSize: 50
+                    },
+                    failure: function() {
+                        store.isPending = false;
+                    },
+                    success: function(r) {
+                        var response = Ext.decode(r.responseText),
+							data = response.dataElementOperands || [],
+                            pager = response.pager;
+
+						for (var i = 0; i < data.length; i++) {
+							data[i].id = data[i].id.split('.').join('-');
+						}
+
+                        store.loadStore(data, pager, append);
+                    }
+                });
 			},
-			storage: {},
-			sortStore: function() {
+            loadStore: function(data, pager, append) {
+                this.loadData(data, append);
+                this.lastPage = this.nextPage;
+
+                if (pager.pageCount > this.nextPage) {
+                    this.nextPage++;
+                }
+
+                this.isPending = false;
+                ns.core.web.multiSelect.filterAvailable({store: dataElementAvailableStore}, {store: dataElementSelectedStore});
+            },
+            sortStore: function() {
 				this.sort('name', 'ASC');
-			},
-			setTotalsProxy: function(uid) {
-				var path;
-
-				if (Ext.isString(uid)) {
-					path = '/dataElementGroups/' + uid + '.json?domainType=aggregate&links=false&paging=false';
-				}
-				else if (uid === 0) {
-					path = '/dataElements.json?domainType=aggregate&paging=false&links=false';
-				}
-
-				if (!path) {
-					alert('Available data elements: invalid id');
-					return;
-				}
-
-				this.setProxy({
-					type: 'ajax',
-					url: ns.core.init.contextPath + '/api' + path,
-					reader: {
-						type: 'json',
-						root: 'dataElements'
-					}
-				});
-
-				this.load({
-					scope: this,
-					callback: function() {
-						ns.core.web.multiSelect.filterAvailable({store: dataElementAvailableStore}, {store: dataElementSelectedStore});
-					}
-				});
-			},
-			setDetailsProxy: function(uid) {
-				if (Ext.isString(uid)) {
-					this.setProxy({
-						type: 'ajax',
-						url: ns.core.init.contextPath + '/api/generatedDataElementOperands.json?links=false&dataElementGroup=' + uid,
-						reader: {
-							type: 'json',
-							root: 'dataElementOperands'
-						}
-					});
-
-					this.load({
-						scope: this,
-						callback: function() {
-							this.each(function(r) {
-								r.set('id', r.data.id.split('.').join('-'));
-							});
-
-							ns.core.web.multiSelect.filterAvailable({store: dataElementAvailableStore}, {store: dataElementSelectedStore});
-						}
-					});
-				}
-				else {
-					this.removeAll();
-                    dataElementGroupComboBox.clearValue();
-				}
-			},
-			listeners: {
-				load: function(s) {
-					ns.core.web.storage.internal.add(s);
-					ns.core.web.multiSelect.filterAvailable({store: s}, {store: dataElementSelectedStore});
-				}
 			}
 		});
 		ns.app.stores.dataElementAvailable = dataElementAvailableStore;
@@ -2328,13 +2467,11 @@
 			},
 			listeners: {
 				load: function(s) {
-					if (dataElementDetailLevel.getValue() === ns.core.conf.finals.dimension.dataElement.objectName) {
-						s.add({
-							id: 0,
-							name: '[ ' + NS.i18n.all_data_elements + ' ]',
-							index: -1
-						});
-					}
+                    s.add({
+                        id: 0,
+                        name: '[ ' + NS.i18n.all_data_elements + ' ]',
+                        index: -1
+                    });
 
 					s.sort([
 						{property: 'index', direction: 'ASC'},
@@ -2403,19 +2540,19 @@
 		});
 		ns.app.stores.fixedPeriodSelected = fixedPeriodSelectedStore;
 
-		chartStore = Ext.create('Ext.data.Store', {
+		reportTableStore = Ext.create('Ext.data.Store', {
 			fields: ['id', 'name', 'lastUpdated', 'access'],
 			proxy: {
 				type: 'ajax',
 				reader: {
 					type: 'json',
-					root: 'charts'
+					root: 'reportTables'
 				}
 			},
 			isLoaded: false,
 			pageSize: 10,
 			page: 1,
-			defaultUrl: ns.core.init.contextPath + '/api/charts.json?viewClass=sharing&links=false',
+			defaultUrl: ns.core.init.contextPath + '/api/reportTables.json?viewClass=sharing&links=false',
 			loadStore: function(url) {
 				this.proxy.url = url || this.defaultUrl;
 
@@ -2444,7 +2581,7 @@
 				}
 			}
 		});
-		ns.app.stores.chart = chartStore;
+		ns.app.stores.reportTable = reportTableStore;
 
 		organisationUnitLevelStore = Ext.create('Ext.data.Store', {
 			fields: ['id', 'name', 'level'],
@@ -2466,6 +2603,112 @@
 		ns.app.stores.organisationUnitGroup = organisationUnitGroupStore;
 
 		// data
+
+		isScrolled = function(e) {
+			var el = e.srcElement,
+				scrollBottom = el.scrollTop + ((el.clientHeight / el.scrollHeight) * el.scrollHeight);
+                
+			return scrollBottom / el.scrollHeight > 0.9;
+		};
+
+        indicatorLabel = Ext.create('Ext.form.Label', {
+            text: NS.i18n.available,
+            cls: 'ns-toolbar-multiselect-left-label',
+            style: 'margin-right:5px'
+        });
+
+        indicatorSearch = Ext.create('Ext.button.Button', {
+            width: 22,
+            height: 22,
+            cls: 'ns-button-icon',
+            disabled: true,
+            style: 'background: url(images/search_14.png) 3px 3px no-repeat',
+            showFilter: function() {
+                indicatorLabel.hide();
+                this.hide();
+                indicatorFilter.show();
+                indicatorFilter.reset();
+            },
+            hideFilter: function() {
+                indicatorLabel.show();
+                this.show();
+                indicatorFilter.hide();
+                indicatorFilter.reset();
+            },
+            handler: function() {
+                this.showFilter();
+            }
+        });
+
+        indicatorFilter = Ext.create('Ext.form.field.Trigger', {
+            cls: 'ns-trigger-filter',
+            emptyText: 'Filter available..',
+            height: 22,
+            hidden: true,
+            enableKeyEvents: true,
+            fieldStyle: 'height:22px; border-right:0 none',
+            style: 'height:22px',
+            onTriggerClick: function() {
+                this.reset();
+                this.onKeyUp();
+            },
+            onKeyUp: function() {
+                var value = indicatorGroup.getValue(),
+                    store = indicatorAvailableStore;
+
+                if (Ext.isString(value) || Ext.isNumber(value)) {
+                    store.loadPage(null, this.getValue(), false);
+                }
+            },
+            listeners: {
+                keyup: {
+                    fn: function(cmp) {
+                        cmp.onKeyUp();
+                    },
+                    buffer: 100
+                },
+                show: function(cmp) {
+                    cmp.focus(false, 50);
+                },
+                focus: function(cmp) {
+                    cmp.addCls('ns-trigger-filter-focused');
+                },
+                blur: function(cmp) {
+                    cmp.removeCls('ns-trigger-filter-focused');
+                }
+            }
+        });
+
+        indicatorGroup = Ext.create('Ext.form.field.ComboBox', {
+            cls: 'ns-combo',
+            style: 'margin-bottom:2px; margin-top:0px',
+            width: ns.core.conf.layout.west_fieldset_width - ns.core.conf.layout.west_width_padding,
+            valueField: 'id',
+            displayField: 'name',
+            emptyText: NS.i18n.select_indicator_group,
+            editable: false,
+            store: indicatorGroupStore,
+			loadAvailable: function(reset) {
+				var store = indicatorAvailableStore,
+					id = this.getValue();
+
+				if (id !== null) {
+                    if (reset) {
+                        store.reset();
+                    }
+
+                    store.loadPage(id, null, false);
+				}
+			},
+			listeners: {
+				select: function(cb) {
+					cb.loadAvailable(true);
+
+                    indicatorSearch.enable();
+				}
+			}
+        });
+
 		indicatorAvailable = Ext.create('Ext.ux.form.MultiSelect', {
 			cls: 'ns-toolbar-multiselect-left',
 			width: (ns.core.conf.layout.west_fieldset_width - ns.core.conf.layout.west_width_padding) / 2,
@@ -2473,11 +2716,9 @@
 			displayField: 'name',
 			store: indicatorAvailableStore,
 			tbar: [
-				{
-					xtype: 'label',
-					text: NS.i18n.available,
-					cls: 'ns-toolbar-multiselect-left-label'
-				},
+				indicatorLabel,
+                indicatorSearch,
+                indicatorFilter,
 				'->',
 				{
 					xtype: 'button',
@@ -2497,10 +2738,18 @@
 				}
 			],
 			listeners: {
-				afterrender: function() {
-					this.boundList.on('itemdblclick', function() {
-						ns.core.web.multiSelect.select(this, indicatorSelected);
-					}, this);
+				render: function(ms) {
+                    var el = Ext.get(ms.boundList.getEl().id + '-listEl').dom;
+
+                    el.addEventListener('scroll', function(e) {
+                        if (isScrolled(e) && !indicatorAvailableStore.isPending) {
+                            indicatorAvailableStore.loadPage(null, null, true);
+                        }
+                    });
+
+					ms.boundList.on('itemdblclick', function() {
+						ns.core.web.multiSelect.select(ms, indicatorSelected);
+					}, ms);
 				}
 			}
 		});
@@ -2575,46 +2824,13 @@
 				);
 			},
 			items: [
-				{
-					xtype: 'combobox',
-					cls: 'ns-combo',
-					style: 'margin-bottom:2px; margin-top:0px',
-					width: ns.core.conf.layout.west_fieldset_width - ns.core.conf.layout.west_width_padding,
-					valueField: 'id',
-					displayField: 'name',
-					emptyText: NS.i18n.select_indicator_group,
-					editable: false,
-					store: indicatorGroupStore,
-					listeners: {
-						select: function(cb) {
-							var store = indicatorAvailableStore,
-								id = cb.getValue();
-
-							store.parent = id;
-
-							if (ns.core.support.prototype.object.hasObject(store.storage, 'parent', id)) {
-								ns.core.web.storage.internal.load(store);
-								ns.core.web.multiSelect.filterAvailable(indicatorAvailable, indicatorSelected);
-							}
-							else {
-								if (id === 0) {
-									store.proxy.url = ns.core.init.contextPath + '/api/indicators.json?paging=false&links=false';
-									store.load();
-								}
-								else {
-									store.proxy.url = ns.core.init.contextPath + '/api/indicatorGroups/' + id + '.json';
-									store.load();
-								}
-							}
-						}
-					}
-				},
+				indicatorGroup,
 				{
 					xtype: 'panel',
 					layout: 'column',
 					bodyStyle: 'border-style:none',
 					items: [
-						indicatorAvailable,
+                        indicatorAvailable,
 						indicatorSelected
 					]
 				}
@@ -2629,18 +2845,86 @@
 			}
 		};
 
+		dataElementLabel = Ext.create('Ext.form.Label', {
+            text: NS.i18n.available,
+            cls: 'ns-toolbar-multiselect-left-label',
+            style: 'margin-right:5px'
+        });
+
+        dataElementSearch = Ext.create('Ext.button.Button', {
+            width: 22,
+            height: 22,
+            cls: 'ns-button-icon',
+            disabled: true,
+            style: 'background: url(images/search_14.png) 3px 3px no-repeat',
+            showFilter: function() {
+                dataElementLabel.hide();
+                this.hide();
+                dataElementFilter.show();
+                dataElementFilter.reset();
+            },
+            hideFilter: function() {
+                dataElementLabel.show();
+                this.show();
+                dataElementFilter.hide();
+                dataElementFilter.reset();
+            },
+            handler: function() {
+                this.showFilter();
+            }
+        });
+
+        dataElementFilter = Ext.create('Ext.form.field.Trigger', {
+            cls: 'ns-trigger-filter',
+            emptyText: 'Filter available..',
+            height: 22,
+            hidden: true,
+            enableKeyEvents: true,
+            fieldStyle: 'height:22px; border-right:0 none',
+            style: 'height:22px',
+            onTriggerClick: function() {
+                this.reset();
+                this.onKeyUp();
+            },
+            onKeyUp: function() {
+                var value = dataElementGroup.getValue(),
+                    store = dataElementAvailableStore;
+
+                if (Ext.isString(value) || Ext.isNumber(value)) {
+                    store.loadPage(null, this.getValue(), false);
+                }
+            },
+            listeners: {
+                keyup: {
+                    fn: function(cmp) {
+                        cmp.onKeyUp();
+                    },
+                    buffer: 100
+                },
+                show: function(cmp) {
+                    cmp.focus(false, 50);
+                },
+                focus: function(cmp) {
+                    cmp.addCls('ns-trigger-filter-focused');
+                },
+                blur: function(cmp) {
+                    cmp.removeCls('ns-trigger-filter-focused');
+                }
+            }
+        });
+
 		dataElementAvailable = Ext.create('Ext.ux.form.MultiSelect', {
 			cls: 'ns-toolbar-multiselect-left',
 			width: (ns.core.conf.layout.west_fieldset_width - ns.core.conf.layout.west_width_padding) / 2,
 			valueField: 'id',
 			displayField: 'name',
+            isPending: false,
+            page: 1,
 			store: dataElementAvailableStore,
 			tbar: [
-				{
-					xtype: 'label',
-					text: NS.i18n.available,
-					cls: 'ns-toolbar-multiselect-left-label'
-				},
+				dataElementLabel,
+                dataElementSearch,
+                dataElementFilter,
 				'->',
 				{
 					xtype: 'button',
@@ -2660,10 +2944,18 @@
 				}
 			],
 			listeners: {
-				afterrender: function() {
-					this.boundList.on('itemdblclick', function() {
-						ns.core.web.multiSelect.select(this, dataElementSelected);
-					}, this);
+				render: function(ms) {
+                    var el = Ext.get(ms.boundList.getEl().id + '-listEl').dom;
+
+                    el.addEventListener('scroll', function(e) {
+                        if (isScrolled(e) && !dataElementAvailableStore.isPending) {
+                            dataElementAvailableStore.loadPage(null, null, true);
+                        }
+                    });
+
+					ms.boundList.on('itemdblclick', function() {
+						ns.core.web.multiSelect.select(ms, dataElementSelected);
+					}, ms);
 				}
 			}
 		});
@@ -2708,7 +3000,7 @@
 			}
 		});
 
-		dataElementGroupComboBox = Ext.create('Ext.form.field.ComboBox', {
+		dataElementGroup = Ext.create('Ext.form.field.ComboBox', {
 			cls: 'ns-combo',
 			style: 'margin:0 2px 2px 0',
 			width: ns.core.conf.layout.west_fieldset_width - ns.core.conf.layout.west_width_padding - 90,
@@ -2717,23 +3009,23 @@
 			emptyText: NS.i18n.select_data_element_group,
 			editable: false,
 			store: dataElementGroupStore,
-			loadAvailable: function() {
+			loadAvailable: function(reset) {
 				var store = dataElementAvailableStore,
-					detailLevel = dataElementDetailLevel.getValue(),
-					value = this.getValue();
-
-				if (value !== null) {
-					if (detailLevel === dimConf.dataElement.objectName) {
-						store.setTotalsProxy(value);
-					}
-					else {
-						store.setDetailsProxy(value);
-					}
+					id = this.getValue();
+
+				if (id !== null) {
+                    if (reset) {
+                        store.reset();
+                    }
+
+                    store.loadPage(id, null, false);
 				}
 			},
 			listeners: {
 				select: function(cb) {
-					cb.loadAvailable();
+					cb.loadAvailable(true);
+
+                    dataElementSearch.enable();
 				}
 			}
 		});
@@ -2757,21 +3049,7 @@
 			},
 			listeners: {
 				select: function(cb) {
-					var record = dataElementGroupStore.getById(0);
-
-					if (cb.getValue() === ns.core.conf.finals.dimension.operand.objectName && record) {
-						dataElementGroupStore.remove(record);
-					}
-
-					if (cb.getValue() === ns.core.conf.finals.dimension.dataElement.objectName && !record) {
-						dataElementGroupStore.insert(0, {
-							id: 0,
-							name: '[ ' + NS.i18n.all_data_element_groups + ' ]',
-							index: -1
-						});
-					}
-
-					dataElementGroupComboBox.loadAvailable();
+					dataElementGroup.loadAvailable(true);
 					dataElementSelectedStore.removeAll();
 				}
 			}
@@ -2811,7 +3089,7 @@
 					xtype: 'container',
 					layout: 'column',
 					items: [
-						dataElementGroupComboBox,
+						dataElementGroup,
 						dataElementDetailLevel
 					]
 				},
@@ -2820,7 +3098,7 @@
 					layout: 'column',
 					bodyStyle: 'border-style:none',
 					items: [
-						dataElementAvailable,
+                        dataElementAvailable,
 						dataElementSelected
 					]
 				}
@@ -2969,6 +3247,7 @@
 		};
 
 		// period
+
 		rewind = Ext.create('Ext.form.field.Checkbox', {
 			relativePeriodId: 'rewind',
 			boxLabel: 'Rewind one period',
@@ -3524,6 +3803,7 @@
 		};
 
 		// organisation unit
+
 		treePanel = Ext.create('Ext.tree.Panel', {
 			cls: 'ns-tree',
 			style: 'border-top: 1px solid #ddd; padding-top: 1px',
@@ -3987,6 +4267,7 @@
         };
 
 		// dimensions
+
 		getDimensionPanels = function(dimensions, iconCls) {
 			var	getAvailableStore,
 				getSelectedStore,
@@ -4212,6 +4493,7 @@
 		};
 
 		// viewport
+        
 		update = function() {
 			var config = ns.core.web.chart.getLayoutConfig(),
 				layout = ns.core.api.layout.Layout(config);

=== modified file 'dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/styles/style.css'
--- dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/styles/style.css	2013-12-05 12:11:45 +0000
+++ dhis-2/dhis-web/dhis-web-visualizer/src/main/webapp/dhis-web-visualizer/app/styles/style.css	2014-02-04 14:39:04 +0000
@@ -112,6 +112,14 @@
 	opacity: 0.4;
 	cursor: default !important;
 }
+
+.disabled-toolbar {
+	opacity: 1;
+/*
+	cursor: default !important;
+*/
+}
+
 	/* Collapse splitter column */
 .x-splitter {
     display: none;
@@ -200,7 +208,29 @@
 
 	/* Combobox border-radius */
 .ns-combo input {
-    border-radius: 2px 0px 0px 0px;
+    border-radius: 1px 0px 0px 0px;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PT Trigger
+ *--------------------------------------------------------------------------*/
+
+    /* field default > trigger */
+.ns-trigger-filter .x-form-trigger {
+    height: 22px !important;
+    background: url(../images/clear_20.png) 0 0 no-repeat;
+    border-top: 1px solid #b5b8c8;
+    border-right: 1px solid #b5b8c8;
+}
+    /* field focused > trigger */
+.ns-trigger-filter-focused .x-form-trigger {
+    border: 1px solid #a1a1a1;
+    border-left: 0 none;
+}
+    /* field over > trigger */
+.ns-trigger-filter .x-form-trigger-over {
+    background: url(../images/clear-over_20.png) 0 0 no-repeat;
 }
 
 
@@ -239,14 +269,15 @@
 }
 
 .ns-toolbar-multiselect-left .x-docked-top {
-	border-top-left-radius: 2px;
+	border-top-left-radius: 1px;
+	border-right: 0 none;
 }
 .ns-toolbar-multiselect-right .x-docked-top {
-	border-top-right-radius: 2px;
+	border-top-right-radius: 1px;
 }
 .ns-toolbar-multiselect-leftright .x-docked-top {
-	border-top-left-radius: 2px;
-	border-top-right-radius: 2px;
+	border-top-left-radius: 1px;
+	border-top-right-radius: 1px;
 }
 
 	/* Multiselect docked bar font size */
@@ -254,6 +285,7 @@
 .ns-toolbar-multiselect-right .ns-toolbar-multiselect-right-label,
 .ns-toolbar-multiselect-leftright .ns-toolbar-multiselect-leftright-label {
     font-size: 11px;
+    color: #222;
 }
 .ns-toolbar-multiselect-left .ns-toolbar-multiselect-left-label {
 	padding-left: 6px;
@@ -277,6 +309,10 @@
     border-color: #e5e5e5;
 }
 
+.ns-toolbar-multiselect-left .x-panel-body {
+	border-right: 0 none;
+}
+
 
 /*----------------------------------------------------------------------------
  * Checkbox
@@ -489,6 +525,11 @@
 	width: 18px !important;
 }
 
+.ns-button-icon.x-btn-default-toolbar-small-disabled {
+    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=25)";
+    opacity: 0.25;
+}
+
 
 /*----------------------------------------------------------------------------
  * Tooltip