You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2015/09/11 12:06:48 UTC

ignite git commit: IGNITE-1408: Fixed mark dirty/pristine.

Repository: ignite
Updated Branches:
  refs/heads/ignite-843 b463eaa80 -> c86d24f63


IGNITE-1408: Fixed mark dirty/pristine.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/c86d24f6
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/c86d24f6
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/c86d24f6

Branch: refs/heads/ignite-843
Commit: c86d24f6346282285d8dafe9b65ad44157165cc4
Parents: b463eaa
Author: Alexey Kuznetsov <ak...@apache.org>
Authored: Fri Sep 11 17:07:25 2015 +0700
Committer: Alexey Kuznetsov <ak...@apache.org>
Committed: Fri Sep 11 17:07:25 2015 +0700

----------------------------------------------------------------------
 .../main/js/controllers/caches-controller.js    | 173 +++++++------------
 .../main/js/controllers/clusters-controller.js  |  87 ++++------
 .../src/main/js/controllers/common-module.js    |  71 +++++---
 .../main/js/controllers/metadata-controller.js  |  97 ++++-------
 .../src/main/js/views/configuration/caches.jade |   2 +-
 .../main/js/views/configuration/clusters.jade   |   2 +-
 .../main/js/views/configuration/metadata.jade   |   2 +-
 .../src/main/js/views/includes/controls.jade    |   4 +-
 8 files changed, 173 insertions(+), 265 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/c86d24f6/modules/control-center-web/src/main/js/controllers/caches-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/caches-controller.js b/modules/control-center-web/src/main/js/controllers/caches-controller.js
