ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From akuznet...@apache.org
Subject [3/4] ignite git commit: IGNITE-2347 WIP reworking metadata load.
Date Mon, 11 Jan 2016 17:44:21 GMT
http://git-wip-us.apache.org/repos/asf/ignite/blob/65f74f5f/modules/control-center-web/src/main/js/controllers/metadata-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/metadata-controller.js b/modules/control-center-web/src/main/js/controllers/metadata-controller.js
index 5016b60..5b24d12 100644
--- a/modules/control-center-web/src/main/js/controllers/metadata-controller.js
+++ b/modules/control-center-web/src/main/js/controllers/metadata-controller.js
@@ -18,340 +18,348 @@
 // Controller for Metadata screen.
 consoleModule.controller('metadataController', function ($filter, $http, $timeout, $state, $scope, $controller, $modal,
      $common,  $focus, $confirm, $confirmBatch, $clone, $table, $preview, $loading, $unsavedChangesGuard, $agentDownload) {
-            $unsavedChangesGuard.install($scope);
-
-            // Initialize the super class and extend it.
-            angular.extend(this, $controller('save-remove', {$scope: $scope}));
-
-            $scope.ui = $common.formUI();
-
-            $scope.ui.packageName = $commonUtils.toJavaPackageName($scope.$root.user.email.replace('@', '.').split('.')
-                .reverse().join('.') + '.model');
-            $scope.ui.builtinKeys = true;
-            $scope.ui.usePrimitives = true;
-            $scope.ui.generateCaches = true;
-            $scope.ui.generatedCachesClusters = [];
-
-            $scope.joinTip = $common.joinTip;
-            $scope.getModel = $common.getModel;
-            $scope.javaBuildInClasses = $common.javaBuildInClasses;
-            $scope.compactJavaName = $common.compactJavaName;
-            $scope.widthIsSufficient = $common.widthIsSufficient;
-            $scope.saveBtnTipText = $common.saveBtnTipText;
-            $scope.panelExpanded = $common.panelExpanded;
-
-            $scope.tableVisibleRow = $table.tableVisibleRow;
-
-            $scope.tableSave = function (field, index, stopEdit) {
-                if ($table.tableEditing({model: 'table-index-fields'}, $table.tableEditedRowIndex())) {
-                    if ($scope.tableIndexItemSaveVisible(field, index))
-                        return $scope.tableIndexItemSave(field, field.indexIdx, index, stopEdit);
-                }
-                else {
-                    switch (field.type) {
-                        case 'fields':
-                        case 'aliases':
-                            if ($table.tablePairSaveVisible(field, index))
-                                return $table.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit);
+        $unsavedChangesGuard.install($scope);
+
+        // Initialize the super class and extend it.
+        angular.extend(this, $controller('save-remove', {$scope: $scope}));
+
+        $scope.ui = $common.formUI();
+
+        $scope.ui.packageName = $commonUtils.toJavaPackageName($scope.$root.user.email.replace('@', '.')
+            .split('.').reverse().join('.') + '.model');
+        $scope.ui.builtinKeys = true;
+        $scope.ui.usePrimitives = true;
+        $scope.ui.generateCaches = true;
+        $scope.ui.generatedCachesClusters = [];
+
+        $scope.joinTip = $common.joinTip;
+        $scope.getModel = $common.getModel;
+        $scope.javaBuildInClasses = $common.javaBuildInClasses;
+        $scope.compactJavaName = $common.compactJavaName;
+        $scope.widthIsSufficient = $common.widthIsSufficient;
+        $scope.saveBtnTipText = $common.saveBtnTipText;
+        $scope.panelExpanded = $common.panelExpanded;
+
+        $scope.tableVisibleRow = $table.tableVisibleRow;
+
+        $scope.tableSave = function (field, index, stopEdit) {
+            if ($table.tableEditing({model: 'table-index-fields'}, $table.tableEditedRowIndex())) {
+                if ($scope.tableIndexItemSaveVisible(field, index))
+                    return $scope.tableIndexItemSave(field, field.indexIdx, index, stopEdit);
+            }
+            else {
+                switch (field.type) {
+                    case 'fields':
+                    case 'aliases':
+                        if ($table.tablePairSaveVisible(field, index))
+                            return $table.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit);
 
-                            break;
+                        break;
 
-                        case 'indexes':
-                            if ($scope.tableIndexSaveVisible(field, index))
-                                return $scope.tableIndexSave(field, index, stopEdit);
+                    case 'indexes':
+                        if ($scope.tableIndexSaveVisible(field, index))
+                            return $scope.tableIndexSave(field, index, stopEdit);
 
-                            break;
+                        break;
 
-                        case 'table-db-fields':
-                            if ($scope.tableDbFieldSaveVisible(field, index))
-                                return $scope.tableDbFieldSave(field, index, stopEdit);
+                    case 'table-db-fields':
+                        if ($scope.tableDbFieldSaveVisible(field, index))
+                            return $scope.tableDbFieldSave(field, index, stopEdit);
 
-                            break;
-                    }
+                        break;
                 }
+            }
 
-                return true;
-            };
-
-            $scope.tableReset = function (save) {
-                var field = $table.tableField();
-
-                if (!save || !$common.isDefined(field) || $scope.tableSave(field, $table.tableEditedRowIndex(), true)) {
-                    $table.tableReset();
-
-                    return true;
-                }
-
-                return false;
-            };
-
-            $scope.tableNewItem = function (field) {
-                if ($scope.tableReset(true))
-                    $table.tableNewItem(field);
-            };
-
-            $scope.tableNewItemActive = $table.tableNewItemActive;
-
-            $scope.tableStartEdit = function (item, field, index) {
-                if ($scope.tableReset(true))
-                    $table.tableStartEdit(item, field, index);
-            };
-
-            $scope.tableEditing = $table.tableEditing;
-
-            $scope.tableRemove = function (item, field, index) {
-                if ($scope.tableReset(true))
-                    $table.tableRemove(item, field, index);
-            };
-
-            $scope.tableSimpleSave = $table.tableSimpleSave;
-            $scope.tableSimpleSaveVisible = $table.tableSimpleSaveVisible;
-
-            $scope.tableSimpleUp = function (item, field, index) {
-                if ($scope.tableReset(true))
-                    $table.tableSimpleUp(item, field, index);
-            };
-
-            $scope.tableSimpleDown = function (item, field, index) {
-                if ($scope.tableReset(true))
-                    $table.tableSimpleDown(item, field, index);
-            };
-
-            $scope.tableSimpleDownVisible = $table.tableSimpleDownVisible;
-
-            $scope.tablePairStartEdit = $table.tablePairStartEdit;
-            $scope.tablePairSave = $table.tablePairSave;
-            $scope.tablePairSaveVisible = $table.tablePairSaveVisible;
-
-            var INFO_CONNECT_TO_DB = 'Configure connection to database';
-            var INFO_SELECT_SCHEMAS = 'Select schemas to load tables from';
-            var INFO_SELECT_TABLES = 'Select tables to load as cache type metadata';
-            var INFO_SELECT_OPTIONS = 'Select load metadata options';
-            var LOADING_JDBC_DRIVERS = {text: 'Loading JDBC drivers...'};
-            var LOADING_SCHEMAS = {text: 'Loading schemas...'};
-            var LOADING_TABLES = {text: 'Loading tables...'};
-            var LOADING_METADATA = {text: 'Loading metadata...'};
-
-            var previews = [];
-
-            $scope.previewInit = function (preview) {
-                previews.push(preview);
+            return true;
+        };
 
-                $preview.previewInit(preview);
-            };
+        $scope.tableReset = function (save) {
+            var field = $table.tableField();
 
-            $scope.previewChanged = $preview.previewChanged;
+            if (!save || !$common.isDefined(field) || $scope.tableSave(field, $table.tableEditedRowIndex(), true)) {
+                $table.tableReset();
 
-            $scope.hidePopover = $common.hidePopover;
+                return true;
+            }
 
-            var showPopoverMessage = $common.showPopoverMessage;
+            return false;
+        };
+
+        $scope.tableNewItem = function (field) {
+            if ($scope.tableReset(true))
+                $table.tableNewItem(field);
+        };
+
+        $scope.tableNewItemActive = $table.tableNewItemActive;
+
+        $scope.tableStartEdit = function (item, field, index) {
+            if ($scope.tableReset(true))
+                $table.tableStartEdit(item, field, index);
+        };
+
+        $scope.tableEditing = $table.tableEditing;
+
+        $scope.tableRemove = function (item, field, index) {
+            if ($scope.tableReset(true))
+                $table.tableRemove(item, field, index);
+        };
+
+        $scope.tableSimpleSave = $table.tableSimpleSave;
+        $scope.tableSimpleSaveVisible = $table.tableSimpleSaveVisible;
+
+        $scope.tableSimpleUp = function (item, field, index) {
+            if ($scope.tableReset(true))
+                $table.tableSimpleUp(item, field, index);
+        };
+
+        $scope.tableSimpleDown = function (item, field, index) {
+            if ($scope.tableReset(true))
+                $table.tableSimpleDown(item, field, index);
+        };
+
+        $scope.tableSimpleDownVisible = $table.tableSimpleDownVisible;
+
+        $scope.tablePairStartEdit = $table.tablePairStartEdit;
+        $scope.tablePairSave = $table.tablePairSave;
+        $scope.tablePairSaveVisible = $table.tablePairSaveVisible;
+
+        var INFO_CONNECT_TO_DB = 'Configure connection to database';
+        var INFO_SELECT_SCHEMAS = 'Select schemas to load tables from';
+        var INFO_SELECT_TABLES = 'Select tables to load as cache type metadata';
+        var INFO_SELECT_OPTIONS = 'Select load metadata options';
+        var LOADING_JDBC_DRIVERS = {text: 'Loading JDBC drivers...'};
+        var LOADING_SCHEMAS = {text: 'Loading schemas...'};
+        var LOADING_TABLES = {text: 'Loading tables...'};
+        var LOADING_METADATA = {text: 'Loading metadata...'};
+
+        var previews = [];
+
+        $scope.previewInit = function (preview) {
+            previews.push(preview);
+
+            $preview.previewInit(preview);
+        };
+
+        $scope.previewChanged = $preview.previewChanged;
+
+        $scope.hidePopover = $common.hidePopover;
+
+        var showPopoverMessage = $common.showPopoverMessage;
+
+        $scope.preview = {
+            general: {xml: '', java: '', allDefaults: true},
+            query: {xml: '', java: '', allDefaults: true},
+            store: {xml: '', java: '', allDefaults: true}
+        };
+
+        $scope.indexType = $common.mkOptions(['SORTED', 'FULLTEXT', 'GEOSPATIAL']);
+
+        var _dbPresets = [
+            {
+                db: 'oracle',
+                jdbcDriverClass: 'oracle.jdbc.OracleDriver',
+                jdbcUrl: 'jdbc:oracle:thin:@[host]:[port]:[database]',
+                user: 'system'
+            },
+            {
+                db: 'db2',
+                jdbcDriverClass: 'com.ibm.db2.jcc.DB2Driver',
+                jdbcUrl: 'jdbc:db2://[host]:[port]/[database]',
+                user: 'db2admin'
+            },
+            {
+                db: 'mssql',
+                jdbcDriverClass: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
+                jdbcUrl: 'jdbc:sqlserver://[host]:[port][;databaseName=database]',
+                user: 'sa'
+            },
+            {
+                db: 'postgre',
+                jdbcDriverClass: 'org.postgresql.Driver',
+                jdbcUrl: 'jdbc:postgresql://[host]:[port]/[database]',
+                user: 'sa'
+            },
+            {
+                db: 'mysql',
+                jdbcDriverClass: 'com.mysql.jdbc.Driver',
+                jdbcUrl: 'jdbc:mysql://[host]:[port]/[database]',
+                user: 'root'
+            },
+            {
+                db: 'h2',
+                jdbcDriverClass: 'org.h2.Driver',
+                jdbcUrl: 'jdbc:h2:tcp://[host]/[database]',
+                user: 'sa'
+            }
+        ];
 
-            $scope.preview = {
-                general: {xml: '', java: '', allDefaults: true},
-                query: {xml: '', java: '', allDefaults: true},
-                store: {xml: '', java: '', allDefaults: true}
-            };
+        function _loadPresets () {
+            try {
+               var restoredPresets =  JSON.parse(localStorage.dbPresets);
 
-            $scope.indexType = $common.mkOptions(['SORTED', 'FULLTEXT', 'GEOSPATIAL']);
+                _.forEach(restoredPresets, function (restoredPreset) {
+                    var preset = _.find(_dbPresets, { jdbcDriverClass: restoredPreset.jdbcDriverClass });
 
-            var _dbPresets = [
-                {
-                    db: 'oracle',
-                    jdbcDriverClass: 'oracle.jdbc.OracleDriver',
-                    jdbcUrl: 'jdbc:oracle:thin:@[host]:[port]:[database]',
-                    user: 'system'
-                },
-                {
-                    db: 'db2',
-                    jdbcDriverClass: 'com.ibm.db2.jcc.DB2Driver',
-                    jdbcUrl: 'jdbc:db2://[host]:[port]/[database]',
-                    user: 'db2admin'
-                },
-                {
-                    db: 'mssql',
-                    jdbcDriverClass: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
-                    jdbcUrl: 'jdbc:sqlserver://[host]:[port][;databaseName=database]',
-                    user: 'sa'
-                },
-                {
-                    db: 'postgre',
-                    jdbcDriverClass: 'org.postgresql.Driver',
-                    jdbcUrl: 'jdbc:postgresql://[host]:[port]/[database]',
-                    user: 'sa'
-                },
-                {
-                    db: 'mysql',
-                    jdbcDriverClass: 'com.mysql.jdbc.Driver',
-                    jdbcUrl: 'jdbc:mysql://[host]:[port]/[database]',
-                    user: 'root'
-                },
-                {
-                    db: 'h2',
-                    jdbcDriverClass: 'org.h2.Driver',
-                    jdbcUrl: 'jdbc:h2:tcp://[host]/[database]',
-                    user: 'sa'
-                }
-            ];
+                    if (preset) {
+                        preset.jdbcUrl = restoredPreset.jdbcUrl;
 
-            function _loadPresets () {
-                try {
-                   var restoredPresets =  JSON.parse(localStorage.dbPresets);
+                        preset.user = restoredPreset.user;
+                    }
+                });
+            }
+            catch (ignore) {
+                // No-op.
+            }
+        }
 
-                    _.forEach(restoredPresets, function (restoredPreset) {
-                        var preset = _.find(_dbPresets, { jdbcDriverClass: restoredPreset.jdbcDriverClass });
+        _loadPresets();
 
-                        if (preset) {
-                            preset.jdbcUrl = restoredPreset.jdbcUrl;
+        function _savePreset (preset) {
+            try {
+                var oldPreset = _.find(_dbPresets, { jdbcDriverClass: preset.jdbcDriverClass });
 
-                            preset.user = restoredPreset.user;
-                        }
-                    });
+                if (oldPreset)
+                    angular.extend(oldPreset, preset);
+                else
+                    _dbPresets.push(preset);
 
-                }
-                catch (ignore) {
-                    // No-op.
-                }
+                localStorage.dbPresets = JSON.stringify(_dbPresets);
+            }
+            catch (errMsg) {
+                $common.showError(errMsg);
             }
+        }
 
-            _loadPresets();
+        $scope.preset = {
+            db: 'unknown',
+            jdbcDriverClass: '',
+            jdbcDriverJar: '',
+            jdbcUrl: 'jdbc:[database]',
+            user: 'sa',
+            tablesOnly: true
+        };
 
-            function _savePreset (preset) {
-                try {
-                    if (preset.demo)
-                        return;
+        $scope.selectedPreset = {};
 
-                    var oldPreset = _.find(_dbPresets, { jdbcDriverClass: preset.jdbcDriverClass });
+        $scope.ui.showValid = true;
 
-                    if (oldPreset)
-                        angular.extend(oldPreset, preset);
-                    else
-                        _dbPresets.push(preset);
+        function _findPreset(idx) {
+            var selected = $scope.jdbcDriverJars[idx];
 
-                    localStorage.dbPresets = JSON.stringify(_dbPresets);
-                }
-                catch (errMsg) {
-                    $common.showError(errMsg);
-                }
-            }
+            idx = _.findIndex(_dbPresets, function (preset) {
+                return preset.jdbcDriverClass === selected.jdbcDriverClass;
+            });
+
+            if (idx >= 0)
+                return _dbPresets[idx];
 
-            $scope.preset = {
+            return {
                 db: 'unknown',
-                jdbcDriverClass: '',
-                jdbcDriverJar: '',
+                jdbcDriverClass: selected.jdbcDriverClass,
+                jdbcDriverJar: selected.jdbcDriverJar,
                 jdbcUrl: 'jdbc:[database]',
-                user: 'sa',
-                tablesOnly: true
+                user: 'admin'
             };
+        }
 
-            $scope.ui.showValid = true;
-
-            function _findPreset(idx) {
-                var selected = $scope.jdbcDriverJars[idx];
+        $scope.$watch('preset.drvIdx', function (drvIdx) {
+            if (_.isNumber(drvIdx)) {
+                var preset = $scope.jdbcDriverJars[drvIdx];
 
-                idx = _.findIndex(_dbPresets, function (preset) {
-                    return preset.jdbcDriverClass === selected.jdbcDriverClass;
-                });
+                var jdbcDriverJar = preset.jdbcDriverJar;
 
-                if (idx >= 0)
-                    return _dbPresets[idx];
+                preset = _findPreset(drvIdx);
 
-                return {
-                    db: 'unknown',
-                    jdbcDriverClass: selected.jdbcDriverClass,
-                    jdbcDriverJar: selected.jdbcDriverJar,
-                    jdbcUrl: 'jdbc:[database]',
-                    user: 'admin'
-                };
-            }
+                var newPreset = angular.copy(preset);
 
-            $scope.$watch('preset.drvIdx', function (drvIdx) {
-                if (_.isNumber(drvIdx)) {
-                    var preset = $scope.jdbcDriverJars[drvIdx];
+                newPreset.jdbcDriverJar = jdbcDriverJar;
+                newPreset.drvIdx = drvIdx;
+                newPreset.tablesOnly = $scope.preset.tablesOnly;
 
-                    var jdbcDriverJar = preset.jdbcDriverJar;
-
-                    if (!preset.demo)
-                        preset = _findPreset(drvIdx);
-
-                    var newPreset = angular.copy(preset);
+                $scope.preset = newPreset;
+            }
+        }, true);
 
-                    newPreset.jdbcDriverJar = jdbcDriverJar;
-                    newPreset.drvIdx = drvIdx;
-                    newPreset.tablesOnly = $scope.preset.tablesOnly;
+        $scope.supportedJdbcTypes = $common.mkOptions($common.SUPPORTED_JDBC_TYPES);
 
-                    $scope.preset = newPreset;
-                }
-            }, true);
+        $scope.supportedJavaTypes = $common.mkOptions($common.javaBuildInTypes);
 
-            $scope.supportedJdbcTypes = $common.mkOptions($common.SUPPORTED_JDBC_TYPES);
+        $scope.sortDirections = [
+            {value: true, label: 'ASC'},
+            {value: false, label: 'DESC'}
+        ];
 
-            $scope.supportedJavaTypes = $common.mkOptions($common.javaBuildInTypes);
+        $scope.panels = {activePanels: [0, 1]};
 
-            $scope.sortDirections = [
-                {value: true, label: 'ASC'},
-                {value: false, label: 'DESC'}
-            ];
+        $scope.metadatas = [];
 
-            $scope.panels = {activePanels: [0, 1]};
+        $scope.isJavaBuildInClass = function () {
+            var item = $scope.backupItem;
 
-            $scope.metadatas = [];
+            if (item && item.keyType)
+                return $common.isJavaBuildInClass(item.keyType);
 
-            $scope.isJavaBuildInClass = function () {
-                var item = $scope.backupItem;
+            return false;
+        };
 
-                if (item && item.keyType)
-                    return $common.isJavaBuildInClass(item.keyType);
+        $scope.selectAllSchemas = function () {
+            var allSelected = $scope.loadMeta.allSchemasSelected;
 
-                return false;
-            };
+            _.forEach($scope.loadMeta.displayedSchemas, function (schema) {
+                schema.use = allSelected;
+            });
+        };
 
-            $scope.selectAllSchemas = function () {
-                var allSelected = $scope.loadMeta.allSchemasSelected;
+        $scope.selectSchema = function () {
+            if ($common.isDefined($scope.loadMeta) && $common.isDefined($scope.loadMeta.displayedSchemas))
+                $scope.loadMeta.allSchemasSelected = $scope.loadMeta.displayedSchemas.length > 0 &&
+                    _.every($scope.loadMeta.displayedSchemas, 'use', true);
+        };
 
-                _.forEach($scope.loadMeta.displayedSchemas, function (schema) {
-                    schema.use = allSelected;
-                });
-            };
+        $scope.selectAllTables = function () {
+            var allSelected = $scope.loadMeta.allTablesSelected;
 
-            $scope.selectSchema = function () {
-                if ($common.isDefined($scope.loadMeta) && $common.isDefined($scope.loadMeta.displayedSchemas))
-                    $scope.loadMeta.allSchemasSelected = $scope.loadMeta.displayedSchemas.length > 0 &&
-                        _.every($scope.loadMeta.displayedSchemas, 'use', true);
-            };
+            _.forEach($scope.loadMeta.displayedTables, function (table) {
+                table.use = allSelected;
+            });
+        };
 
-            $scope.selectAllTables = function () {
-                var allSelected = $scope.loadMeta.allTablesSelected;
+        $scope.selectTable = function () {
+            if ($common.isDefined($scope.loadMeta) && $common.isDefined($scope.loadMeta.displayedTables))
+                $scope.loadMeta.allTablesSelected = $scope.loadMeta.displayedTables.length > 0 &&
+                    _.every($scope.loadMeta.displayedTables, 'use', true);
+        };
 
-                _.forEach($scope.loadMeta.displayedTables, function (table) {
-                    table.use = allSelected;
-                });
-            };
+        $scope.$watch('loadMeta.displayedSchemas', $scope.selectSchema);
 
-            $scope.selectTable = function () {
-                if ($common.isDefined($scope.loadMeta) && $common.isDefined($scope.loadMeta.displayedTables))
-                    $scope.loadMeta.allTablesSelected = $scope.loadMeta.displayedTables.length > 0 &&
-                        _.every($scope.loadMeta.displayedTables, 'use', true);
-            };
+        $scope.$watch('loadMeta.displayedTables', $scope.selectTable);
 
-            $scope.$watch('loadMeta.displayedSchemas', $scope.selectSchema);
+        // Pre-fetch modal dialogs.
+        var loadMetaModal = $modal({scope: $scope, templateUrl: '/configuration/metadata-load.html', show: false});
 
-            $scope.$watch('loadMeta.displayedTables', $scope.selectTable);
+        var hideLoadMetadata = loadMetaModal.hide;
 
-            // Pre-fetch modal dialogs.
-            var loadMetaModal = $modal({scope: $scope, templateUrl: '/configuration/metadata-load.html', show: false});
+        loadMetaModal.hide = function () {
+            $agentDownload.stopAwaitAgent();
 
-            var hideLoadMetadata = loadMetaModal.hide;
+            hideLoadMetadata();
+        };
 
-            loadMetaModal.hide = function () {
-                $agentDownload.stopAwaitAgent();
+        /**
+         * Show load metadata modal.
+         *
+         * @param demo If 'true' then load metadata from demo database.
+         */
+        $scope.showLoadMetadataModal = function (demo) {
+            $table.tableReset();
 
-                hideLoadMetadata();
-            };
+            $common.confirmUnsavedChanges($scope.ui.isDirty(), function () {
+                if ($scope.ui.isDirty())
+                    $scope.backupItem = $scope.selectedItem ? angular.copy($scope.selectedItem) : prepareNewItem();
 
-            // Show load metadata modal.
-            function _showLoadMetadataModal () {
                 $scope.loadMeta = {
-                    action: 'connect',
+                    demo: demo,
+                    action: 'drivers',
                     schemas: [],
                     allSchemasSelected: false,
                     tables: [],
@@ -360,7 +368,6 @@ consoleModule.controller('metadataController', function ($filter, $http, $timeou
                     info: ''
                 };
 
-                $scope.loadMeta.action = 'drivers';
                 $scope.loadMeta.loadingOptions = LOADING_JDBC_DRIVERS;
 
                 $agentDownload.awaitAgent(function (result, onSuccess, onException) {
@@ -382,16 +389,14 @@ consoleModule.controller('metadataController', function ($filter, $http, $timeou
                                     });
 
                                     if (_h2DrvJar) {
-                                        $scope.jdbcDriverJars.push({
-                                            label: 'Demo database for load metadata',
-                                            value: $scope.jdbcDriverJars.length,
+                                        $scope.demoConnection = {
                                             jdbcDriverJar: _h2DrvJar.jdbcDriverJar,
                                             jdbcDriverClass: 'org.h2.Driver',
                                             jdbcUrl: 'jdbc:h2:mem:demo-db',
                                             user: 'sa',
                                             password: '',
-                                            demo: true
-                                        });
+                                            tablesOnly: true
+                                        };
                                     }
 
                                     drivers.forEach(function (driver) {
@@ -431,1053 +436,1037 @@ consoleModule.controller('metadataController', function ($filter, $http, $timeou
                             });
                     }
                 });
-            }
-
-            // Show load metadata modal.
-            $scope.showLoadMetadataModal = function () {
-                $table.tableReset();
-
-                $common.confirmUnsavedChanges($scope.ui.isDirty(), function () {
-                    if ($scope.ui.isDirty())
-                        $scope.backupItem = $scope.selectedItem ? angular.copy($scope.selectedItem) : prepareNewItem();
-
-                    _showLoadMetadataModal();
-                });
-            };
-
-            /**
-             * Load list of database schemas.
-             */
-            function _loadSchemas() {
-                $loading.start('loadingMetadataFromDb');
-
-                var preset = angular.copy($scope.preset);
-
-                if (preset.jdbcUrl === 'jdbc:h2:mem:demo-db') {
-                    preset.user = 'sa';
-                    preset.password = '';
-                }
+            });
+        };
 
-                $http.post('/api/v1/agent/schemas', preset)
-                    .success(function (schemas) {
-                        $scope.loadMeta.schemas = _.map(schemas, function (schema) {
-                            return {use: false, name: schema};
-                        });
-                        $scope.loadMeta.action = 'schemas';
+        /**
+         * Load list of database schemas.
+         */
+        function _loadSchemas() {
+            $loading.start('loadingMetadataFromDb');
 
-                        if ($scope.loadMeta.schemas.length === 0)
-                            $scope.loadMetadataNext();
-                        else
-                            _.forEach($scope.loadMeta.schemas, function (sch) {
-                                sch.use = true;
-                            });
-                    })
-                    .error(function (errMsg) {
-                        $common.showError(errMsg);
-                    })
-                    .finally(function() {
-                        $scope.loadMeta.info = INFO_SELECT_SCHEMAS;
-                        $scope.loadMeta.loadingOptions = LOADING_TABLES;
+            $scope.selectedPreset = $scope.loadMeta.demo ? angular.copy($scope.demoConnection) : angular.copy($scope.preset);
 
-                        $loading.finish('loadingMetadataFromDb');
+            $http.post('/api/v1/agent/schemas', $scope.selectedPreset)
+                .success(function (schemas) {
+                    $scope.loadMeta.schemas = _.map(schemas, function (schema) {
+                        return {use: false, name: schema};
                     });
-            }
-
-            /**
-             * Load list of database tables.
-             */
-            function _loadTables() {
-                $loading.start('loadingMetadataFromDb');
+                    $scope.loadMeta.action = 'schemas';
 
-                $scope.loadMeta.allTablesSelected = false;
-                $scope.preset.schemas = [];
+                    if ($scope.loadMeta.schemas.length === 0)
+                        $scope.loadMetadataNext();
+                    else
+                        _.forEach($scope.loadMeta.schemas, function (sch) {
+                            sch.use = true;
+                        });
+                })
+                .error(function (errMsg) {
+                    $common.showError(errMsg);
+                })
+                .finally(function() {
+                    $scope.loadMeta.info = INFO_SELECT_SCHEMAS;
+                    $scope.loadMeta.loadingOptions = LOADING_TABLES;
 
-                _.forEach($scope.loadMeta.schemas, function (schema) {
-                    if (schema.use)
-                        $scope.preset.schemas.push(schema.name);
+                    $loading.finish('loadingMetadataFromDb');
                 });
-
-                $http.post('/api/v1/agent/metadata', $scope.preset)
-                    .success(function (tables) {
-                        tables.forEach(function (tbl) {
-                            Object.defineProperty(tbl, 'label', {
-                                get: function () {
-                                    return tbl.schema + '.' + tbl.tbl;
-                                }
-                            });
-
-                            tbl.use = $common.isDefined(_.find(tbl.cols, function (col) {
-                                return col.key;
-                            }));
+        }
+
+        /**
+         * Load list of database tables.
+         */
+        function _loadTables() {
+            $loading.start('loadingMetadataFromDb');
+
+            $scope.loadMeta.allTablesSelected = false;
+            $scope.selectedPreset.schemas = [];
+
+            _.forEach($scope.loadMeta.schemas, function (schema) {
+                if (schema.use)
+                    $scope.selectedPreset.schemas.push(schema.name);
+            });
+
+            $http.post('/api/v1/agent/metadata', $scope.selectedPreset)
+                .success(function (tables) {
+                    tables.forEach(function (tbl) {
+                        Object.defineProperty(tbl, 'label', {
+                            get: function () {
+                                return tbl.schema + '.' + tbl.tbl;
+                            }
                         });
 
-                        $scope.loadMeta.tables = tables;
-                        $scope.loadMeta.action = 'tables';
-                    })
-                    .error(function (errMsg) {
-                        $common.showError(errMsg);
-                    })
-                    .finally(function() {
-                        $scope.loadMeta.info = INFO_SELECT_TABLES;
-                        $scope.loadMeta.loadingOptions = LOADING_METADATA;
-
-                        $loading.finish('loadingMetadataFromDb');
+                        tbl.use = $common.isDefined(_.find(tbl.cols, function (col) {
+                            return col.key;
+                        }));
                     });
-            }
-
-            /**
-             * Show page with load metadata options.
-             */
-            function _selectOptions() {
-                $scope.loadMeta.action = 'options';
-                $scope.loadMeta.button = 'Save';
-                $scope.loadMeta.info = INFO_SELECT_OPTIONS;
-            }
 
-            function toJavaClassName(name) {
-                var len = name.length;
+                    $scope.loadMeta.tables = tables;
+                    $scope.loadMeta.action = 'tables';
+                })
+                .error(function (errMsg) {
+                    $common.showError(errMsg);
+                })
+                .finally(function() {
+                    $scope.loadMeta.info = INFO_SELECT_TABLES;
+                    $scope.loadMeta.loadingOptions = LOADING_METADATA;
 
-                var buf = '';
+                    $loading.finish('loadingMetadataFromDb');
+                });
+        }
 
-                var capitalizeNext = true;
+        /**
+         * Show page with load metadata options.
+         */
+        function _selectOptions() {
+            $scope.loadMeta.action = 'options';
+            $scope.loadMeta.button = 'Save';
+            $scope.loadMeta.info = INFO_SELECT_OPTIONS;
+        }
 
-                for (var i = 0; i < len; i++) {
-                    var ch = name.charAt(i);
+        function toJavaClassName(name) {
+            var len = name.length;
 
-                    if (ch === ' ' ||  ch === '_')
-                        capitalizeNext = true;
-                    else if (capitalizeNext) {
-                        buf += ch.toLocaleUpperCase();
+            var buf = '';
 
-                        capitalizeNext = false;
-                    }
-                    else
-                        buf += ch.toLocaleLowerCase();
-                }
+            var capitalizeNext = true;
 
-                return buf;
-            }
+            for (var i = 0; i < len; i++) {
+                var ch = name.charAt(i);
 
-            function toJavaName(dbName) {
-                var javaName = toJavaClassName(dbName);
+                if (ch === ' ' ||  ch === '_')
+                    capitalizeNext = true;
+                else if (capitalizeNext) {
+                    buf += ch.toLocaleUpperCase();
 
-                return javaName.charAt(0).toLocaleLowerCase() + javaName.slice(1);
+                    capitalizeNext = false;
+                }
+                else
+                    buf += ch.toLocaleLowerCase();
             }
 
-            function _saveBatch(batch) {
-                if (batch && batch.length > 0) {
-                    $loading.start('loadingMetadataFromDb');
+            return buf;
+        }
 
-                    $http.post('/api/v1/configuration/metadata/save/batch', batch)
-                        .success(function (savedBatch) {
-                            var lastItem;
-                            var newItems = [];
+        function toJavaName(dbName) {
+            var javaName = toJavaClassName(dbName);
 
-                            _.forEach(savedBatch.generatedCaches, function (generatedCache) {
-                                $scope.caches.push(generatedCache);
-                            });
+            return javaName.charAt(0).toLocaleLowerCase() + javaName.slice(1);
+        }
 
-                            _.forEach(savedBatch.savedMetas, function (savedItem) {
-                                var idx = _.findIndex($scope.metadatas, function (meta) {
-                                    return meta._id === savedItem._id;
-                                });
+        function _saveBatch(batch) {
+            if (batch && batch.length > 0) {
+                $loading.start('loadingMetadataFromDb');
 
-                                if (idx >= 0)
-                                    $scope.metadatas[idx] = savedItem;
-                                else
-                                    newItems.push(savedItem);
+                $http.post('/api/v1/configuration/metadata/save/batch', batch)
+                    .success(function (savedBatch) {
+                        var lastItem;
+                        var newItems = [];
 
-                                lastItem = savedItem;
-                            });
+                        _.forEach(savedBatch.generatedCaches, function (generatedCache) {
+                            $scope.caches.push(generatedCache);
+                        });
 
-                            _.forEach(newItems, function (item) {
-                                $scope.metadatas.push(item);
+                        _.forEach(savedBatch.savedMetas, function (savedItem) {
+                            var idx = _.findIndex($scope.metadatas, function (meta) {
+                                return meta._id === savedItem._id;
                             });
 
-                            if (!lastItem && $scope.metadatas.length > 0)
-                                lastItem = $scope.metadatas[0];
+                            if (idx >= 0)
+                                $scope.metadatas[idx] = savedItem;
+                            else
+                                newItems.push(savedItem);
 
-                            $scope.selectItem(lastItem);
+                            lastItem = savedItem;
+                        });
 
-                            $common.showInfo('Cache type metadata loaded from database.');
+                        _.forEach(newItems, function (item) {
+                            $scope.metadatas.push(item);
+                        });
 
-                            $scope.panels.activePanels = [0, 1, 2];
+                        if (!lastItem && $scope.metadatas.length > 0)
+                            lastItem = $scope.metadatas[0];
 
-                            $scope.ui.showValid = true;
-                        })
-                        .error(function (errMsg) {
-                            $common.showError(errMsg);
-                        })
-                        .finally(function() {
-                            $loading.finish('loadingMetadataFromDb');
+                        $scope.selectItem(lastItem);
 
-                            loadMetaModal.hide();
-                        });
-                }
-                else
-                    loadMetaModal.hide();
-            }
+                        $common.showInfo('Cache type metadata loaded from database.');
 
-            function _saveMetadata() {
-                if ($common.isEmptyString($scope.ui.packageName))
-                    return $common.showPopoverMessage(undefined, undefined, 'metadataLoadPackage',
-                        'Package should be not empty');
+                        $scope.panels.activePanels = [0, 1, 2];
 
-                if (!$common.isValidJavaClass('Package', $scope.ui.packageName, false, 'metadataLoadPackage', true))
-                    return false;
+                        $scope.ui.showValid = true;
+                    })
+                    .error(function (errMsg) {
+                        $common.showError(errMsg);
+                    })
+                    .finally(function() {
+                        $loading.finish('loadingMetadataFromDb');
 
-                _savePreset($scope.preset);
+                        loadMetaModal.hide();
+                    });
+            }
+            else
+                loadMetaModal.hide();
+        }
 
-                var batch = [];
-                var tables = [];
-                var dupCnt = 0;
+        function _saveMetadata() {
+            if ($common.isEmptyString($scope.ui.packageName))
+                return $common.showPopoverMessage(undefined, undefined, 'metadataLoadPackage',
+                    'Package should be not empty');
 
-                var containKey = true;
+            if (!$common.isValidJavaClass('Package', $scope.ui.packageName, false, 'metadataLoadPackage', true))
+                return false;
 
-                function queryField(name, jdbcType) {
-                    return {name: toJavaName(name), className: jdbcType.javaType};
-                }
+            _savePreset($scope.preset);
 
-                function dbField(name, jdbcType, nullable) {
-                    return {
-                        jdbcType: jdbcType,
-                        databaseFieldName: name,
-                        databaseFieldType: jdbcType.dbName,
-                        javaFieldName: toJavaName(name),
-                        javaFieldType: nullable ? jdbcType.javaType :
-                            ($scope.ui.usePrimitives && jdbcType.primitiveType ? jdbcType.primitiveType : jdbcType.javaType)
-                    };
-                }
+            var batch = [];
+            var tables = [];
+            var dupCnt = 0;
 
-                _.forEach($scope.loadMeta.tables, function (table) {
-                    if (table.use) {
-                        var qryFields = [];
-                        var indexes = [];
-                        var keyFields = [];
-                        var valFields = [];
+            var containKey = true;
 
-                        var tableName = table.tbl;
+            function queryField(name, jdbcType) {
+                return {name: toJavaName(name), className: jdbcType.javaType};
+            }
 
-                        var dup = tables.indexOf(tableName) >= 0;
+            function dbField(name, jdbcType, nullable) {
+                return {
+                    jdbcType: jdbcType,
+                    databaseFieldName: name,
+                    databaseFieldType: jdbcType.dbName,
+                    javaFieldName: toJavaName(name),
+                    javaFieldType: nullable ? jdbcType.javaType :
+                        ($scope.ui.usePrimitives && jdbcType.primitiveType ? jdbcType.primitiveType : jdbcType.javaType)
+                };
+            }
 
-                        if (dup)
-                            dupCnt++;
+            _.forEach($scope.loadMeta.tables, function (table) {
+                if (table.use) {
+                    var qryFields = [];
+                    var indexes = [];
+                    var keyFields = [];
+                    var valFields = [];
 
-                        var typeName = toJavaClassName(tableName);
-                        var valType = $commonUtils.toJavaPackageName($scope.ui.packageName) + '.' + typeName;
+                    var tableName = table.tbl;
 
-                        var _containKey = false;
+                    var dup = tables.indexOf(tableName) >= 0;
 
-                        _.forEach(table.cols, function (col) {
-                            var colName = col.name;
-                            var jdbcType = $common.findJdbcType(col.type);
-                            var nullable = col.nullable;
+                    if (dup)
+                        dupCnt++;
 
-                            qryFields.push(queryField(colName, jdbcType));
+                    var typeName = toJavaClassName(tableName);
+                    var valType = $commonUtils.toJavaPackageName($scope.ui.packageName) + '.' + typeName;
 
-                            if (col.key) {
-                                keyFields.push(dbField(colName, jdbcType, nullable));
+                    var _containKey = false;
 
-                                _containKey = true;
-                            }
-                            else
-                                valFields.push(dbField(colName, jdbcType, nullable));
-                        });
+                    _.forEach(table.cols, function (col) {
+                        var colName = col.name;
+                        var jdbcType = $common.findJdbcType(col.type);
+                        var nullable = col.nullable;
 
-                        containKey &= _containKey;
+                        qryFields.push(queryField(colName, jdbcType));
 
-                        if (table.idxs) {
-                            _.forEach(table.idxs, function (idx) {
-                                var fields = Object.keys(idx.fields);
+                        if (col.key) {
+                            keyFields.push(dbField(colName, jdbcType, nullable));
 
-                                indexes.push(
-                                    {name: idx.name, indexType: 'SORTED', fields: _.map(fields, function (fieldName) {
-                                        return {
-                                            name: toJavaName(fieldName),
-                                            direction: !idx.fields[fieldName]
-                                        };
-                                    })});
-                            });
+                            _containKey = true;
                         }
+                        else
+                            valFields.push(dbField(colName, jdbcType, nullable));
+                    });
 
-                        var metaFound = _.find($scope.metadatas, function (meta) {
-                            return meta.valueType === valType;
-                        });
+                    containKey &= _containKey;
 
-                        var meta = {
-                            confirm: false,
-                            skip: false,
-                            space: $scope.spaces[0],
-                            caches: []
-                        };
+                    if (table.idxs) {
+                        _.forEach(table.idxs, function (idx) {
+                            var fields = Object.keys(idx.fields);
 
-                        if ($common.isDefined(metaFound)) {
-                            meta._id = metaFound._id;
-                            meta.caches = metaFound.caches;
-                            meta.confirm = true;
-                        }
+                            indexes.push(
+                                {name: idx.name, indexType: 'SORTED', fields: _.map(fields, function (fieldName) {
+                                    return {
+                                        name: toJavaName(fieldName),
+                                        direction: !idx.fields[fieldName]
+                                    };
+                                })});
+                        });
+                    }
 
-                        var dupSfx = (dup ? '_' + dupCnt : '');
+                    var metaFound = _.find($scope.metadatas, function (meta) {
+                        return meta.valueType === valType;
+                    });
 
-                        meta.keyType = valType + 'Key' + dupSfx;
+                    var meta = {
+                        confirm: false,
+                        skip: false,
+                        space: $scope.spaces[0],
+                        caches: []
+                    };
 
-                        meta.keyType = valType + 'Key' + dupSfx;
-                        meta.valueType = valType + dupSfx;
-                        meta.databaseSchema = table.schema;
-                        meta.databaseTable = tableName;
-                        meta.fields = qryFields;
-                        meta.indexes = indexes;
-                        meta.keyFields = keyFields;
-                        meta.valueFields = valFields;
+                    if ($common.isDefined(metaFound)) {
+                        meta._id = metaFound._id;
+                        meta.caches = metaFound.caches;
+                        meta.confirm = true;
+                    }
 
-                        // Use Java built-in type for key.
-                        if ($scope.ui.builtinKeys && meta.keyFields.length === 1) {
-                            meta.keyType = meta.keyFields[0].jdbcType.javaType;
-                            meta.keyFields = [];
-                        }
+                    var dupSfx = (dup ? '_' + dupCnt : '');
 
-                        // Prepare caches for generation.
-                        if ($scope.ui.generateCaches)
-                            meta.newCache = {
-                                name: typeName,
-                                clusters: $scope.ui.generatedCachesClusters
-                            };
+                    meta.keyType = valType + 'Key' + dupSfx;
 
-                        batch.push(meta);
-                        tables.push(tableName);
+                    meta.keyType = valType + 'Key' + dupSfx;
+                    meta.valueType = valType + dupSfx;
+                    meta.databaseSchema = table.schema;
+                    meta.databaseTable = tableName;
+                    meta.fields = qryFields;
+                    meta.indexes = indexes;
+                    meta.keyFields = keyFields;
+                    meta.valueFields = valFields;
+
+                    // Use Java built-in type for key.
+                    if ($scope.ui.builtinKeys && meta.keyFields.length === 1) {
+                        meta.keyType = meta.keyFields[0].jdbcType.javaType;
+                        meta.keyFields = [];
                     }
-                });
 
-                /**
-                 * Generate message to show on confirm dialog.
-                 *
-                 * @param meta Object to confirm.
-                 * @returns {string} Generated message.
-                 */
-                function overwriteMessage(meta) {
-                    return '<span>' +
-                        'Metadata with name &quot;' + meta.databaseTable + '&quot; already exist.<br/><br/>' +
-                        'Are you sure you want to overwrite it?' +
-                        '</span>';
-                }
+                    // Prepare caches for generation.
+                    if ($scope.ui.generateCaches)
+                        meta.newCache = {
+                            name: typeName,
+                            clusters: $scope.ui.generatedCachesClusters
+                        };
 
-                var itemsToConfirm = _.filter(batch, function (item) { return item.confirm; });
-
-                function checkOverwrite() {
-                    if (itemsToConfirm.length > 0)
-                        $confirmBatch.confirm(overwriteMessage, itemsToConfirm)
-                            .then(function () {
-                                _saveBatch(_.filter(batch, function (item) {
-                                    return !item.skip;
-                                }));
-                            }, function () {
-                                $common.showError('Cache type metadata loading interrupted by user.');
-                            });
-                    else
-                        _saveBatch(batch);
+                    batch.push(meta);
+                    tables.push(tableName);
                 }
+            });
 
-                if (containKey)
-                    checkOverwrite();
-                else
-                    $confirm.confirm('Some tables have no primary key.<br/>' +
-                        'You will need to configure key type and key fields for such tables after load complete.')
-                        .then(function () { checkOverwrite(); });
+            /**
+             * Generate message to show on confirm dialog.
+             *
+             * @param meta Object to confirm.
+             * @returns {string} Generated message.
+             */
+            function overwriteMessage(meta) {
+                return '<span>' +
+                    'Metadata with name &quot;' + meta.databaseTable + '&quot; already exist.<br/><br/>' +
+                    'Are you sure you want to overwrite it?' +
+                    '</span>';
             }
 
-            $scope.loadMetadataNext = function () {
-                if (!$scope.nextAvailable())
-                    return;
-
-                if ($scope.loadMeta.action === 'connect')
-                    _loadSchemas();
-                else if ($scope.loadMeta.action === 'schemas')
-                    _loadTables();
-                else if ($scope.loadMeta.action === 'tables')
-                    _selectOptions();
-                else if ($scope.loadMeta.action === 'options')
-                    _saveMetadata();
-            };
-
-            $scope.nextTooltipText = function () {
-                if ($scope.loadMeta.action === 'tables' && !$scope.nextAvailable())
-                    return 'Select tables to continue';
-            };
+            var itemsToConfirm = _.filter(batch, function (item) { return item.confirm; });
 
-            $scope.nextAvailable = function () {
-                switch ($scope.loadMeta.action) {
-                    case 'tables': return $('#metadataTableData').find(':checked').length > 0;
-                    case 'schemas': return $common.isEmptyArray($scope.loadMeta.schemas) || $('#metadataSchemaData').find(':checked').length > 0;
-                    default: return true;
-                }
-            };
-
-            $scope.loadMetadataPrev = function () {
-                $scope.loadMeta.button = 'Next';
+            function checkOverwrite() {
+                if (itemsToConfirm.length > 0)
+                    $confirmBatch.confirm(overwriteMessage, itemsToConfirm)
+                        .then(function () {
+                            _saveBatch(_.filter(batch, function (item) {
+                                return !item.skip;
+                            }));
+                        }, function () {
+                            $common.showError('Cache type metadata loading interrupted by user.');
+                        });
+                else
+                    _saveBatch(batch);
+            }
 
-                if  ($scope.loadMeta.action === 'options') {
-                    $scope.loadMeta.action = 'tables';
-                    $scope.loadMeta.info = INFO_SELECT_TABLES;
-                }
-                else if  ($scope.loadMeta.action === 'tables' && $scope.loadMeta.schemas.length > 0) {
-                    $scope.loadMeta.action = 'schemas';
-                    $scope.loadMeta.info = INFO_SELECT_SCHEMAS;
-                    $scope.loadMeta.loadingOptions = LOADING_TABLES;
-                }
-                else {
-                    $scope.loadMeta.action = 'connect';
-                    $scope.loadMeta.info = INFO_CONNECT_TO_DB;
-                    $scope.loadMeta.loadingOptions = LOADING_SCHEMAS;
-                }
-            };
+            if (containKey)
+                checkOverwrite();
+            else
+                $confirm.confirm('Some tables have no primary key.<br/>' +
+                    'You will need to configure key type and key fields for such tables after load complete.')
+                    .then(function () { checkOverwrite(); });
+        }
+
+        $scope.loadMetadataNext = function () {
+            if (!$scope.nextAvailable())
+                return;
+
+            if ($scope.loadMeta.action === 'connect')
+                _loadSchemas();
+            else if ($scope.loadMeta.action === 'schemas')
+                _loadTables();
+            else if ($scope.loadMeta.action === 'tables')
+                _selectOptions();
+            else if ($scope.loadMeta.action === 'options')
+                _saveMetadata();
+        };
+
+        $scope.nextTooltipText = function () {
+            if ($scope.loadMeta.action === 'tables' && !$scope.nextAvailable())
+                return 'Select tables to continue';
+        };
+
+        $scope.nextAvailable = function () {
+            switch ($scope.loadMeta.action) {
+                case 'tables': return $('#metadataTableData').find(':checked').length > 0;
+                case 'schemas': return $common.isEmptyArray($scope.loadMeta.schemas) || $('#metadataSchemaData').find(':checked').length > 0;
+                default: return true;
+            }
+        };
 
-            $scope.metadataTitle = function () {
-                return $scope.ui.showValid ? 'Types metadata:' : 'Type metadata without key fields:';
-            };
+        $scope.loadMetadataPrev = function () {
+            $scope.loadMeta.button = 'Next';
 
-            function selectFirstItem() {
-                if ($scope.metadatas.length > 0)
-                    $scope.selectItem($scope.metadatas[0]);
+            if  ($scope.loadMeta.action === 'options') {
+                $scope.loadMeta.action = 'tables';
+                $scope.loadMeta.info = INFO_SELECT_TABLES;
+            }
+            else if  ($scope.loadMeta.action === 'tables' && $scope.loadMeta.schemas.length > 0) {
+                $scope.loadMeta.action = 'schemas';
+                $scope.loadMeta.info = INFO_SELECT_SCHEMAS;
+                $scope.loadMeta.loadingOptions = LOADING_TABLES;
             }
+            else {
+                $scope.loadMeta.action = 'connect';
+                $scope.loadMeta.info = INFO_CONNECT_TO_DB;
+                $scope.loadMeta.loadingOptions = LOADING_SCHEMAS;
+            }
+        };
+
+        $scope.metadataTitle = function () {
+            return $scope.ui.showValid ? 'Types metadata:' : 'Type metadata without key fields:';
+        };
+
+        function selectFirstItem() {
+            if ($scope.metadatas.length > 0)
+                $scope.selectItem($scope.metadatas[0]);
+        }
+
+        // When landing on the page, get metadatas and show them.
+        $loading.start('loadingMetadataScreen');
+
+        $http.post('/api/v1/configuration/metadata/list')
+            .success(function (data) {
+                $scope.spaces = data.spaces;
+                $scope.clusters = data.clusters;
+                $scope.caches = data.caches;
+                $scope.metadatas = data.metadatas;
+
+                if (_.size($scope.clusters) > 0)
+                    $scope.ui.generatedCachesClusters.push($scope.clusters[0].value);
+
+                // Load page descriptor.
+                $http.get('/models/metadata.json')
+                    .success(function (data) {
+                        $scope.screenTip = data.screenTip;
+                        $scope.moreInfo = data.moreInfo;
+                        $scope.metadata = data.metadata;
+                        $scope.metadataDb = data.metadataDb;
+
+                        $scope.ui.groups = data.metadata;
+
+                        if ($common.getQueryVariable('new'))
+                            $scope.createItem($common.getQueryVariable('id'));
+                        else {
+                            var lastSelectedMetadata = angular.fromJson(sessionStorage.lastSelectedMetadata);
 
-            // When landing on the page, get metadatas and show them.
-            $loading.start('loadingMetadataScreen');
-
-            $http.post('/api/v1/configuration/metadata/list')
-                .success(function (data) {
-                    $scope.spaces = data.spaces;
-                    $scope.clusters = data.clusters;
-                    $scope.caches = data.caches;
-                    $scope.metadatas = data.metadatas;
-
-                    if (_.size($scope.clusters) > 0)
-                        $scope.ui.generatedCachesClusters.push($scope.clusters[0].value);
-
-                    // Load page descriptor.
-                    $http.get('/models/metadata.json')
-                        .success(function (data) {
-                            $scope.screenTip = data.screenTip;
-                            $scope.moreInfo = data.moreInfo;
-                            $scope.metadata = data.metadata;
-                            $scope.metadataDb = data.metadataDb;
-
-                            $scope.ui.groups = data.metadata;
-
-                            if ($common.getQueryVariable('new'))
-                                $scope.createItem($common.getQueryVariable('id'));
-                            else {
-                                var lastSelectedMetadata = angular.fromJson(sessionStorage.lastSelectedMetadata);
-
-                                if (lastSelectedMetadata) {
-                                    var idx = _.findIndex($scope.metadatas, function (metadata) {
-                                        return metadata._id === lastSelectedMetadata;
-                                    });
+                            if (lastSelectedMetadata) {
+                                var idx = _.findIndex($scope.metadatas, function (metadata) {
+                                    return metadata._id === lastSelectedMetadata;
+                                });
 
-                                    if (idx >= 0)
-                                        $scope.selectItem($scope.metadatas[idx]);
-                                    else {
-                                        sessionStorage.removeItem('lastSelectedMetadata');
+                                if (idx >= 0)
+                                    $scope.selectItem($scope.metadatas[idx]);
+                                else {
+                                    sessionStorage.removeItem('lastSelectedMetadata');
 
-                                        selectFirstItem();
-                                    }
-                                }
-                                else
                                     selectFirstItem();
+                                }
                             }
+                            else
+                                selectFirstItem();
+                        }
 
-                            $timeout(function () {
-                                $scope.$apply();
-                            });
-
-                            $scope.$watch('backupItem', function (val) {
-                                if (val) {
-                                    var srcItem = $scope.selectedItem ? $scope.selectedItem : prepareNewItem();
-
-                                    $scope.ui.checkDirty(val, srcItem);
-
-                                    $scope.preview.general.xml = $generatorXml.metadataGeneral(val).asString();
-                                    $scope.preview.general.java = $generatorJava.metadataGeneral(val).asString();
-                                    $scope.preview.general.allDefaults = $common.isEmptyString($scope.preview.general.xml);
-
-                                    $scope.preview.query.xml = $generatorXml.metadataQuery(val).asString();
-                                    $scope.preview.query.java = $generatorJava.metadataQuery(val).asString();
-                                    $scope.preview.query.allDefaults = $common.isEmptyString($scope.preview.query.xml);
-
-                                    $scope.preview.store.xml = $generatorXml.metadataStore(val).asString();
-                                    $scope.preview.store.java = $generatorJava.metadataStore(val, false).asString();
-                                    $scope.preview.store.allDefaults = $common.isEmptyString($scope.preview.store.xml);
-                                }
-                            }, true);
-                        })
-                        .error(function (errMsg) {
-                            $common.showError(errMsg);
+                        $timeout(function () {
+                            $scope.$apply();
                         });
-                })
-                .error(function (errMsg) {
-                    $common.showError(errMsg);
-                })
-                .finally(function() {
-                    $scope.ui.ready = true;
-                    $loading.finish('loadingMetadataScreen');
-                });
 
-            $scope.selectItem = function (item, backup) {
-                function selectItem() {
-                    $table.tableReset();
+                        $scope.$watch('backupItem', function (val) {
+                            if (val) {
+                                var srcItem = $scope.selectedItem ? $scope.selectedItem : prepareNewItem();
 
-                    $scope.selectedItem = angular.copy(item);
+                                $scope.ui.checkDirty(val, srcItem);
 
-                    try {
-                        if (item && item._id)
-                            sessionStorage.lastSelectedMetadata = angular.toJson(item._id);
-                        else
-                            sessionStorage.removeItem('lastSelectedMetadata');
-                    }
-                    catch (error) { }
+                                $scope.preview.general.xml = $generatorXml.metadataGeneral(val).asString();
+                                $scope.preview.general.java = $generatorJava.metadataGeneral(val).asString();
+                                $scope.preview.general.allDefaults = $common.isEmptyString($scope.preview.general.xml);
+
+                                $scope.preview.query.xml = $generatorXml.metadataQuery(val).asString();
+                                $scope.preview.query.java = $generatorJava.metadataQuery(val).asString();
+                                $scope.preview.query.allDefaults = $common.isEmptyString($scope.preview.query.xml);
 
-                    _.forEach(previews, function(preview) {
-                        preview.attractAttention = false;
+                                $scope.preview.store.xml = $generatorXml.metadataStore(val).asString();
+                                $scope.preview.store.java = $generatorJava.metadataStore(val, false).asString();
+                                $scope.preview.store.allDefaults = $common.isEmptyString($scope.preview.store.xml);
+                            }
+                        }, true);
+                    })
+                    .error(function (errMsg) {
+                        $common.showError(errMsg);
                     });
+            })
+            .error(function (errMsg) {
+                $common.showError(errMsg);
+            })
+            .finally(function() {
+                $scope.ui.ready = true;
+                $loading.finish('loadingMetadataScreen');
+            });
+
+        $scope.selectItem = function (item, backup) {
+            function selectItem() {
+                $table.tableReset();
 
-                    if (backup)
-                        $scope.backupItem = backup;
-                    else if (item)
-                        $scope.backupItem = angular.copy(item);
-                    else
-                        $scope.backupItem = undefined;
+                $scope.selectedItem = angular.copy(item);
 
-                    if ($common.getQueryVariable('new'))
-                        $state.go('base.configuration.metadata');
+                try {
+                    if (item && item._id)
+                        sessionStorage.lastSelectedMetadata = angular.toJson(item._id);
+                    else
+                        sessionStorage.removeItem('lastSelectedMetadata');
                 }
+                catch (error) { }
 
-                $common.confirmUnsavedChanges($scope.ui.isDirty(), selectItem);
+                _.forEach(previews, function(preview) {
+                    preview.attractAttention = false;
+                });
 
-                $scope.ui.formTitle = $common.isDefined($scope.backupItem) && $scope.backupItem._id ?
-                    'Selected metadata: ' + $scope.backupItem.valueType
-                    : 'New metadata';
-            };
+                if (backup)
+                    $scope.backupItem = backup;
+                else if (item)
+                    $scope.backupItem = angular.copy(item);
+                else
+                    $scope.backupItem = undefined;
 
-            function prepareNewItem(cacheId) {
-                return {
-                    space: $scope.spaces[0]._id,
-                    caches: cacheId && _.find($scope.caches, {value: cacheId}) ? [cacheId] :
-                        (!$common.isEmptyArray($scope.caches) ? [$scope.caches[0].value] : [])
-                };
+                if ($common.getQueryVariable('new'))
+                    $state.go('base.configuration.metadata');
             }
 
-            // Add new metadata.
-            $scope.createItem = function (cacheId) {
-                if ($scope.tableReset(true)) {
-                    $timeout(function () {
-                        $common.ensureActivePanel($scope.panels, 'query');
-                        $common.ensureActivePanel($scope.panels, 'general', 'keyType');
-                    });
+            $common.confirmUnsavedChanges($scope.ui.isDirty(), selectItem);
 
-                    $scope.selectItem(undefined, prepareNewItem(cacheId));
-                }
+            $scope.ui.formTitle = $common.isDefined($scope.backupItem) && $scope.backupItem._id ?
+                'Selected metadata: ' + $scope.backupItem.valueType
+                : 'New metadata';
+        };
+
+        function prepareNewItem(cacheId) {
+            return {
+                space: $scope.spaces[0]._id,
+                caches: cacheId && _.find($scope.caches, {value: cacheId}) ? [cacheId] :
+                    (!$common.isEmptyArray($scope.caches) ? [$scope.caches[0].value] : [])
             };
+        }
+
+        // Add new metadata.
+        $scope.createItem = function (cacheId) {
+            if ($scope.tableReset(true)) {
+                $timeout(function () {
+                    $common.ensureActivePanel($scope.panels, 'query');
+                    $common.ensureActivePanel($scope.panels, 'general', 'keyType');
+                });
 
-            // Check metadata logical consistency.
-            function validate(item) {
-                if ($common.isEmptyString(item.keyType))
-                    return showPopoverMessage($scope.panels, 'general', 'keyType', 'Key type should not be empty');
-                else if (!$common.isValidJavaClass('Key type', item.keyType, true, 'keyType'))
-                    return false;
+                $scope.selectItem(undefined, prepareNewItem(cacheId));
+            }
+        };
 
-                if ($common.isEmptyString(item.valueType))
-                    return showPopoverMessage($scope.panels, 'general', 'valueType', 'Value type should not be empty');
-                else if (!$common.isValidJavaClass('Value type', item.valueType, false, 'valueType'))
-                    return false;
+        // Check metadata logical consistency.
+        function validate(item) {
+            if ($common.isEmptyString(item.keyType))
+                return showPopoverMessage($scope.panels, 'general', 'keyType', 'Key type should not be empty');
+            else if (!$common.isValidJavaClass('Key type', item.keyType, true, 'keyType'))
+                return false;
 
-                var qry = $common.metadataForQueryConfigured(item);
+            if ($common.isEmptyString(item.valueType))
+                return showPopoverMessage($scope.panels, 'general', 'valueType', 'Value type should not be empty');
+            else if (!$common.isValidJavaClass('Value type', item.valueType, false, 'valueType'))
+                return false;
 
-                if (qry) {
-                    if ($common.isEmptyArray(item.fields))
-                        return showPopoverMessage($scope.panels, 'query', 'fields-legend', 'Query fields should not be empty');
+            var qry = $common.metadataForQueryConfigured(item);
 
-                    var indexes = item.indexes;
+            if (qry) {
+                if ($common.isEmptyArray(item.fields))
+                    return showPopoverMessage($scope.panels, 'query', 'fields-legend', 'Query fields should not be empty');
 
-                    if (indexes && indexes.length > 0) {
-                        if (_.find(indexes, function(index, i) {
-                            if ($common.isEmptyArray(index.fields))
-                                return !showPopoverMessage($scope.panels, 'query', 'indexes' + i, 'Index fields are not specified');
-                        }))
-                            return false;
-                    }
-                }
+                var indexes = item.indexes;
 
-                var str = $common.metadataForStoreConfigured(item);
+                if (indexes && indexes.length > 0) {
+                    if (_.find(indexes, function(index, i) {
+                        if ($common.isEmptyArray(index.fields))
+                            return !showPopoverMessage($scope.panels, 'query', 'indexes' + i, 'Index fields are not specified');
+                    }))
+                        return false;
+                }
+            }
 
-                if (str) {
-                    if ($common.isEmptyString(item.databaseSchema))
-                        return showPopoverMessage($scope.panels, 'store', 'databaseSchema', 'Database schema should not be empty');
+            var str = $common.metadataForStoreConfigured(item);
 
-                    if ($common.isEmptyString(item.databaseTable))
-                        return showPopoverMessage($scope.panels, 'store', 'databaseTable', 'Database table should not be empty');
+            if (str) {
+                if ($common.isEmptyString(item.databaseSchema))
+                    return showPopoverMessage($scope.panels, 'store', 'databaseSchema', 'Database schema should not be empty');
 
-                    if ($common.isEmptyArray(item.keyFields) && !$common.isJavaBuildInClass(item.keyType))
-                        return showPopoverMessage($scope.panels, 'store', 'keyFields-add', 'Key fields are not specified');
+                if ($common.isEmptyString(item.databaseTable))
+                    return showPopoverMessage($scope.panels, 'store', 'databaseTable', 'Database table should not be empty');
 
-                    if ($common.isEmptyArray(item.valueFields))
-                        return showPopoverMessage($scope.panels, 'store', 'valueFields-add', 'Value fields are not specified');
-                }
-                else if (!qry) {
-                    return showPopoverMessage($scope.panels, 'query', 'query-title', 'SQL query metadata should be configured');
-                }
+                if ($common.isEmptyArray(item.keyFields) && !$common.isJavaBuildInClass(item.keyType))
+                    return showPopoverMessage($scope.panels, 'store', 'keyFields-add', 'Key fields are not specified');
 
-                return true;
+                if ($common.isEmptyArray(item.valueFields))
+                    return showPopoverMessage($scope.panels, 'store', 'valueFields-add', 'Value fields are not specified');
+            }
+            else if (!qry) {
+                return showPopoverMessage($scope.panels, 'query', 'query-title', 'SQL query metadata should be configured');
             }
 
-            // Save cache type metadata into database.
-            function save(item) {
-                var qry = $common.metadataForQueryConfigured(item);
-                var str = $common.metadataForStoreConfigured(item);
-
-                item.kind = 'query';
-
-                if (qry && str)
-                    item.kind = 'both';
-                else if (str)
-                    item.kind = 'store';
+            return true;
+        }
 
-                $http.post('/api/v1/configuration/metadata/save', item)
-                    .success(function (res) {
-                        $scope.ui.markPristine();
+        // Save cache type metadata into database.
+        function save(item) {
+            var qry = $common.metadataForQueryConfigured(item);
+            var str = $common.metadataForStoreConfigured(item);
 
-                        var savedMeta = res[0];
+            item.kind = 'query';
 
-                        var idx = _.findIndex($scope.metadatas, function (metadata) {
-                            return metadata._id === savedMeta._id;
-                        });
+            if (qry && str)
+                item.kind = 'both';
+            else if (str)
+                item.kind = 'store';
 
-                        if (idx >= 0)
-                            angular.extend($scope.metadatas[idx], savedMeta);
-                        else
-                            $scope.metadatas.push(savedMeta);
+            $http.post('/api/v1/configuration/metadata/save', item)
+                .success(function (res) {
+                    $scope.ui.markPristine();
 
-                        $scope.selectItem(savedMeta);
+                    var savedMeta = res[0];
 
-                        $common.showInfo('Cache type metadata"' + item.valueType + '" saved.');
-                    })
-                    .error(function (errMsg) {
-                        $common.showError(errMsg);
+                    var idx = _.findIndex($scope.metadatas, function (metadata) {
+                        return metadata._id === savedMeta._id;
                     });
-            }
 
-            // Save cache type metadata.
-            $scope.saveItem = function () {
-                if ($scope.tableReset(true)) {
-                    var item = $scope.backupItem;
-
-                    if (validate(item))
-                        save(item);
-                }
-            };
+                    if (idx >= 0)
+                        angular.extend($scope.metadatas[idx], savedMeta);
+                    else
+                        $scope.metadatas.push(savedMeta);
 
-            // Save cache type metadata with new name.
-            $scope.cloneItem = function () {
-                if ($scope.tableReset(true)) {
-                    if (validate($scope.backupItem))
-                        $clone.confirm($scope.backupItem.valueType).then(function (newName) {
-                            var item = angular.copy($scope.backupItem);
+                    $scope.selectItem(savedMeta);
 
-                            item._id = undefined;
-                            item.valueType = newName;
+                    $common.showInfo('Cache type metadata"' + item.valueType + '" saved.');
+                })
+                .error(function (errMsg) {
+                    $common.showError(errMsg);
+                });
+        }
 
-                            save(item);
-                        });
-                }
-            };
+        // Save cache type metadata.
+        $scope.saveItem = function () {
+            if ($scope.tableReset(true)) {
+                var item = $scope.backupItem;
 
-            // Remove metadata from db.
-            $scope.removeItem = function () {
-                $table.tableReset();
+                if (validate(item))
+                    save(item);
+            }
+        };
 
-                var selectedItem = $scope.selectedItem;
+        // Save cache type metadata with new name.
+        $scope.cloneItem = function () {
+            if ($scope.tableReset(true)) {
+                if (validate($scope.backupItem))
+                    $clone.confirm($scope.backupItem.valueType).then(function (newName) {
+                        var item = angular.copy($scope.backupItem);
 
-                $confirm.confirm('Are you sure you want to remove cache type metadata: "' + selectedItem.valueType + '"?')
-                    .then(function () {
-                            var _id = selectedItem._id;
+                        item._id = undefined;
+                        item.valueType = newName;
 
-                            $http.post('/api/v1/configuration/metadata/remove', {_id: _id})
-                                .success(function () {
-                                    $common.showInfo('Cache type metadata has been removed: ' + selectedItem.valueType);
+                        save(item);
+                    });
+            }
+        };
 
-                                    var metadatas = $scope.metadatas;
+        // Remove metadata from db.
+        $scope.removeItem = function () {
+            $table.tableReset();
 
-                                    var idx = _.findIndex(metadatas, function (metadata) {
-                                        return metadata._id === _id;
-                                    });
+            var selectedItem = $scope.selectedItem;
 
-                                    if (idx >= 0) {
-                                        metadatas.splice(idx, 1);
+            $confirm.confirm('Are you sure you want to remove cache type metadata: "' + selectedItem.valueType + '"?')
+                .then(function () {
+                        var _id = selectedItem._id;
 
-                                        if (metadatas.length > 0)
-                                            $scope.selectItem(metadatas[0]);
-                                        else
-                                            $scope.selectItem(undefined, undefined);
-                                    }
+                        $http.post('/api/v1/configuration/metadata/remove', {_id: _id})
+                            .success(function () {
+                                $common.showInfo('Cache type metadata has been removed: ' + selectedItem.valueType);
 
-                                    if (!$scope.ui.showValid) {
-                                        var validFilter = $filter('metadatasValidation');
+                                var metadatas = $scope.metadatas;
 
-                                        $scope.ui.showValid = validFilter($scope.metadatas, false, true).length === 0;
-                                    }
-                                })
-                                .error(function (errMsg) {
-                                    $common.showError(errMsg);
+                                var idx = _.findIndex(metadatas, function (metadata) {
+                                    return metadata._id === _id;
                                 });
-                    });
-            };
 
-            // Remove all metadata from db.
-            $scope.removeAllItems = function () {
-                $table.tableReset();
+                                if (idx >= 0) {
+                                    metadatas.splice(idx, 1);
 
-                $confirm.confirm('Are you sure you want to remove all metadata?')
-                    .then(function () {
-                            $http.post('/api/v1/configuration/metadata/remove/all')
-                                .success(function () {
-                                    $common.showInfo('All metadata have been removed');
+                                    if (metadatas.length > 0)
+                                        $scope.selectItem(metadatas[0]);
+                                    else
+                                        $scope.selectItem(undefined, undefined);
+                                }
 
-                                    $scope.metadatas = [];
+                                if (!$scope.ui.showValid) {
+                                    var validFilter = $filter('metadatasValidation');
 
-                                    $scope.selectItem(undefined, undefined);
+                                    $scope.ui.showValid = validFilter($scope.metadatas, false, true).length === 0;
+                                }
+                            })
+                            .error(function (errMsg) {
+                                $common.showError(errMsg);
+                            });
+                });
+        };
 
-                                    $scope.ui.showValid = true;
-                                })
-                                .error(function (errMsg) {
-                                    $common.showError(errMsg);
-                                });
-                    });
-            };
+        // Remove all metadata from db.
+        $scope.removeAllItems = function () {
+            $table.tableReset();
+
+            $confirm.confirm('Are you sure you want to remove all metadata?')
+                .then(function () {
+                        $http.post('/api/v1/configuration/metadata/remove/all')
+                            .success(function () {
+                                $common.showInfo('All metadata have been removed');
 
-            $scope.toggleValid = function () {
-                $scope.ui.showValid = !$scope.ui.showValid;
+                                $scope.metadatas = [];
 
-                var validFilter = $filter('metadatasValidation');
+                                $scope.selectItem(undefined, undefined);
 
-                var idx = _.findIndex(validFilter($scope.metadatas, $scope.ui.showValid, true), function (metadata) {
-                    return metadata._id === $scope.selectedItem._id;
+                                $scope.ui.showValid = true;
+                            })
+                            .error(function (errMsg) {
+                                $common.showError(errMsg);
+                            });
                 });
+        };
 
-                if (idx === -1)
-                    $scope.selectItem(undefined, undefined);
-            };
+        $scope.toggleValid = function () {
+            $scope.ui.showValid = !$scope.ui.showValid;
 
-            var pairFields = {
-                fields: {msg: 'Query field class', id: 'QryField', idPrefix: 'Key', searchCol: 'name', valueCol: 'key', classValidation: true, dupObjName: 'name'},
-                aliases: {id: 'Alias', idPrefix: 'Value', searchCol: 'alias', valueCol: 'value', dupObjName: 'alias'}
-            };
+            var validFilter = $filter('metadatasValidation');
 
-            $scope.tablePairValid = function (item, field, index) {
-                var pairField = pairFields[field.model];
+            var idx = _.findIndex(validFilter($scope.metadatas, $scope.ui.showValid, true), function (metadata) {
+                return metadata._id === $scope.selectedItem._id;
+            });
 
-                var pairValue = $table.tablePairValue(field, index);
+            if (idx === -1)
+                $scope.selectItem(undefined, undefined);
+        };
 
-                if (pairField) {
-                    var model = item[field.model];
+        var pairFields = {
+            fields: {msg: 'Query field class', id: 'QryField', idPrefix: 'Key', searchCol: 'name', valueCol: 'key', classValidation: true, dupObjName: 'name'},
+            aliases: {id: 'Alias', idPrefix: 'Value', searchCol: 'alias', valueCol: 'value', dupObjName: 'alias'}
+        };
 
-                    if ($common.isDefined(model)) {
-                        var idx = _.findIndex(model, function (pair) {
-                            return pair[pairField.searchCol] === pairValue[pairField.valueCol];
-                        });
+        $scope.tablePairValid = function (item, field, index) {
+            var pairField = pairFields[field.model];
 
-                        // Found duplicate by key.
-                        if (idx >= 0 && idx !== index)
-                            return showPopoverMessage($scope.panels, 'query', $table.tableFieldId(index, pairField.idPrefix + pairField.id), 'Field with such ' + pairField.dupObjName + ' already exists!');
-                    }
+            var pairValue = $table.tablePairValue(field, index);
 
-                    if (pairField.classValidation && !$common.isValidJavaClass(pairField.msg, pairValue.value, true, $table.tableFieldId(index, 'Value' + pairField.id)))
-                        return $table.tableFocusInvalidField(index, 'Value' + pairField.id);
-                }
+            if (pairField) {
+                var model = item[field.model];
 
-                return true;
-            };
+                if ($common.isDefined(model)) {
+                    var idx = _.findIndex(model, function (pair) {
+                        return pair[pairField.searchCol] === pairValue[pairField.valueCol];
+                    });
 
-            function tableD

<TRUNCATED>

Mime
View raw message