index cf012ea..437772c 100644
--- a/modules/control-center-web/src/main/js/controllers/caches-controller.js
+++ b/modules/control-center-web/src/main/js/controllers/caches-controller.js
@@ -22,6 +22,8 @@ controlCenterModule.controller('cachesController', [
             // Initialize the super class and extend it.
             angular.extend(this, $controller('save-remove', {$scope: $scope}));
 
+            $scope.ui = $common.formUI(2);
+
             $scope.joinTip = $common.joinTip;
             $scope.getModel = $common.getModel;
             $scope.javaBuildInClasses = $common.javaBuildInClasses;
@@ -36,7 +38,7 @@ controlCenterModule.controller('cachesController', [
             $scope.tableRemove = function (item, field, index) {
                 $table.tableRemove(item, field, index);
 
-                $common.markChanged($scope.ui.inputForm, 'cacheBackupItemChanged');
+                $scope.ui.markDirty();
             };
 
             $scope.tableSimpleSave = $table.tableSimpleSave;
@@ -58,8 +60,6 @@ controlCenterModule.controller('cachesController', [
 
             $scope.previewChanged = $preview.previewChanged;
 
-            $scope.formChanged = $common.formChanged;
-
             $scope.hidePopover = $common.hidePopover;
 
             var showPopoverMessage = $common.showPopoverMessage;
@@ -98,8 +98,6 @@ controlCenterModule.controller('cachesController', [
                 {value: 'H2', label: 'H2 database'}
             ];
 
-            $scope.ui = {expanded: false};
-
             $scope.toggleExpanded = function () {
                 $scope.ui.expanded = !$scope.ui.expanded;
 
@@ -224,6 +222,11 @@ controlCenterModule.controller('cachesController', [
                 return true;
             };
 
+            function selectFirstItem() {
+                if ($scope.caches.length > 0)
+                    $scope.selectItem($scope.caches[0]);
+            }
+
             // When landing on the page, get caches and show them.
             $http.post('caches/list')
                 .success(function (data) {
@@ -235,110 +238,42 @@ controlCenterModule.controller('cachesController', [
                         return {value: meta._id, label: meta.valueType, kind: meta.kind, meta: meta}
                     });
 
-                    var restoredItem = angular.fromJson(sessionStorage.cacheBackupItem);
+                    var lastSelectedCache = angular.fromJson(sessionStorage.lastSelectedCache);
 
-                    if (restoredItem) {
-                        restoredItem.metadatas = _.filter(restoredItem.metadatas, function (metaId) {
-                            return _.findIndex($scope.metadatas, function (scopeMeta) {
-                                    return scopeMeta.value == metaId;
-                                }) >= 0;
+                    if (lastSelectedCache) {
+                        var idx = _.findIndex($scope.caches, function (cache) {
+                            return cache._id == lastSelectedCache;
                         });
 
-                        if (restoredItem._id) {
-                            var idx = _.findIndex($scope.caches, function (cache) {
-                                return cache._id == restoredItem._id;
-                            });
-
-                            if (idx >= 0) {
-                                var cache = $scope.caches[idx];
-
-                                var restoredSelectedItem = angular.fromJson(sessionStorage.cacheSelectedItem);
-
-                                // Clusters not changed by user. We should take clusters from server as they could be changed on Clusters screen.
-                                if (restoredSelectedItem && _.isEqual(restoredItem.clusters, restoredSelectedItem.clusters)) {
-                                    restoredItem.clusters = [];
-
-                                    _.forEach(cache.clusters, function (cluster) {
-                                        restoredItem.clusters.push(cluster)
-                                    });
-                                }
-                                else {
-                                    // Clusters changed by user. We need to remove deleted clusters (if any).
-                                    restoredItem.clusters = _.filter(restoredItem.clusters, function (clusterId) {
-                                        return _.findIndex($scope.clusters, function (scopeCluster) {
-                                                return scopeCluster.value == clusterId;
-                                            }) >= 0;
-                                    });
-                                }
+                        if (idx >= 0)
+                            $scope.selectItem($scope.caches[idx]);
+                        else {
+                            sessionStorage.removeItem('lastSelectedCache');
 
-                                // Metadatas not changed by user. We should take metadatas from server as they could be changed on Metadata screen.
-                                if (restoredSelectedItem && _.isEqual(restoredItem.metadatas, restoredSelectedItem.metadatas)) {
-                                    restoredItem.metadatas = [];
+                            selectFirstItem();
+                        }
 
-                                    _.forEach(cache.metadatas, function (meta) {
-                                        restoredItem.metadatas.push(meta)
-                                    });
-                                }
-                                else {
-                                    // Metadatas changed by user. We need to remove deleted metadatas (if any).
-                                    restoredItem.metadatas = _.filter(restoredItem.metadatas, function (metaId) {
-                                        return _.findIndex($scope.metadatas, function (scopeMeta) {
-                                                return scopeMeta.value == metaId;
-                                            }) >= 0;
-                                    });
-                                }
+                    }
+                    else
+                        selectFirstItem();
 
-                                $scope.selectItem(cache, restoredItem, sessionStorage.cacheBackupItemChanged);
+                    function cacheMetadatas(item) {
+                        return _.reduce($scope.metadatas, function (memo, meta) {
+                            if (item && _.contains(item.metadatas, meta.value)) {
+                                memo.push(meta.meta);
                             }
-                            else
-                                sessionStorage.removeItem('cacheBackupItem');
-                        }
-                        else
-                            $scope.selectItem(undefined, restoredItem, sessionStorage.cacheBackupItemChanged)
-                    }
-                    else if ($scope.caches.length > 0)
-                        $scope.selectItem($scope.caches[0]);
 
-                    function isStoreFactoryDefined(cache) {
-                        return $common.isDefined(cache)
-                            && $common.isDefined(cache.cacheStoreFactory)
-                            && $common.isDefined(cache.cacheStoreFactory.kind);
+                            return memo;
+                        }, []);
                     }
 
-                    $scope.$watch('backupItem', function (val) {
+                    $scope.$watch('backupItem', function (val, old) {
                         if (val) {
-                            // Collect cache metadatas.
-                            var cacheMetadatas = _.reduce($scope.metadatas, function(memo, meta){
-                                if (_.contains(val.metadatas, meta.value)) {
-                                    memo.push(meta.meta);
-                                }
-
-                                return memo;
-                            }, []);
-
-                            var prevVal = angular.fromJson(sessionStorage.cacheBackupItem);
-                            var prevCacheStore = isStoreFactoryDefined(prevVal);
-                            var newCacheStore = isStoreFactoryDefined(val);
-
-                            if (!prevCacheStore && !newCacheStore) {
-                                if (_.findIndex(cacheMetadatas, $common.metadataForStoreConfigured) >= 0) {
-                                    val.cacheStoreFactory.kind = 'CacheJdbcPojoStoreFactory';
-
-                                    if (!val.readThrough && !val.writeThrough) {
-                                        val.readThrough = true;
-                                        val.writeThrough = true;
-                                    }
-
-                                    $timeout(function () {
-                                        $common.ensureActivePanel($scope.panels, 'store');
-                                    });
-                                }
-                            }
-
+                            var metas = cacheMetadatas();
                             var varName = 'cache';
 
-                            $scope.preview.general.xml = $generatorXml.cacheMetadatas(cacheMetadatas, $generatorXml.cacheGeneral(val)).asString();
-                            $scope.preview.general.java = $generatorJava.cacheMetadatas(cacheMetadatas, varName, $generatorJava.cacheGeneral(val, varName)).asString();
+                            $scope.preview.general.xml = $generatorXml.cacheMetadatas(metas, $generatorXml.cacheGeneral(val)).asString();
+                            $scope.preview.general.java = $generatorJava.cacheMetadatas(metas, varName, $generatorJava.cacheGeneral(val, varName)).asString();
                             $scope.preview.general.allDefaults = $common.isEmptyString($scope.preview.general.xml);
 
                             $scope.preview.memory.xml = $generatorXml.cacheMemory(val).asString();
@@ -369,9 +304,30 @@ controlCenterModule.controller('cachesController', [
                             $scope.preview.statistics.java = $generatorJava.cacheStatistics(val, varName).asString();
                             $scope.preview.statistics.allDefaults = $common.isEmptyString($scope.preview.statistics.xml);
 
-                            sessionStorage.cacheBackupItem = angular.toJson(val);
+                            $scope.ui.markDirty();
+                        }
+                    }, true);
+
+                    $scope.$watch('backupItem.metadatas', function (val) {
+                        var item = $scope.backupItem;
+
+                        var cacheStoreFactory = $common.isDefined(item) &&
+                            $common.isDefined(item.cacheStoreFactory) &&
+                            $common.isDefined(item.cacheStoreFactory.kind);
 
-                            $common.markChanged($scope.ui.inputForm, 'cacheBackupItemChanged');
+                        if (val && !cacheStoreFactory) {
+                            if (_.findIndex(cacheMetadatas(item), $common.metadataForStoreConfigured) >= 0) {
+                                item.cacheStoreFactory.kind = 'CacheJdbcPojoStoreFactory';
+
+                                if (!item.readThrough && !item.writeThrough) {
+                                    item.readThrough = true;
+                                    item.writeThrough = true;
+                                }
+
+                                $timeout(function () {
+                                    $common.ensureActivePanel($scope.panels, 'store');
+                                });
+                            }
                         }
                     }, true);
                })
@@ -379,16 +335,16 @@ controlCenterModule.controller('cachesController', [
                     $common.showError(errMsg);
                 });
 
-            $scope.selectItem = function (item, backup, changed) {
+            $scope.selectItem = function (item, backup) {
                 function selectItem() {
                     $table.tableReset();
 
                     $scope.selectedItem = item;
 
                     if (item)
-                        sessionStorage.cacheSelectedItem = angular.toJson(item);
+                        sessionStorage.lastSelectedCache = angular.toJson(item._id);
                     else
-                        sessionStorage.removeItem('cacheSelectedItem');
+                        sessionStorage.removeItem('lastSelectedCache');
 
                     _.forEach(previews, function(preview) {
                         preview.attractAttention = false;
@@ -401,15 +357,10 @@ controlCenterModule.controller('cachesController', [
                     else
                         $scope.backupItem = undefined;
 
-                    $timeout(function () {
-                        if (changed)
-                            $common.markChanged($scope.ui.inputForm, 'cacheBackupItemChanged');
-                        else
-                            $common.markPristine($scope.ui.inputForm, 'cacheBackupItemChanged');
-                    }, 50);
+                    $scope.ui.markPristine();
                 }
 
-                $common.confirmUnsavedChanges($confirm, $scope.ui.inputForm, selectItem);
+                $common.confirmUnsavedChanges($scope.ui.isDirty(), selectItem);
 
                 $scope.ui.formTitle = $common.isDefined($scope.backupItem) && $scope.backupItem._id ?
                     'Selected cache: ' + $scope.backupItem.name : 'New cache';
@@ -490,7 +441,7 @@ controlCenterModule.controller('cachesController', [
             function save(item) {
                 $http.post('caches/save', item)
                     .success(function (_id) {
-                        $common.markPristine($scope.ui.inputForm, 'cacheBackupItemChanged');
+                        $scope.ui.markPristine();
 
                         var idx = _.findIndex($scope.caches, function (cache) {
                             return cache._id == _id;
@@ -546,7 +497,7 @@ controlCenterModule.controller('cachesController', [
 
                 $confirm.show('Are you sure you want to remove cache: "' + selectedItem.name + '"?').then(
                     function () {
-                        $common.markPristine($scope.ui.inputForm, 'cacheBackupItemChanged');
+                        $scope.ui.markPristine();
 
                         var _id = selectedItem._id;
 
@@ -582,7 +533,7 @@ controlCenterModule.controller('cachesController', [
 
                 $confirm.show('Are you sure you want to remove all caches?').then(
                     function () {
-                        $common.markPristine($scope.ui.inputForm, 'cacheBackupItemChanged');
+                        $scope.ui.markPristine();
 
                         $http.post('caches/remove/all')
                             .success(function () {

http://git-wip-us.apache.org/repos/asf/ignite/blob/c86d24f6/modules/control-center-web/src/main/js/controllers/clusters-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/clusters-controller.js b/modules/control-center-web/src/main/js/controllers/clusters-controller.js
index 55cef9b..1ad15f9 100644
--- a/modules/control-center-web/src/main/js/controllers/clusters-controller.js
+++ b/modules/control-center-web/src/main/js/controllers/clusters-controller.js
@@ -21,6 +21,8 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
         // Initialize the super class and extend it.
         angular.extend(this, $controller('save-remove', {$scope: $scope}));
 
+        $scope.ui = $common.formUI(2);
+
         $scope.joinTip = $common.joinTip;
         $scope.getModel = $common.getModel;
         $scope.compactJavaName = $common.compactJavaName;
@@ -34,7 +36,7 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
         $scope.tableRemove = function (item, field, index) {
             $table.tableRemove(item, field, index);
 
-            $common.markChanged($scope.ui.inputForm, 'clusterBackupItemChanged');
+            $scope.ui.markDirty();
         };
 
         $scope.tableSimpleSave = $table.tableSimpleSave;
@@ -53,8 +55,6 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
 
         $scope.previewChanged = $preview.previewChanged;
 
-        $scope.formChanged = $common.formChanged;
-
         $scope.hidePopover = $common.hidePopover;
 
         var showPopoverMessage = $common.showPopoverMessage;
@@ -109,8 +109,6 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
 
         $scope.marshallers = $common.mkOptions(['OptimizedMarshaller', 'JdkMarshaller']);
 
-        $scope.ui = {expanded: false};
-
         $scope.toggleExpanded = function () {
             $scope.ui.expanded = !$scope.ui.expanded;
 
@@ -159,6 +157,11 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
                 $common.showError(errMsg);
             });
 
+        function selectFirstItem() {
+            if ($scope.clusters.length > 0)
+                $scope.selectItem($scope.clusters[0]);
+        }
+
         // When landing on the page, get clusters and show them.
         $http.post('clusters/list')
             .success(function (data) {
@@ -168,51 +171,26 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
                     return {value: cache._id, label: cache.name, cache: cache};
                 });
 
-                var restoredItem = angular.fromJson(sessionStorage.clusterBackupItem);
-
-                if (restoredItem) {
-                    if (restoredItem._id) {
-                        var idx = _.findIndex($scope.clusters, function (cluster) {
-                            return cluster._id == restoredItem._id;
-                        });
-
-                        if (idx >= 0) {
-                            var cluster = $scope.clusters[idx];
-
-                            var restoredSelectedItem = angular.fromJson(sessionStorage.clusterSelectedItem);
+                var lastSelectedCluster = angular.fromJson(sessionStorage.lastSelectedCluster);
 
-                            // Caches not changed by user. We should take caches from server as they could be changed on Caches screen.
-                            if (restoredSelectedItem && _.isEqual(restoredItem.caches, restoredSelectedItem.caches)) {
-                                restoredItem.caches = [];
+                if (lastSelectedCluster) {
+                    var idx = _.findIndex($scope.clusters, function (cluster) {
+                        return cluster._id == lastSelectedCluster;
+                    });
 
-                                _.forEach(cluster.caches, function (cache) {
-                                    restoredItem.caches.push(cache)
-                                });
-                            }
-                            else {
-                                // Caches changed by user. We need to remove deleted caches (if any).
-                                restoredItem.caches = _.filter(restoredItem.caches, function (cacheId) {
-                                    return _.findIndex($scope.caches, function (scopeCache) {
-                                            return scopeCache.value == cacheId;
-                                        }) >= 0;
-                                });
-                            }
+                    if (idx >= 0)
+                        $scope.selectItem($scope.clusters[idx]);
+                    else {
+                        sessionStorage.removeItem('lastSelectedCluster');
 
-                            $scope.selectItem(cluster, restoredItem, sessionStorage.clusterBackupItemChanged);
-                        }
-                        else
-                            sessionStorage.removeItem('clusterBackupItem');
+                        selectFirstItem();
                     }
-                    else
-                        $scope.selectItem(undefined, restoredItem, sessionStorage.clusterBackupItemChanged);
                 }
-                else if ($scope.clusters.length > 0)
-                    $scope.selectItem($scope.clusters[0]);
+                else
+                    selectFirstItem();
 
                 $scope.$watch('backupItem', function (val) {
                     if (val) {
-                        sessionStorage.clusterBackupItem = angular.toJson(val);
-
                         var clusterCaches = _.reduce($scope.caches, function(caches, cache){
                             if (_.contains(val.caches, cache.value)) {
                                 caches.push(cache.cache);
@@ -269,7 +247,7 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
                         $scope.preview.transactions.java = $generatorJava.clusterTransactions(val).asString();
                         $scope.preview.transactions.allDefaults = $common.isEmptyString($scope.preview.transactions.xml);
 
-                        $common.markChanged($scope.ui.inputForm, 'clusterBackupItemChanged');
+                        $scope.ui.markDirty();
                     }
                 }, true);
             })
@@ -277,16 +255,16 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
                 $common.showError(errMsg);
             });
 
-        $scope.selectItem = function (item, backup, changed) {
+        $scope.selectItem = function (item, backup) {
             function selectItem() {
                 $table.tableReset();
 
                 $scope.selectedItem = item;
 
-                if (item)
-                    sessionStorage.clusterSelectedItem = angular.toJson(item);
+                if (item && item._id)
+                    sessionStorage.lastSelectedCluster = angular.toJson(item._id);
                 else
-                    sessionStorage.removeItem('clusterSelectedItem');
+                    sessionStorage.removeItem('lastSelectedCluster');
 
                 _.forEach(previews, function(preview) {
                     preview.attractAttention = false;
@@ -299,15 +277,10 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
                 else
                     $scope.backupItem = undefined;
 
-                $timeout(function () {
-                    if (changed)
-                        $common.markChanged($scope.ui.inputForm, 'clusterBackupItemChanged');
-                    else
-                        $common.markPristine($scope.ui.inputForm, 'clusterBackupItemChanged');
-                }, 50);
+                $scope.ui.markPristine();
             }
 
-            $common.confirmUnsavedChanges($confirm, $scope.ui.inputForm, selectItem);
+            $common.confirmUnsavedChanges($scope.ui.isDirty(), selectItem);
 
             $scope.ui.formTitle = $common.isDefined($scope.backupItem) && $scope.backupItem._id ?
                 'Selected cluster: ' + $scope.backupItem.name : 'New cluster';
@@ -399,7 +372,7 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
         function save(item) {
             $http.post('clusters/save', item)
                 .success(function (_id) {
-                    $common.markPristine($scope.ui.inputForm, 'clusterBackupItemChanged');
+                    $scope.ui.markPristine();
 
                     var idx = _.findIndex($scope.clusters, function (cluster) {
                         return cluster._id == _id;
@@ -455,7 +428,7 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
 
             $confirm.show('Are you sure you want to remove cluster: "' + selectedItem.name + '"?').then(
                 function () {
-                    $common.markPristine($scope.ui.inputForm, 'clusterBackupItemChanged');
+                    $scope.ui.markPristine();
 
                     var _id = selectedItem._id;
 
@@ -491,7 +464,7 @@ controlCenterModule.controller('clustersController', ['$scope', '$controller', '
 
             $confirm.show('Are you sure you want to remove all clusters?').then(
                 function () {
-                    $common.markPristine($scope.ui.inputForm, 'clusterBackupItemChanged');
+                    $scope.ui.markPristine();
 
                     $http.post('clusters/remove/all')
                         .success(function () {

http://git-wip-us.apache.org/repos/asf/ignite/blob/c86d24f6/modules/control-center-web/src/main/js/controllers/common-module.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/common-module.js b/modules/control-center-web/src/main/js/controllers/common-module.js
index 9c3bf82..813457f 100644
--- a/modules/control-center-web/src/main/js/controllers/common-module.js
+++ b/modules/control-center-web/src/main/js/controllers/common-module.js
@@ -77,8 +77,8 @@ controlCenterModule.config(function($modalProvider) {
 
 // Unsaved changes configuration.
 controlCenterModule.config(['unsavedWarningsConfigProvider', function(unsavedWarningsConfigProvider) {
-    unsavedWarningsConfigProvider.navigateMessage = 'You have unsaved changes.';
-    unsavedWarningsConfigProvider.reloadMessage = 'You have unsaved changes.';
+    unsavedWarningsConfigProvider.navigateMessage = 'Unsaved changes will be discarded.';
+    unsavedWarningsConfigProvider.reloadMessage = 'Unsaved changes will be discarded.';
 }]);
 
 // Common functions to be used in controllers.
@@ -496,8 +496,14 @@ controlCenterModule.service('$common', [
             return false;
         }
 
-        function formChanged (form) {
-            return isDefined(form) && form.$dirty;
+        function markDirty (form) {
+            if (isDefined(form))
+                form.$setDirty();
+        }
+
+        function markPristine (form) {
+            if (isDefined(form))
+                form.$setPristine();
         }
 
         function getModel(obj, field) {
@@ -666,22 +672,8 @@ controlCenterModule.service('$common', [
                 if (popover)
                     popover.hide();
             },
-            markChanged: function (form, item) {
-                sessionStorage.setItem(item, 'true');
-
-                form.$setDirty();
-            },
-            markPristine: function (form, item) {
-                if (isDefined(form))
-                    form.$setPristine();
-
-                sessionStorage.removeItem(item);
-            },
-            formChanged: function (form) {
-                return isDefined(form) && form.$dirty;
-            },
-            confirmUnsavedChanges: function(confirm, form, selectFunc) {
-                if (formChanged(form)) {
+            confirmUnsavedChanges: function(dirty, selectFunc) {
+                if (dirty) {
                     if ($window.confirm('You have unsaved changes.\n\nAre you sure you want to discard them?'))
                         selectFunc();
                 }
@@ -689,8 +681,8 @@ controlCenterModule.service('$common', [
                     selectFunc();
 
             },
-            saveBtnTipText: function (form, objectName) {
-                if (formChanged(form))
+            saveBtnTipText: function (dirty, objectName) {
+                if (dirty)
                     return 'Save ' + objectName;
 
                 return 'Nothing to save';
@@ -760,6 +752,37 @@ controlCenterModule.service('$common', [
                         break;
                     }
                 }
+            },
+            formUI: function (dirtyCnt) {
+                return {
+                    expanded: false,
+                    dirty: dirtyCnt,
+                    isDirty: function () {
+                        return this.dirty < 0;
+                    },
+                    markDirty: function () {
+                        this.dirty--;
+
+                        if (isDefined(this.inputForm)) {
+                            if (this.dirty < 0)
+                                markDirty(this.inputForm);
+                            else
+                                markPristine(this.inputForm);
+                        }
+                    },
+                    markPristine: function () {
+                        this.dirty = dirtyCnt;
+
+                        if (isDefined(this.inputForm))
+                            markPristine(this.inputForm);
+                    },
+                    noSubmit: function() {
+                        if (this.dirty < 0)
+                            markDirty(this.inputForm);
+                        else
+                            markPristine(this.inputForm);
+                    }
+                };
             }
         }
     }]);
@@ -770,10 +793,8 @@ controlCenterModule.service('$confirm', function ($modal, $rootScope, $q) {
 
     var deferred;
 
-    var dfltCancelTitle = 'Cancel';
-
     // Configure title of cancel button.
-    scope.cancelTitle = dfltCancelTitle;
+    scope.cancelTitle = 'Cancel';
 
     scope.ok = function () {
         deferred.resolve();

http://git-wip-us.apache.org/repos/asf/ignite/blob/c86d24f6/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 df24a31..5cd8e69 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
@@ -24,7 +24,8 @@ controlCenterModule.controller('metadataController', [
 
             // Initialize the super class and extend it.
             angular.extend(this, $controller('agent-download', {$scope: $scope}));
-            $scope.ui = {};
+
+            $scope.ui = $common.formUI(1);
 
             $scope.agentGoal = 'load metadata from database schema';
             $scope.agentTestDriveOption = '--test-metadata';
@@ -43,7 +44,7 @@ controlCenterModule.controller('metadataController', [
             $scope.tableRemove = function (item, field, index) {
                 $table.tableRemove(item, field, index);
 
-                $common.markChanged($scope.ui.inputForm, 'metadataBackupItemChanged');
+                $scope.ui.markDirty();
             };
 
             $scope.tableSimpleSave = $table.tableSimpleSave;
@@ -70,8 +71,6 @@ controlCenterModule.controller('metadataController', [
 
             $scope.previewChanged = $preview.previewChanged;
 
-            $scope.formChanged = $common.formChanged;
-
             $scope.hidePopover = $common.hidePopover;
 
             var showPopoverMessage = $common.showPopoverMessage;
@@ -201,11 +200,6 @@ controlCenterModule.controller('metadataController', [
                     $common.showError(errMsg);
                 });
 
-            function selectFirstItem() {
-                if ($scope.metadatas.length > 0)
-                    $scope.selectItem($scope.metadatas[0]);
-            }
-
             $scope.selectAllSchemas = function () {
                 var allSelected = $scope.loadMeta.allSchemasSelected;
 
@@ -261,7 +255,7 @@ controlCenterModule.controller('metadataController', [
                                 $scope.loadMeta.action = 'connect';
                                 $scope.loadMeta.tables = [];
 
-                                $common.confirmUnsavedChanges($confirm, $scope.ui.inputForm, loadMetaModal.show);
+                                $common.confirmUnsavedChanges($scope.ui.isDirty(), selectItem);
 
                                 $focus('jdbcUrl');
                             });
@@ -569,6 +563,11 @@ controlCenterModule.controller('metadataController', [
                 }
             };
 
+            function selectFirstItem() {
+                if ($scope.metadatas.length > 0)
+                    $scope.selectItem($scope.metadatas[0]);
+            }
+
             // When landing on the page, get metadatas and show them.
             $http.post('metadata/list')
                 .success(function (data) {
@@ -576,46 +575,20 @@ controlCenterModule.controller('metadataController', [
                     $scope.caches = data.caches;
                     $scope.metadatas = data.metadatas;
 
-                    var restoredItem = angular.fromJson(sessionStorage.metadataBackupItem);
-
-                    if (restoredItem) {
-                        if (restoredItem._id) {
-                            var idx = _.findIndex($scope.metadatas, function (metadata) {
-                                return metadata._id == restoredItem._id;
-                            });
-
-                            if (idx >= 0) {
-                                var meta = $scope.metadatas[idx];
-
-                                var restoredSelectedItem = angular.fromJson(sessionStorage.metadataSelectedItem);
+                    var lastSelectedMetadata = angular.fromJson(sessionStorage.lastSelectedMetadata);
 
-                                // Caches not changed by user. We should take caches from server as they could be changed on Caches screen.
-                                if (restoredSelectedItem && _.isEqual(restoredItem.caches, restoredSelectedItem.caches)) {
-                                    restoredItem.caches = [];
-
-                                    _.forEach(meta.caches, function (cache) {
-                                        restoredItem.caches.push(cache)
-                                    });
-                                }
-                                else {
-                                    // Caches changed by user. We need to remove deleted caches (if any).
-                                    restoredItem.caches = _.filter(restoredItem.caches, function (cacheId) {
-                                        return _.findIndex($scope.caches, function (scopeCache) {
-                                                return scopeCache.value == cacheId;
-                                            }) >= 0;
-                                    });
-                                }
+                    if (lastSelectedMetadata) {
+                        var idx = _.findIndex($scope.metadatas, function (metadata) {
+                            return metadata._id == lastSelectedMetadata;
+                        });
 
-                                $scope.selectItem(meta, restoredItem, sessionStorage.metadataBackupItemChanged);
-                            }
-                            else {
-                                sessionStorage.removeItem('metadataBackupItem');
+                        if (idx >= 0)
+                            $scope.selectItem($scope.metadatas[idx]);
+                        else {
+                            sessionStorage.removeItem('lastSelectedMetadata');
 
-                                selectFirstItem();
-                            }
+                            selectFirstItem();
                         }
-                        else
-                            $scope.selectItem(undefined, restoredItem, sessionStorage.metadataBackupItemChanged);
                     }
                     else
                         selectFirstItem();
@@ -626,8 +599,6 @@ controlCenterModule.controller('metadataController', [
 
                     $scope.$watch('backupItem', function (val) {
                         if (val) {
-                            sessionStorage.metadataBackupItem = angular.toJson(val);
-
                             $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);
@@ -640,7 +611,7 @@ controlCenterModule.controller('metadataController', [
                             $scope.preview.store.java = $generatorJava.metadataStore(val).asString();
                             $scope.preview.store.allDefaults = $common.isEmptyString($scope.preview.store.xml);
 
-                            $common.markChanged($scope.ui.inputForm, 'metadataBackupItemChanged');
+                            $scope.ui.markDirty();
                         }
                     }, true);
                 })
@@ -665,16 +636,16 @@ controlCenterModule.controller('metadataController', [
                     $common.showError(errMsg);
                 });
 
-            $scope.selectItem = function (item, backup, changed) {
+            $scope.selectItem = function (item, backup) {
                 function selectItem() {
                     $table.tableReset();
 
                     $scope.selectedItem = item;
 
-                    if (item)
-                        sessionStorage.metadataSelectedItem = angular.toJson(item);
+                    if (item && item._id)
+                        sessionStorage.lastSelectedMetadata = angular.toJson(item._id);
                     else
-                        sessionStorage.removeItem('metadataSelectedItem');
+                        sessionStorage.removeItem('lastSelectedMetadata');
 
                     _.forEach(previews, function(preview) {
                         preview.attractAttention = false;
@@ -687,15 +658,10 @@ controlCenterModule.controller('metadataController', [
                     else
                         $scope.backupItem = undefined;
 
-                    $timeout(function () {
-                        if (changed)
-                            $common.markChanged($scope.ui.inputForm, 'metadataBackupItemChanged');
-                        else
-                            $common.markPristine($scope.ui.inputForm, 'metadataBackupItemChanged');
-                    }, 50);
+                    $scope.ui.markPristine();
                 }
 
-                $common.confirmUnsavedChanges($confirm, $scope.ui.inputForm, selectItem);
+                $common.confirmUnsavedChanges($scope.ui.isDirty(), selectItem);
 
                 $scope.ui.formTitle = $common.isDefined($scope.backupItem) && $scope.backupItem._id
                     ? 'Selected metadata: ' + $scope.backupItem.name
@@ -785,7 +751,7 @@ controlCenterModule.controller('metadataController', [
 
                 $http.post('metadata/save', item)
                     .success(function (_id) {
-                        $common.markPristine($scope.ui.inputForm, 'metadataBackupItemChanged');
+                        $scope.ui.markPristine();
 
                         var idx = _.findIndex($scope.metadatas, function (metadata) {
                             return metadata._id == _id;
@@ -846,7 +812,7 @@ controlCenterModule.controller('metadataController', [
 
                         $http.post('metadata/remove', {_id: _id})
                             .success(function () {
-                                $common.markPristine($scope.ui.inputForm, 'metadataBackupItemChanged');
+                                $scope.ui.markPristine();
 
                                 $common.showInfo('Cache type metadata has been removed: ' + selectedItem.name);
 
@@ -879,7 +845,7 @@ controlCenterModule.controller('metadataController', [
 
                 $confirm.show('Are you sure you want to remove all metadata?').then(
                     function () {
-                        $common.markPristine($scope.ui.inputForm, 'metadataBackupItemChanged');
+                        $scope.ui.markPristine();
 
                         $http.post('metadata/remove/all')
                             .success(function () {
@@ -1178,10 +1144,7 @@ controlCenterModule.controller('metadataController', [
 
                 group.fields.splice(index, 1);
 
-                $common.markChanged($scope.ui.inputForm, 'metadataBackupItemChanged');
-
-                // Dirty state do not change automatically.
-                $scope.ui.inputForm.$dirty = true;
+                $scope.ui.markDirty();
             };
 
             $scope.resetItem = function (group) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/c86d24f6/modules/control-center-web/src/main/js/views/configuration/caches.jade
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/views/configuration/caches.jade b/modules/control-center-web/src/main/js/views/configuration/caches.jade
index 33ef4d6..61c3232 100644
--- a/modules/control-center-web/src/main/js/views/configuration/caches.jade
+++ b/modules/control-center-web/src/main/js/views/configuration/caches.jade
@@ -35,7 +35,7 @@ block content
             //label {{ui.formTitle}}
             br
             hr
-        form.form-horizontal(name='ui.inputForm' ng-if='backupItem' novalidate unsaved-warning-form)
+        form.form-horizontal(name='ui.inputForm' ng-if='backupItem' ng-submit='ui.noSubmit()' novalidate unsaved-warning-form)
             .panel-group(bs-collapse ng-model='panels.activePanels' data-allow-multiple='true')
                 +groups('general', 'backupItem')
                 div(ng-show='ui.expanded')

http://git-wip-us.apache.org/repos/asf/ignite/blob/c86d24f6/modules/control-center-web/src/main/js/views/configuration/clusters.jade
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/views/configuration/clusters.jade b/modules/control-center-web/src/main/js/views/configuration/clusters.jade
index ab987d0..23448f7 100644
--- a/modules/control-center-web/src/main/js/views/configuration/clusters.jade
+++ b/modules/control-center-web/src/main/js/views/configuration/clusters.jade
@@ -35,7 +35,7 @@ block content
             //label {{ui.formTitle}}
             br
             hr
-        form.form-horizontal(name='ui.inputForm' ng-if='backupItem' novalidate unsaved-warning-form)
+        form.form-horizontal(name='ui.inputForm' ng-if='backupItem' ng-submit='ui.noSubmit()' novalidate unsaved-warning-form)
             .panel-group(bs-collapse ng-model='panels.activePanels' data-allow-multiple='true' ng-click='triggerDigest = true')
                 +groups('general', 'backupItem')
                 div(ng-show='ui.expanded')

http://git-wip-us.apache.org/repos/asf/ignite/blob/c86d24f6/modules/control-center-web/src/main/js/views/configuration/metadata.jade
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/views/configuration/metadata.jade b/modules/control-center-web/src/main/js/views/configuration/metadata.jade
index 4653651..fc7e263 100644
--- a/modules/control-center-web/src/main/js/views/configuration/metadata.jade
+++ b/modules/control-center-web/src/main/js/views/configuration/metadata.jade
@@ -37,7 +37,7 @@ block content
             //label {{ui.formTitle}}
             br
             hr
-        form.form-horizontal(name='ui.inputForm' ng-if='backupItem' novalidate unsaved-warning-form)
+        form.form-horizontal(name='ui.inputForm' ng-if='backupItem' ng-submit='ui.noSubmit()' novalidate unsaved-warning-form)
             .panel-group(bs-collapse ng-model='panels.activePanels' data-allow-multiple='true')
                 +groups('metadata', 'backupItem')
             .section

http://git-wip-us.apache.org/repos/asf/ignite/blob/c86d24f6/modules/control-center-web/src/main/js/views/includes/controls.jade
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/views/includes/controls.jade b/modules/control-center-web/src/main/js/views/includes/controls.jade
index 410ba11..bbdf918 100644
--- a/modules/control-center-web/src/main/js/views/includes/controls.jade
+++ b/modules/control-center-web/src/main/js/views/includes/controls.jade
@@ -482,9 +482,9 @@ mixin save-remove-buttons(objectName)
     -var removeTip = '"Remove current ' + objectName + '"'
 
     .panel-tip-container(ng-hide='!backupItem || backupItem._id')
-        a.btn.btn-primary(ng-disabled='!formChanged(ui.inputForm)' ng-click='formChanged(ui.inputForm) ? saveItem() : ""' bs-tooltip data-title='{{saveBtnTipText(ui.inputForm, "#{objectName}")}}' data-placement='bottom' data-trigger='hover') Save
+        a.btn.btn-primary(ng-disabled='!ui.isDirty()' ng-click='ui.isDirty() ? saveItem() : ""' bs-tooltip='' data-title='{{saveBtnTipText(ui.isDirty(), "#{objectName}")}}' data-placement='bottom' data-trigger='hover') Save
     .btn-group.panel-tip-container(ng-show='backupItem._id')
-        a.btn.btn-primary(id='save-item' ng-disabled='!formChanged(ui.inputForm)' ng-click='formChanged(ui.inputForm) ? saveItem() : ""' bs-tooltip data-title='{{saveBtnTipText(ui.inputForm, "#{objectName}")}}' data-placement='bottom' data-trigger='hover') Save
+        a.btn.btn-primary(id='save-item' ng-disabled='!ui.isDirty()' ng-click='ui.isDirty() ? saveItem() : ""' bs-tooltip='' data-title='{{saveBtnTipText(ui.isDirty(), "#{objectName}")}}' data-placement='bottom' data-trigger='hover') Save
         button.btn.dropdown-toggle.btn-primary(id='save-item-dropdown' ng-disabled='!backupItem._id' data-toggle='dropdown' data-container='body' bs-dropdown='saveDropdown' data-placement='bottom-right')
             span.caret
     .btn-group.panel-tip-container(ng-show='backupItem._id')