You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/11/09 08:38:43 UTC

[29/50] [abbrv] ignite git commit: Web console beta-5.

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/form/validator/java-package-specified.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/java-package-specified.directive.js b/modules/web-console/frontend/app/modules/form/validator/java-package-specified.directive.js
index 5f24eb7..d3274ae 100644
--- a/modules/web-console/frontend/app/modules/form/validator/java-package-specified.directive.js
+++ b/modules/web-console/frontend/app/modules/form/validator/java-package-specified.directive.js
@@ -22,9 +22,13 @@ export default ['javaPackageSpecified', ['JavaTypes', (JavaTypes) => {
 
         const allowBuiltIn = attrs.javaPackageSpecified === 'allow-built-in';
 
-        ngModel.$validators.javaPackageSpecified = (value) => _.isEmpty(value) ||
+        ngModel.$validators.javaPackageSpecified = (value) => attrs.validationActive === 'false' ||
+            _.isEmpty(value) ||
             !JavaTypes.validClassName(value) || JavaTypes.packageSpecified(value) ||
             (allowBuiltIn && !JavaTypes.nonBuiltInClass(value));
+
+        if (attrs.validationActive !== 'always')
+            attrs.$observe('validationActive', () => ngModel.$validate());
     };
 
     return {

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/nodes/Nodes.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/nodes/Nodes.service.js b/modules/web-console/frontend/app/modules/nodes/Nodes.service.js
new file mode 100644
index 0000000..b320ae4
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/nodes/Nodes.service.js
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import nodesDialogTemplate from './nodes-dialog.jade';
+
+const DEFAULT_OPTIONS = {
+    grid: {
+        multiSelect: false
+    }
+};
+
+class Nodes {
+    static $inject = ['$q', '$modal'];
+
+    /**
+     * @param $q
+     * @param $modal
+     */
+    constructor($q, $modal) {
+        this.$q = $q;
+        this.$modal = $modal;
+    }
+
+    selectNode(nodes, cacheName, options = DEFAULT_OPTIONS) {
+        const { $q, $modal } = this;
+        const defer = $q.defer();
+        options.target = cacheName;
+
+        const modalInstance = $modal({
+            templateUrl: nodesDialogTemplate,
+            show: true,
+            resolve: {
+                nodes: () => nodes || [],
+                options: () => options
+            },
+            placement: 'center',
+            controller: 'nodesDialogController',
+            controllerAs: '$ctrl'
+        });
+
+        modalInstance.$scope.$ok = (data) => {
+            defer.resolve(data);
+            modalInstance.$scope.$hide();
+        };
+
+        modalInstance.$scope.$cancel = () => {
+            defer.reject();
+            modalInstance.$scope.$hide();
+        };
+
+        return defer.promise;
+    }
+}
+
+export default Nodes;

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/nodes/nodes-dialog.controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/nodes/nodes-dialog.controller.js b/modules/web-console/frontend/app/modules/nodes/nodes-dialog.controller.js
new file mode 100644
index 0000000..3e588ac
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/nodes/nodes-dialog.controller.js
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const NID_TEMPLATE = '<div class="ui-grid-cell-contents" title="{{ COL_FIELD }}">{{ COL_FIELD | limitTo:8 }}</div>';
+
+const COLUMNS_DEFS = [
+    {displayName: 'Node ID8', field: 'nid', headerTooltip: 'Node ID8', cellTemplate: NID_TEMPLATE, minWidth: 85, width: 85, pinnedLeft: true},
+    {displayName: 'Node IP', field: 'ip', headerTooltip: 'Primary IP address of node', minWidth: 75, width: 120},
+    {displayName: 'Grid name', field: 'gridName', headerTooltip: 'Name of node grid cluster', minWidth: 75, width: 120},
+    {displayName: 'Version', field: 'version', headerTooltip: 'Node version', minWidth: 75, width: 140},
+    {displayName: 'OS information', field: 'os', headerTooltip: 'OS information for node\'s host', minWidth: 125}
+];
+
+export default ['$scope', '$animate', 'uiGridConstants', 'nodes', 'options', function($scope, $animate, uiGridConstants, nodes, options) {
+    const $ctrl = this;
+
+    const updateSelected = () => {
+        const nids = $ctrl.gridApi.selection.getSelectedRows().map((node) => node.nid).sort();
+
+        if (!_.isEqual(nids, $ctrl.selected))
+            $ctrl.selected = nids;
+    };
+
+    $ctrl.nodes = nodes;
+    $ctrl.options = options;
+    $ctrl.selected = [];
+
+    $ctrl.gridOptions = {
+        data: nodes,
+        columnVirtualizationThreshold: 30,
+        columnDefs: COLUMNS_DEFS,
+        enableRowSelection: true,
+        enableRowHeaderSelection: false,
+        enableColumnMenus: false,
+        multiSelect: true,
+        modifierKeysToMultiSelect: true,
+        noUnselect: false,
+        flatEntityAccess: true,
+        fastWatch: true,
+        onRegisterApi: (api) => {
+            $animate.enabled(api.grid.element, false);
+
+            $ctrl.gridApi = api;
+
+            api.selection.on.rowSelectionChanged($scope, updateSelected);
+            api.selection.on.rowSelectionChangedBatch($scope, updateSelected);
+
+            $ctrl.gridApi.grid.element.css('height', '270px');
+
+            setTimeout(() => $ctrl.gridApi.core.notifyDataChange(uiGridConstants.dataChange.COLUMN), 300);
+        },
+        ...options.grid
+    };
+}];

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/nodes/nodes-dialog.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/nodes/nodes-dialog.jade b/modules/web-console/frontend/app/modules/nodes/nodes-dialog.jade
new file mode 100644
index 0000000..d9ea68c
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/nodes/nodes-dialog.jade
@@ -0,0 +1,35 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+.modal.ignite-nodes-dialog(tabindex='-1' role='dialog')
+    .modal-dialog
+        .modal-content
+            .modal-header
+                button.close(ng-click='$cancel()' aria-hidden='true') &times;
+                h4.modal-title Select Node
+            .modal-body.modal-body-with-scroll
+                p Choose node to execute query for cache: #[strong {{ $ctrl.options.target }}]
+
+                .panel.panel-default.nodes-grid
+                    .panel-heading
+                        label Cache Nodes: {{ $ctrl.nodes.length }}
+
+                    .panel-body.panel-body_collapse
+                        .grid(ui-grid='$ctrl.gridOptions' ui-grid-resize-columns ui-grid-selection ui-grid-pinning)
+
+            .modal-footer
+                button.btn.btn-primary(id='confirm-btn-confirm' ng-click='$ok($ctrl.selected)' ng-disabled='$ctrl.selected.length === 0') Select node
+                button.btn.btn-default(id='confirm-btn-close' ng-click='$cancel()') Cancel

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/nodes/nodes-dialog.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/nodes/nodes-dialog.scss b/modules/web-console/frontend/app/modules/nodes/nodes-dialog.scss
new file mode 100644
index 0000000..0c65e54
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/nodes/nodes-dialog.scss
@@ -0,0 +1,20 @@
+.ignite-nodes-dialog {
+    label {
+        font-size: 18px;
+        margin-right: 20px;
+    }
+
+    .ui-grid-pinned-container.ui-grid-pinned-container-left .ui-grid-cell:last-child,
+    .ui-grid-pinned-container.ui-grid-pinned-container-left .ui-grid-header-cell:last-child,
+    .ui-grid-header-cell:last-child .ui-grid-column-resizer.right {
+      //border-right: none;
+    }
+
+    .nodes-grid {
+        height: 320px;
+    }
+    .panel-body_collapse {
+        padding: 0;
+        margin: 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/nodes/nodes.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/nodes/nodes.module.js b/modules/web-console/frontend/app/modules/nodes/nodes.module.js
new file mode 100644
index 0000000..4e68b39
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/nodes/nodes.module.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import angular from 'angular';
+
+import './nodes-dialog.scss';
+
+import Nodes from './Nodes.service';
+import nodesDialogController from './nodes-dialog.controller';
+
+angular.module('ignite-console.nodes', [])
+    .service('IgniteNodes', Nodes)
+    .controller('nodesDialogController', nodesDialogController);

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/sql/sql.controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/sql/sql.controller.js b/modules/web-console/frontend/app/modules/sql/sql.controller.js
index 92eb7be..0c2be01 100644
--- a/modules/web-console/frontend/app/modules/sql/sql.controller.js
+++ b/modules/web-console/frontend/app/modules/sql/sql.controller.js
@@ -27,6 +27,8 @@ const SCAN_CACHE_WITH_FILTER = 'VISOR_SCAN_CACHE_WITH_FILTER';
 /** Prefix for node local key for SCAN near queries. */
 const SCAN_CACHE_WITH_FILTER_CASE_SENSITIVE = 'VISOR_SCAN_CACHE_WITH_FILTER_CASE_SENSITIVE';
 
+const NON_COLLOCATED_JOINS_SINCE = '1.7.0';
+
 const _fullColName = (col) => {
     const res = [];
 
@@ -51,6 +53,25 @@ class Paragraph {
 
         _.assign(this, paragraph);
 
+        const _enableColumns = (categories, visible) => {
+            _.forEach(categories, (cat) => {
+                cat.visible = visible;
+
+                _.forEach(this.gridOptions.columnDefs, (col) => {
+                    if (col.displayName === cat.name)
+                        col.visible = visible;
+                });
+            });
+
+            this.gridOptions.api.grid.refresh();
+        };
+
+        const _selectableColumns = () => _.filter(this.gridOptions.categories, (cat) => cat.selectable);
+
+        this.toggleColumns = (category, visible) => _enableColumns([category], visible);
+        this.selectAllColumns = () => _enableColumns(_selectableColumns(), true);
+        this.clearAllColumns = () => _enableColumns(_selectableColumns(), false);
+
         Object.defineProperty(this, 'gridOptions', {value: {
             enableGridMenu: false,
             enableColumnMenus: false,
@@ -60,6 +81,7 @@ class Paragraph {
                 if (_.isNil(this.api))
                     return;
 
+                this.categories = [];
                 this.columnDefs = _.reduce(self.meta, (cols, col, idx) => {
                     if (self.columnFilter(col)) {
                         cols.push({
@@ -69,6 +91,12 @@ class Paragraph {
                             minWidth: 50,
                             cellClass: 'cell-left'
                         });
+
+                        this.categories.push({
+                            name: col.fieldName,
+                            visible: true,
+                            selectable: true
+                        });
                     }
 
                     return cols;
@@ -133,7 +161,7 @@ class Paragraph {
     }
 
     queryExecuted() {
-        return !_.isEmpty(this.meta);
+        return !_.isEmpty(this.meta) || !_.isEmpty(this.errMsg);
     }
 
     scanExplain() {
@@ -154,8 +182,8 @@ class Paragraph {
 }
 
 // Controller for SQL notebook screen.
-export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$modal', '$popover', 'IgniteLoading', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'IgniteNotebook', 'IgniteScanFilterInput', 'uiGridExporterConstants',
-    function($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $modal, $popover, Loading, LegacyUtils, Messages, Confirm, agentMonitor, IgniteChartColors, Notebook, ScanFilterInput, uiGridExporterConstants) {
+export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$filter', '$modal', '$popover', 'IgniteLoading', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'IgniteNotebook', 'IgniteScanFilterInput', 'IgniteNodes', 'uiGridExporterConstants', 'IgniteVersion',
+    function($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $filter, $modal, $popover, Loading, LegacyUtils, Messages, Confirm, agentMonitor, IgniteChartColors, Notebook, ScanFilterInput, Nodes, uiGridExporterConstants, Version) {
         let stopTopology = null;
 
         const _tryStopRefresh = function(paragraph) {
@@ -212,7 +240,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
             }
         };
 
-        $scope.maskCacheName = (cacheName) => _.isEmpty(cacheName) ? '<default>' : cacheName;
+        const maskCacheName = $filter('defaultName');
 
         // We need max 1800 items to hold history for 30 mins in case of refresh every second.
         const HISTORY_LENGTH = 1800;
@@ -781,24 +809,30 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
          * @private
          */
         const _refreshFn = () =>
-            agentMonitor.topology()
-                .then((clusters) => {
-                    $scope.caches = _.sortBy(_.reduce(clusters, (items, cluster) => {
-                        _.forEach(cluster.caches, (cache) => {
-                            let item = _.find(items, {name: cache.name});
+            agentMonitor.topology(true)
+                .then((nodes) => {
+                    $scope.caches = _.sortBy(_.reduce(nodes, (cachesAcc, node) => {
+                        _.forEach(node.caches, (cache) => {
+                            let item = _.find(cachesAcc, {name: cache.name});
 
                             if (_.isNil(item)) {
-                                cache.label = $scope.maskCacheName(cache.name);
+                                cache.label = maskCacheName(cache.name);
 
-                                cache.nodeIds = [];
+                                cache.nodes = [];
 
-                                items.push(item = cache);
+                                cachesAcc.push(item = cache);
                             }
 
-                            item.nodeIds.push(cluster.nodeId);
+                            item.nodes.push({
+                                nid: node.nodeId.toUpperCase(),
+                                ip: _.head(node.attributes['org.apache.ignite.ips'].split(', ')),
+                                version: node.attributes['org.apache.ignite.build.ver'],
+                                gridName: node.attributes['org.apache.ignite.ignite.name'],
+                                os: `${node.attributes['os.name']} ${node.attributes['os.arch']} ${node.attributes['os.version']}`
+                            });
                         });
 
-                        return items;
+                        return cachesAcc;
                     }, []), 'label');
 
                     if (_.isEmpty($scope.caches))
@@ -830,7 +864,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
                 .then(_refreshFn)
                 .then(() => Loading.finish('sqlLoading'))
                 .then(() => {
-                    $root.IgniteDemoMode && _.forEach($scope.notebook.paragraphs, $scope.execute);
+                    $root.IgniteDemoMode && _.forEach($scope.notebook.paragraphs, (paragraph) => $scope.execute(paragraph));
 
                     stopTopology = $interval(_refreshFn, 5000, 0, false);
                 });
@@ -1072,7 +1106,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
             paragraph.gridOptions.rebuildColumns();
 
             paragraph.chartColumns = _.reduce(paragraph.meta, (acc, col, idx) => {
-                if (paragraph.columnFilter(col) && _notObjectType(col.fieldTypeName)) {
+                if (_notObjectType(col.fieldTypeName)) {
                     acc.push({
                         label: col.fieldName,
                         type: col.fieldTypeName,
@@ -1100,8 +1134,6 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
             if (paragraph.disabledSystemColumns)
                 return;
 
-            paragraph.systemColumns = !paragraph.systemColumns;
-
             paragraph.columnFilter = _columnFilter(paragraph);
 
             paragraph.chartColumns = [];
@@ -1113,10 +1145,11 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
 
         /**
          * @param {Object} paragraph Query
+         * @param {Boolean} clearChart Flag is need clear chart model.
          * @param {{columns: Array, rows: Array, responseNodeId: String, queryId: int, hasMore: Boolean}} res Query results.
          * @private
          */
-        const _processQueryResult = (paragraph, res) => {
+        const _processQueryResult = (paragraph, clearChart, res) => {
             const prevKeyCols = paragraph.chartKeyCols;
             const prevValCols = paragraph.chartValCols;
 
@@ -1131,11 +1164,12 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
                 if (!LegacyUtils.isDefined(paragraph.chartValCols))
                     paragraph.chartValCols = [];
 
-                if (res.columns.length <= 2) {
+                if (res.columns.length) {
                     const _key = _.find(res.columns, {fieldName: '_KEY'});
                     const _val = _.find(res.columns, {fieldName: '_VAL'});
 
-                    paragraph.disabledSystemColumns = (res.columns.length === 2 && _key && _val) ||
+                    paragraph.disabledSystemColumns = !(_key && _val) ||
+                        (res.columns.length === 2 && _key && _val) ||
                         (res.columns.length === 1 && (_key || _val));
                 }
 
@@ -1175,12 +1209,8 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
 
             const chartHistory = paragraph.chartHistory;
 
-            // Clear history on query change.
-            const queryChanged = paragraph.prevQuery !== paragraph.query;
-
-            if (queryChanged) {
-                paragraph.prevQuery = paragraph.query;
-
+                // Clear history on query change.
+            if (clearChart) {
                 chartHistory.length = 0;
 
                 _.forEach(paragraph.charts, (chart) => chart.data.length = 0);
@@ -1198,7 +1228,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
             if (_.isNil(paragraph.result) || paragraph.result === 'none' || paragraph.scanExplain())
                 paragraph.result = 'table';
             else if (paragraph.chart()) {
-                let resetCharts = queryChanged;
+                let resetCharts = clearChart;
 
                 if (!resetCharts) {
                     const curKeyCols = paragraph.chartKeyCols;
@@ -1214,15 +1244,34 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
         };
 
         const _closeOldQuery = (paragraph) => {
-            const queryId = paragraph.queryArgs && paragraph.queryArgs.queryId;
+            if (paragraph.queryId)
+                return agentMonitor.queryClose(paragraph.resNodeId, paragraph.queryId);
 
-            return queryId ? agentMonitor.queryClose(queryId) : $q.when();
+            return $q.when();
         };
 
-        const cacheNode = (name) => {
-            const cache = _.find($scope.caches, {name});
+        /**
+         * @param {String} name Cache name.
+         * @return {Array.<String>} Nids
+         */
+        const cacheNodes = (name) => {
+            return _.find($scope.caches, {name}).nodes;
+        };
+
+        /**
+         * @param {String} name Cache name.
+         * @param {Boolean} local Local query.
+         * @return {String} Nid
+         */
+        const _chooseNode = (name, local) => {
+            const nodes = cacheNodes(name);
+
+            if (local) {
+                return Nodes.selectNode(nodes, name)
+                    .then((selectedNids) => _.head(selectedNids));
+            }
 
-            return cache.nodeIds[_.random(0, cache.nodeIds.length - 1)];
+            return Promise.resolve(nodes[_.random(0, nodes.length - 1)].nid);
         };
 
         const _executeRefresh = (paragraph) => {
@@ -1230,8 +1279,9 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
 
             agentMonitor.awaitAgent()
                 .then(() => _closeOldQuery(paragraph))
-                .then(() => agentMonitor.query(cacheNode(args.cacheName), args.cacheName, args.query, false, args.pageSize))
-                .then(_processQueryResult.bind(this, paragraph))
+                .then(() => args.localNid || _chooseNode(args.cacheName, false))
+                .then((nid) => agentMonitor.query(nid, args.cacheName, args.query, args.nonCollocatedJoins, !!args.localNid, args.pageSize))
+                .then(_processQueryResult.bind(this, paragraph, false))
                 .catch((err) => paragraph.errMsg = err.message);
         };
 
@@ -1249,41 +1299,62 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
             }
         };
 
-        $scope.execute = (paragraph) => {
-            if (!$scope.actionAvailable(paragraph, true))
-                return;
+        const addLimit = (query, limitSize) =>
+            `SELECT * FROM (
+            ${query} 
+            ) LIMIT ${limitSize}`;
 
-            Notebook.save($scope.notebook)
-                .catch(Messages.showError);
+        $scope.nonCollocatedJoinsAvailable = (paragraph) => {
+            const cache = _.find($scope.caches, {name: paragraph.cacheName});
 
-            paragraph.prevQuery = paragraph.queryArgs ? paragraph.queryArgs.query : paragraph.query;
+            if (cache)
+                return !!_.find(cache.nodes, (node) => Version.since(node.version, NON_COLLOCATED_JOINS_SINCE));
 
-            _showLoading(paragraph, true);
+            return false;
+        };
 
-            _closeOldQuery(paragraph)
-                .then(() => {
-                    const args = paragraph.queryArgs = {
-                        cacheName: paragraph.cacheName,
-                        pageSize: paragraph.pageSize,
-                        query: paragraph.query,
-                        type: 'QUERY'
-                    };
+        $scope.execute = (paragraph, nonCollocatedJoins = false) => {
+            const local = !!paragraph.localQry;
 
-                    return agentMonitor.query(cacheNode(paragraph.cacheName), args.cacheName, args.query, false, args.pageSize);
-                })
-                .then((res) => {
-                    _processQueryResult(paragraph, res);
+            $scope.actionAvailable(paragraph, true) && _chooseNode(paragraph.cacheName, local)
+                .then((nid) => {
+                    Notebook.save($scope.notebook)
+                        .catch(Messages.showError);
 
-                    _tryStartRefresh(paragraph);
-                })
-                .catch((err) => {
-                    paragraph.errMsg = err.message;
+                    paragraph.prevQuery = paragraph.queryArgs ? paragraph.queryArgs.query : paragraph.query;
 
-                    _showLoading(paragraph, false);
+                    _showLoading(paragraph, true);
 
-                    $scope.stopRefresh(paragraph);
-                })
-                .then(() => paragraph.ace.focus());
+                    return _closeOldQuery(paragraph)
+                        .then(() => {
+                            const args = paragraph.queryArgs = {
+                                cacheName: paragraph.cacheName,
+                                pageSize: paragraph.pageSize,
+                                query: paragraph.query,
+                                firstPageOnly: paragraph.firstPageOnly,
+                                nonCollocatedJoins,
+                                type: 'QUERY',
+                                localNid: local ? nid : null
+                            };
+
+                            const qry = args.firstPageOnly ? addLimit(args.query, args.pageSize) : paragraph.query;
+
+                            return agentMonitor.query(nid, args.cacheName, qry, nonCollocatedJoins, local, args.pageSize);
+                        })
+                        .then((res) => {
+                            _processQueryResult(paragraph, true, res);
+
+                            _tryStartRefresh(paragraph);
+                        })
+                        .catch((err) => {
+                            paragraph.errMsg = err.message;
+
+                            _showLoading(paragraph, false);
+
+                            $scope.stopRefresh(paragraph);
+                        })
+                        .then(() => paragraph.ace.focus());
+                });
         };
 
         const _cancelRefresh = (paragraph) => {
@@ -1310,7 +1381,8 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
             _showLoading(paragraph, true);
 
             _closeOldQuery(paragraph)
-                .then(() => {
+                .then(() => _chooseNode(paragraph.cacheName, false))
+                .then((nid) => {
                     const args = paragraph.queryArgs = {
                         cacheName: paragraph.cacheName,
                         pageSize: paragraph.pageSize,
@@ -1318,9 +1390,9 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
                         type: 'EXPLAIN'
                     };
 
-                    return agentMonitor.query(cacheNode(paragraph.cacheName), args.cacheName, args.query, false, args.pageSize);
+                    return agentMonitor.query(nid, args.cacheName, args.query, false, false, args.pageSize);
                 })
-                .then(_processQueryResult.bind(this, paragraph))
+                .then(_processQueryResult.bind(this, paragraph, true))
                 .catch((err) => {
                     paragraph.errMsg = err.message;
 
@@ -1330,34 +1402,48 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
         };
 
         $scope.scan = (paragraph, query = null) => {
-            if (!$scope.actionAvailable(paragraph, false))
-                return;
+            const local = !!paragraph.localQry;
 
-            Notebook.save($scope.notebook)
-                .catch(Messages.showError);
+            $scope.actionAvailable(paragraph, false) && _chooseNode(paragraph.cacheName, local)
+                .then((nid) => {
+                    Notebook.save($scope.notebook)
+                        .catch(Messages.showError);
 
-            _cancelRefresh(paragraph);
+                    _cancelRefresh(paragraph);
 
-            _showLoading(paragraph, true);
+                    _showLoading(paragraph, true);
 
-            _closeOldQuery(paragraph)
-                .then(() => {
-                    const args = paragraph.queryArgs = {
-                        cacheName: paragraph.cacheName,
-                        pageSize: paragraph.pageSize,
-                        query,
-                        type: 'SCAN'
-                    };
+                    _closeOldQuery(paragraph)
+                        .then(() => {
+                            const args = paragraph.queryArgs = {
+                                cacheName: paragraph.cacheName,
+                                pageSize: paragraph.pageSize,
+                                firstPageOnly: paragraph.firstPageOnly,
+                                query,
+                                type: 'SCAN',
+                                localNid: local ? nid : null
+                            };
 
-                    return agentMonitor.query(cacheNode(paragraph.cacheName), args.cacheName, query, false, args.pageSize);
-                })
-                .then(_processQueryResult.bind(this, paragraph))
-                .catch((err) => {
-                    paragraph.errMsg = err.message;
+                            return agentMonitor.query(nid, args.cacheName, query, false, local, args.pageSize);
+                        })
+                        .then((res) => {
+                            if (paragraph.firstPageOnly) {
+                                res.hasMore = false;
 
-                    _showLoading(paragraph, false);
-                })
-                .then(() => paragraph.ace.focus());
+                                _processQueryResult(paragraph, true, res);
+
+                                _closeOldQuery(paragraph);
+                            }
+                            else
+                                _processQueryResult(paragraph, true, res);
+                        })
+                        .catch((err) => {
+                            paragraph.errMsg = err.message;
+
+                            _showLoading(paragraph, false);
+                        })
+                        .then(() => paragraph.ace.focus());
+                });
         };
 
         $scope.scanWithFilter = (paragraph) => {
@@ -1480,10 +1566,11 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
             paragraph.gridOptions.api.exporter.pdfExport(uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE);
         };
 
-        $scope.exportCsvAll = function(paragraph) {
+        $scope.exportCsvAll = (paragraph) => {
             const args = paragraph.queryArgs;
 
-            agentMonitor.queryGetAll(cacheNode(args.cacheName), args.cacheName, args.query, false)
+            return Promise.resolve(args.localNid || _chooseNode(args.cacheName, false))
+                .then((nid) => agentMonitor.queryGetAll(nid, args.cacheName, args.query, !!args.nonCollocatedJoins, !!args.localNid))
                 .then((res) => _export(paragraph.name + '-all.csv', paragraph.columnFilter, res.columns, res.rows))
                 .catch(Messages.showError)
                 .then(() => paragraph.ace.focus());
@@ -1601,7 +1688,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
 
                 if (_.isNil(paragraph.queryArgs.query)) {
                     scope.title = 'SCAN query';
-                    scope.content = [`SCAN query for cache: <b>${$scope.maskCacheName(paragraph.queryArgs.cacheName)}</b>`];
+                    scope.content = [`SCAN query for cache: <b>${maskCacheName(paragraph.queryArgs.cacheName, true)}</b>`];
                 }
                 else if (paragraph.queryArgs.query.startsWith(SCAN_CACHE_WITH_FILTER)) {
                     scope.title = 'SCAN query';
@@ -1613,7 +1700,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
                     else
                         filter = paragraph.queryArgs.query.substr(SCAN_CACHE_WITH_FILTER.length);
 
-                    scope.content = [`SCAN query for cache: <b>${$scope.maskCacheName(paragraph.queryArgs.cacheName)}</b> with filter: <b>${filter}</b>`];
+                    scope.content = [`SCAN query for cache: <b>${maskCacheName(paragraph.queryArgs.cacheName, true)}</b> with filter: <b>${filter}</b>`];
                 }
                 else if (paragraph.queryArgs.query .startsWith('EXPLAIN ')) {
                     scope.title = 'Explain query';

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration.state.js b/modules/web-console/frontend/app/modules/states/configuration.state.js
index 7fd7541..888c804 100644
--- a/modules/web-console/frontend/app/modules/states/configuration.state.js
+++ b/modules/web-console/frontend/app/modules/states/configuration.state.js
@@ -30,7 +30,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
     // Summary screen
     .directive(...summaryTabs)
     // Services.
-    .service('igniteConfigurationResource', ConfigurationResource)
+    .service('IgniteConfigurationResource', ConfigurationResource)
     // Configure state provider.
     .config(['$stateProvider', 'AclRouteProvider', ($stateProvider, AclRoute) => {
         // Setup the states.

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/client-near-cache.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/client-near-cache.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/client-near-cache.jade
new file mode 100644
index 0000000..9d8ccbe
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/client-near-cache.jade
@@ -0,0 +1,50 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../app/helpers/jade/mixins.jade
+
+-var form = 'clientNearCache'
+-var model = 'backupItem.clientNearConfiguration'
+
+.panel.panel-default(ng-form=form novalidate ng-show='backupItem.cacheMode === "PARTITIONED"')
+    .panel-heading(bs-collapse-toggle='' ng-click='ui.loadPanel("#{form}")')
+        ignite-form-panel-chevron
+        label Client near cache
+        ignite-form-field-tooltip.tipLabel
+            | Near cache settings for client nodes#[br]
+            | Near cache is a small local cache that stores most recently or most frequently accessed data#[br]
+            | Should be used in case when it is impossible to send computations to remote nodes
+        ignite-form-revert
+    .panel-collapse(role='tabpanel' bs-collapse-target id=form)
+        .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
+            .col-sm-6
+                -var enabled = model + '.clientNearCacheEnabled'
+
+                .settings-row
+                    +checkbox('Enabled', enabled, '"clientNacheEnabled"', 'Flag indicating whether to configure near cache')
+                .settings-row
+                    +number('Start size:', model + '.nearStartSize', '"clientNearStartSize"', enabled, '375000', '0',
+                        'Initial cache size for near cache which will be used to pre-create internal hash table after start')
+                .settings-row
+                    +evictionPolicy(model + '.nearEvictionPolicy', '"clientNearCacheEvictionPolicy"', enabled, 'false',
+                        'Near cache eviction policy\
+                        <ul>\
+                            <li>Least Recently Used (LRU) - Eviction policy based on LRU algorithm and supports batch eviction</li>\
+                            <li>First In First Out (FIFO) - Eviction policy based on FIFO algorithm and supports batch eviction</li>\
+                            <li>SORTED - Eviction policy which will select the minimum cache entry for eviction</li>\
+                        </ul>')
+            .col-sm-6
+                +preview-xml-java('backupItem', 'cacheClientNearCache')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/general.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/general.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/general.jade
index e3147b1..e9ff143 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/caches/general.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/general.jade
@@ -23,6 +23,9 @@ include ../../../../../app/helpers/jade/mixins.jade
     .panel-heading(bs-collapse-toggle)
         ignite-form-panel-chevron
         label General
+        ignite-form-field-tooltip.tipLabel
+            | Common cache configuration#[br]
+            | #[a(href="https://apacheignite.readme.io/docs/data-grid" target="_blank") More info]
         ignite-form-revert
     .panel-collapse(role='tabpanel' bs-collapse-target id='general')
         .panel-body

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/memory.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/memory.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/memory.jade
index e9f29fd..724418f 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/caches/memory.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/memory.jade
@@ -24,7 +24,8 @@ include ../../../../../app/helpers/jade/mixins.jade
         ignite-form-panel-chevron
         label Memory
         ignite-form-field-tooltip.tipLabel
-            | Cache memory settings
+            | Cache memory settings#[br]
+            | #[a(href="https://apacheignite.readme.io/docs/off-heap-memory" target="_blank") More info]
         ignite-form-revert
     .panel-collapse(role='tabpanel' bs-collapse-target id=form)
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
@@ -77,7 +78,7 @@ include ../../../../../app/helpers/jade/mixins.jade
                         </ul>')
                 .settings-row(data-ng-if=model + '.offHeapMode === 1 && ' + model + '.memoryMode !== "OFFHEAP_VALUES"')
                     +number-required('Off-heap memory max size:', model + '.offHeapMaxMemory', '"offHeapMaxMemory"', 'true',
-                        model + '.offHeapMode === 1', '', 1,
+                        model + '.offHeapMode === 1', 'Enter off-heap memory size', '1',
                         'Maximum amount of memory available to off-heap storage in bytes')
                 .settings-row
                     -var onHeapTired = model + '.memoryMode === "ONHEAP_TIERED"'
@@ -94,7 +95,14 @@ include ../../../../../app/helpers/jade/mixins.jade
                             <li>SORTED - Eviction policy which will select the minimum cache entry for eviction</li>\
                         </ul>')
                 .settings-row
-                    +number('Start size:', model + '.startSize', '"startSize"', 'true', '1500000', '0', 'Initial cache size in entries number')
+                    +number('Start size:', model + '.startSize', '"startSize"', 'true', '1500000', '0',
+                        'In terms of size and capacity, Ignite internal cache map acts exactly like a normal Java HashMap: it has some initial capacity\
+                        (which is pretty small by default), which doubles as data arrives. The process of internal cache map resizing is CPU-intensive\
+                        and time-consuming, and if you load a huge dataset into cache (which is a normal use case), the map will have to resize a lot of times.\
+                        To avoid that, you can specify the initial cache map capacity, comparable to the expected size of your dataset.\
+                        This will save a lot of CPU resources during the load time, because the map would not have to resize.\
+                        For example, if you expect to load 10 million entries into cache, you can set this property to 10 000 000.\
+                        This will save you from cache internal map resizes.')
                 .settings-row
                     +checkbox('Swap enabled', model + '.swapEnabled', '"swapEnabled"', 'Flag indicating whether swap storage is enabled or not for this cache')
             .col-sm-6

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/near-cache-client.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/near-cache-client.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/near-cache-client.jade
new file mode 100644
index 0000000..ba538c2
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/near-cache-client.jade
@@ -0,0 +1,51 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../app/helpers/jade/mixins.jade
+
+-var form = 'clientNearCache'
+-var model = 'backupItem'
+
+.panel.panel-default(ng-form=form novalidate ng-show='backupItem.cacheMode === "PARTITIONED"')
+    .panel-heading(bs-collapse-toggle='' ng-click='ui.loadPanel("#{form}")')
+        ignite-form-panel-chevron
+        label Near cache on client node
+        ignite-form-field-tooltip.tipLabel
+            | Near cache settings for client nodes#[br]
+            | Near cache is a small local cache that stores most recently or most frequently accessed data#[br]
+            | Should be used in case when it is impossible to send computations to remote nodes
+        ignite-form-revert
+    .panel-collapse(role='tabpanel' bs-collapse-target id=form)
+        .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
+            .col-sm-6
+                -var nearCfg = model + '.clientNearConfiguration'
+                -var enabled = nearCfg + '.enabled'
+
+                .settings-row
+                    +checkbox('Enabled', enabled, '"clientNearEnabled"', 'Flag indicating whether to configure near cache')
+                .settings-row
+                    +number('Start size:', nearCfg + '.nearStartSize', '"clientNearStartSize"', enabled, '375000', '0',
+                        'Initial cache size for near cache which will be used to pre-create internal hash table after start')
+                .settings-row
+                    +evictionPolicy(nearCfg + '.nearEvictionPolicy', '"clientNearCacheEvictionPolicy"', enabled, 'false',
+                        'Near cache eviction policy\
+                        <ul>\
+                            <li>Least Recently Used (LRU) - Eviction policy based on LRU algorithm and supports batch eviction</li>\
+                            <li>First In First Out (FIFO) - Eviction policy based on FIFO algorithm and supports batch eviction</li>\
+                            <li>SORTED - Eviction policy which will select the minimum cache entry for eviction</li>\
+                        </ul>')
+            .col-sm-6
+                +preview-xml-java(model, 'cacheNearClient')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/near-cache-server.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/near-cache-server.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/near-cache-server.jade
new file mode 100644
index 0000000..a96b947
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/near-cache-server.jade
@@ -0,0 +1,52 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+include ../../../../../app/helpers/jade/mixins.jade
+
+-var form = 'serverNearCache'
+-var model = 'backupItem'
+
+.panel.panel-default(ng-form=form novalidate ng-show='#{model}.cacheMode === "PARTITIONED"')
+    .panel-heading(bs-collapse-toggle='' ng-click='ui.loadPanel("#{form}")')
+        ignite-form-panel-chevron
+        label Near cache on server node
+        ignite-form-field-tooltip.tipLabel
+            | Near cache settings#[br]
+            | Near cache is a small local cache that stores most recently or most frequently accessed data#[br]
+            | Should be used in case when it is impossible to send computations to remote nodes#[br]
+            | #[a(href="https://apacheignite.readme.io/docs/near-caches" target="_blank") More info]
+        ignite-form-revert
+    .panel-collapse(role='tabpanel' bs-collapse-target id=form)
+        .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
+            .col-sm-6
+                -var nearCfg = model + '.nearConfiguration'
+                -var enabled = nearCfg + '.enabled'
+
+                .settings-row
+                    +checkbox('Enabled', enabled, '"nearCacheEnabled"', 'Flag indicating whether to configure near cache')
+                .settings-row
+                    +number('Start size:', nearCfg + '.nearStartSize', '"nearStartSize"', enabled, '375000', '0',
+                        'Initial cache size for near cache which will be used to pre-create internal hash table after start')
+                .settings-row
+                    +evictionPolicy(model + '.nearConfiguration.nearEvictionPolicy', '"nearCacheEvictionPolicy"', enabled, 'false',
+                        'Near cache eviction policy\
+                        <ul>\
+                            <li>Least Recently Used (LRU) - Eviction policy based on LRU algorithm and supports batch eviction</li>\
+                            <li>First In First Out (FIFO) - Eviction policy based on FIFO algorithm and supports batch eviction</li>\
+                            <li>SORTED - Eviction policy which will select the minimum cache entry for eviction</li>\
+                        </ul>')
+            .col-sm-6
+                +preview-xml-java(model, 'cacheNearServer')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/node-filter.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/node-filter.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/node-filter.jade
index ee28c87..eb74736 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/caches/node-filter.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/node-filter.jade
@@ -36,7 +36,6 @@ include ../../../../../app/helpers/jade/mixins.jade
                     +dropdown('Node filter:', nodeFilterKind, '"nodeFilter"', 'true', 'Not set',
                         '[\
                             {value: "IGFS", label: "IGFS nodes"},\
-                            {value: "OnNodes", label: "Specified nodes"},\
                             {value: "Custom", label: "Custom"},\
                             {value: undefined, label: "Not set"}\
                         ]',
@@ -56,53 +55,5 @@ include ../../../../../app/helpers/jade/mixins.jade
 
                         +java-class('Class name:', customNodeFilter + '.className', '"customNodeFilter"',
                             'true', required, 'Class name of custom node filter implementation')
-                    div(ng-show='#{nodeFilterKind} === "OnNodes"')
-                        -var nodeSetFilter = nodeFilter + '.OnNodes.nodeIds'
-
-                        +ignite-form-group(ng-form=form ng-model=nodeSetFilter)
-                            -var uniqueTip = 'Such node ID already exists!'
-
-                            ignite-form-field-label
-                                | Node IDs
-                            ignite-form-group-tooltip
-                                | Set of node IDs to deploy cache
-                            ignite-form-group-add(ng-click='group.add = [{}]')
-                                | Add new node ID
-
-                            .group-content(ng-if='#{nodeSetFilter}.length')
-                                -var model = 'obj.model';
-                                -var name = '"edit" + $index'
-                                -var valid = form + '[' + name + '].$valid'
-                                -var save = nodeSetFilter + '[$index] = ' + model
-
-                                div(ng-repeat='model in #{nodeSetFilter} track by $index' ng-init='obj = {}')
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        .indexField
-                                            | {{ $index+1 }})
-                                        +table-remove-button(nodeSetFilter, 'Remove node ID')
-
-                                        span(ng-hide='field.edit')
-                                            a.labelFormField(ng-click='field.edit = true; #{model} = model;') {{ model }}
-                                        span(ng-if='field.edit' ng-init='#{field} = model')
-                                            +table-uuid-field(name, model, nodeSetFilter, valid, save, false, true)
-                                                +table-save-button(valid, save, false)
-                                                +unique-feedback(name, uniqueTip)
-                                                +uuid-feedback(name)
-
-                            .group-content(ng-repeat='field in group.add')
-                                -var model = 'new';
-                                -var name = '"new"'
-                                -var valid = form + '[' + name + '].$valid'
-                                -var save = nodeSetFilter + '.push(' + model + ')'
-
-                                div
-                                    label.col-xs-12.col-sm-12.col-md-12
-                                        +table-uuid-field(name, model, nodeSetFilter, valid, save, true, true)
-                                            +table-save-button(valid, save, true)
-                                            +unique-feedback(name, uniqueTip)
-                                            +uuid-feedback(name)
-                            .group-content-empty(id='nodeSetFilter' ng-if='!(#{nodeSetFilter}.length) && !group.add.length')
-                                | Not defined
-
             .col-sm-6
                 +preview-xml-java(model, 'cacheNodeFilter', 'igfss')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/query.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/query.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/query.jade
index c709135..ae13166 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/caches/query.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/query.jade
@@ -24,21 +24,28 @@ include ../../../../../app/helpers/jade/mixins.jade
         ignite-form-panel-chevron
         label Queries & Indexing
         ignite-form-field-tooltip.tipLabel
-            | Cache queries settings
+            | Cache queries settings#[br]
+            | #[a(href="https://apacheignite.readme.io/docs/sql-queries" target="_blank") More info]
         ignite-form-revert
     .panel-collapse(role='tabpanel' bs-collapse-target id=form)
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
             .col-sm-6
                 .settings-row
                     +text('SQL schema name:', model + '.sqlSchema', '"sqlSchema"', 'false', 'Input schema name',
-                        'Schema name allow to use existing database queries in your application and according to SQL ANSI-99<br>\
-                        Cache is reffered by schema name in cross-cache queries<br/>\
-                        Nonquoted identifiers are not case sensitive. Quoted identifiers are case sensitive<br/>\
-                        When SQL schema is not specified, quoted cache name should be used<br/>\
-                        Query example without schema name:<br>\
-                        SELECT .... FROM "cache1".Type1 JOIN "cache2".Type2 ...<br>\
-                        The same query using schema name:<br>\
-                        SELECT .... FROM cache1.Type1 JOIN cache2.Type2 ...')
+                        'Specify any custom name to be used as SQL schema for current cache. This name will correspond to SQL ANSI-99 standard.\
+                        Nonquoted identifiers are not case sensitive. Quoted identifiers are case sensitive.\
+                        When SQL schema is not specified, quoted cache name should used instead.<br/>\
+                        For example:\
+                        <ul>\
+                            <li>\
+                                Query without schema names (quoted cache names will be used):\
+                                SELECT * FROM "PersonsCache".Person p INNER JOIN "OrganizationsCache".Organization o on p.org = o.id\
+                            </li>\
+                            <li>\
+                                The same query using schema names "Persons" and "Organizations":\
+                                SELECT * FROM Persons.Person p INNER JOIN Organizations.Organization o on p.org = o.id\
+                            </li>\
+                        </ul>')
                 .settings-row
                     +number('On-heap cache for off-heap indexes:', model + '.sqlOnheapRowCacheSize', '"sqlOnheapRowCacheSize"', 'true', '10240', '1',
                         'Number of SQL rows which will be cached onheap to avoid deserialization on each SQL index access')
@@ -97,7 +104,8 @@ include ../../../../../app/helpers/jade/mixins.jade
                         'Flag indicating whether SQL indexes should support snapshots')
                 .settings-row
                     +checkbox('Escape table and filed names', model + '.sqlEscapeAll', '"sqlEscapeAll"',
-                        'If enabled than all the SQL table and field names will be escaped with double quotes like "tableName"."fieldName"<br/>\
-                        This enforces case sensitivity for field names and also allows having special characters in table and field names')
+                        'If enabled than all schema, table and field names will be escaped with double quotes (for example: "tableName"."fieldName").<br/>\
+                        This enforces case sensitivity for field names and also allows having special characters in table and field names.<br/>\
+                        Escaped names will be used for creation internal structures in Ignite SQL engine.')
             .col-sm-6
                 +preview-xml-java(model, 'cacheQuery', 'domains')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/rebalance.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/rebalance.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/rebalance.jade
index 6cf2d33..824442c 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/caches/rebalance.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/rebalance.jade
@@ -24,7 +24,8 @@ include ../../../../../app/helpers/jade/mixins.jade
         ignite-form-panel-chevron
         label Rebalance
         ignite-form-field-tooltip.tipLabel
-            | Cache rebalance settings
+            | Cache rebalance settings#[br]
+            | #[a(href="https://apacheignite.readme.io/docs/rebalancing" target="_blank") More info]
         ignite-form-revert
     .panel-collapse(role='tabpanel' bs-collapse-target id=form)
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/server-near-cache.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/server-near-cache.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/server-near-cache.jade
deleted file mode 100644
index 74f500b..0000000
--- a/modules/web-console/frontend/app/modules/states/configuration/caches/server-near-cache.jade
+++ /dev/null
@@ -1,51 +0,0 @@
-//-
-    Licensed to the Apache Software Foundation (ASF) under one or more
-    contributor license agreements.  See the NOTICE file distributed with
-    this work for additional information regarding copyright ownership.
-    The ASF licenses this file to You under the Apache License, Version 2.0
-    (the "License"); you may not use this file except in compliance with
-    the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-
-include ../../../../../app/helpers/jade/mixins.jade
-
--var form = 'serverNearCache'
--var model = 'backupItem'
-
-.panel.panel-default(ng-form=form novalidate ng-show='#{model}.cacheMode === "PARTITIONED"')
-    .panel-heading(bs-collapse-toggle='' ng-click='ui.loadPanel("#{form}")')
-        ignite-form-panel-chevron
-        label Server near cache
-        ignite-form-field-tooltip.tipLabel
-            | Near cache settings#[br]
-            | Near cache is a small local cache that stores most recently or most frequently accessed data#[br]
-            | Should be used in case when it is impossible to send computations to remote nodes
-        ignite-form-revert
-    .panel-collapse(role='tabpanel' bs-collapse-target id=form)
-        .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
-            .col-sm-6
-                -var enabled = model + '.nearCacheEnabled'
-                -var nearCfg = model + '.nearConfiguration'
-
-                .settings-row
-                    +checkbox('Enabled', enabled, '"nearCacheEnabled"', 'Flag indicating whether to configure near cache')
-                .settings-row
-                    +number('Start size:', nearCfg + '.nearStartSize', '"nearStartSize"', enabled, '375000', '0',
-                        'Initial cache size for near cache which will be used to pre-create internal hash table after start')
-                .settings-row
-                    +evictionPolicy(model + '.nearConfiguration.nearEvictionPolicy', '"nearCacheEvictionPolicy"', enabled, 'false',
-                        'Near cache eviction policy\
-                        <ul>\
-                            <li>Least Recently Used (LRU) - Eviction policy based on LRU algorithm and supports batch eviction</li>\
-                            <li>First In First Out (FIFO) - Eviction policy based on FIFO algorithm and supports batch eviction</li>\
-                            <li>SORTED - Eviction policy which will select the minimum cache entry for eviction</li>\
-                        </ul>')
-            .col-sm-6
-                +preview-xml-java(model, 'cacheServerNearCache')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/caches/store.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/caches/store.jade b/modules/web-console/frontend/app/modules/states/configuration/caches/store.jade
index 84752d6..a7d8f14 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/caches/store.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/caches/store.jade
@@ -20,34 +20,35 @@ include ../../../../../app/helpers/jade/mixins.jade
 -var model = 'backupItem'
 
 mixin hibernateField(name, model, items, valid, save, newItem)
-    -var reset = newItem ? 'group.add = []' : 'field.edit = false'
-
     -var resetOnEnter = newItem ? '(stopblur = true) && (group.add = [{}])' : '(field.edit = false)'
     -var onEnter = valid + ' && (' + save + '); ' + valid + ' && ' + resetOnEnter + ';'
 
+    -var onEscape = newItem ? 'group.add = []' : 'field.edit = false'
+
     -var resetOnBlur = newItem ? '!stopblur && (group.add = [])' : 'field.edit = false'
     -var onBlur = valid + ' && ( ' + save + '); ' + resetOnBlur + ';'
 
-    if block
-        block
+    div(ignite-on-focus-out=onBlur)
+        if block
+            block
 
-    .input-tip
-        +ignite-form-field-input(name, model, false, 'true', 'key=value')(
-            data-ignite-property-unique=items
-            data-ignite-property-value-specified
-            data-ignite-form-field-input-autofocus='true'
+        .input-tip
+            +ignite-form-field-input(name, model, false, 'true', 'key=value')(
+                data-ignite-property-unique=items
+                data-ignite-property-value-specified
+                data-ignite-form-field-input-autofocus='true'
 
-            ng-blur=onBlur
-            ignite-on-enter=onEnter
-            ignite-on-escape=onEscape
-        )
+                ignite-on-enter=onEnter
+                ignite-on-escape=onEscape
+            )
 
 .panel.panel-default(ng-form=form novalidate)
     .panel-heading(bs-collapse-toggle='' ng-click='ui.loadPanel("#{form}")')
         ignite-form-panel-chevron
         label Store
         ignite-form-field-tooltip.tipLabel
-            | Cache store settings
+            | Cache store settings#[br]
+            | #[a(href="https://apacheignite.readme.io/docs/persistent-store" target="_blank") More info]
         ignite-form-revert
     .panel-collapse(role='tabpanel' bs-collapse-target id=form)
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
@@ -86,6 +87,11 @@ mixin hibernateField(name, model, items, valid, save, newItem)
                                     +dialect('Dialect:', pojoStoreFactory + '.dialect', '"pojoDialect"', required,
                                         'Dialect of SQL implemented by a particular RDBMS:', 'Generic JDBC dialect',
                                         'Choose JDBC dialect')
+                                .details-row
+                                    +checkbox('Escape table and filed names', pojoStoreFactory + '.sqlEscapeAll', '"sqlEscapeAll"',
+                                        'If enabled than all schema, table and field names will be escaped with double quotes (for example: "tableName"."fieldName").<br/>\
+                                        This enforces case sensitivity for field names and also allows having special characters in table and field names.<br/>\
+                                        Escaped names will be used for CacheJdbcPojoStore internal SQL queries.')
                             div(ng-show='#{storeFactoryKind} === "CacheJdbcBlobStoreFactory"')
                                 -var blobStoreFactory = storeFactory + '.CacheJdbcBlobStoreFactory'
                                 -var blobStoreFactoryVia = blobStoreFactory + '.connectVia'
@@ -154,46 +160,28 @@ mixin hibernateField(name, model, items, valid, save, newItem)
                                         ignite-form-group-tooltip
                                             | List of Hibernate properties#[br]
                                             | For example: connection.url=jdbc:h2:mem:exampleDb
-                                        ignite-form-group-add(ng-click='group.add = [{}]')
+                                        ignite-form-group-add(ng-click='tableNewItem(hibernatePropsTbl)')
                                             | Add new Hibernate property
 
                                         -var tipUnique = 'Property with such key already exists!'
                                         -var tipPropertySpecified = 'Property should be present in format key=value!'
 
-                                        .group-content(ng-if='#{hibernateProperties}.length')
-                                            -var model = 'obj.model';
-                                            -var name = '"edit" + $index'
-                                            -var valid = form + '[' + name + '].$valid'
-                                            -var save = hibernateProperties + '[$index] = ' + model
-
-                                            div(ng-repeat='model in #{hibernateProperties} track by $index' ng-init='obj = {}')
-                                                label.col-xs-12.col-sm-12.col-md-12
-                                                    .indexField
-                                                        | {{ $index+1 }})
-                                                    +table-remove-button(hibernateProperties, 'Remove Hibernate property')
-
-                                                    span(ng-hide='field.edit')
-                                                        a.labelFormField(ng-click='field.edit = true; #{model} = model;') {{ model }}
-                                                    span(ng-if='field.edit')
-                                                        +hibernateField(name, model, hibernateProperties, valid, save, false)
-                                                            +table-save-button(valid, save, false)
-                                                            +form-field-feedback(name, 'ignitePropertyUnique', tipUnique)
-                                                            +form-field-feedback(name, 'ignitePropertyValueSpecified', tipPropertySpecified)
-
-                                        .group-content(ng-repeat='field in group.add')
-                                            -var model = 'new';
-                                            -var name = '"new"'
-                                            -var valid = form + '[' + name + '].$valid'
-                                            -var save = hibernateProperties + '.push(' + model + ')'
-
-                                            div
-                                                label.col-xs-12.col-sm-12.col-md-12
-                                                    +hibernateField(name, model, hibernateProperties, valid, save, true)
-                                                        +table-save-button(valid, save, true)
-                                                        +form-field-feedback(name, 'ignitePropertyUnique', tipUnique)
-                                                        +form-field-feedback(name, 'ignitePropertyValueSpecified', tipPropertySpecified)
-                                        .group-content-empty(ng-if='!(#{hibernateProperties}.length) && !group.add.length')
+                                        .group-content-empty(ng-if='!((#{hibernateProperties} && #{hibernateProperties}.length > 0) || tableNewItemActive(hibernatePropsTbl))')
                                             | Not defined
+                                        .group-content(ng-show='(#{hibernateProperties} && #{hibernateProperties}.length > 0) || tableNewItemActive(hibernatePropsTbl)')
+                                            table.links-edit(id='hibernateProps' st-table=hibernateProperties)
+                                                tbody
+                                                    tr(ng-repeat='item in #{hibernateProperties}')
+                                                        td.col-sm-12(ng-hide='tableEditing(hibernatePropsTbl, $index)')
+                                                            a.labelFormField(ng-click='tableStartEdit(backupItem, hibernatePropsTbl, $index)') {{item.name}} = {{item.value}}
+                                                            +btn-remove('tableRemove(backupItem, hibernatePropsTbl, $index)', '"Remove Property"')
+                                                        td.col-sm-12(ng-if='tableEditing(hibernatePropsTbl, $index)')
+                                                            +table-pair-edit('hibernatePropsTbl', 'cur', 'Property name', 'Property value', false, false, '{{::hibernatePropsTbl.focusId + $index}}', '$index', '=')
+                                                tfoot(ng-show='tableNewItemActive(hibernatePropsTbl)')
+                                                    tr
+                                                        td.col-sm-12
+                                                            +table-pair-edit('hibernatePropsTbl', 'new', 'Property name', 'Property value', false, false, '{{::hibernatePropsTbl.focusId + $index}}', '-1', '=')
+
 
                 .settings-row
                     +checkbox('Keep binary in store', model + '.storeKeepBinary', '"storeKeepBinary"',

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/clusters/atomic.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/atomic.jade b/modules/web-console/frontend/app/modules/states/configuration/clusters/atomic.jade
index c4ef88e..ef83356 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/atomic.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/atomic.jade
@@ -25,7 +25,8 @@ include ../../../../../app/helpers/jade/mixins.jade
         label Atomic configuration
         ignite-form-field-tooltip.tipLabel
             | Configuration for atomic data structures#[br]
-            | Atomics are distributed across the cluster, essentially enabling performing atomic operations (such as increment-and-get or compare-and-set) with the same globally-visible value
+            | Atomics are distributed across the cluster, essentially enabling performing atomic operations (such as increment-and-get or compare-and-set) with the same globally-visible value#[br]
+            | #[a(href="https://apacheignite.readme.io/docs/atomic-types" target="_blank") More info]
         ignite-form-revert 
     .panel-collapse(role='tabpanel' bs-collapse-target='' id=form)
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/clusters/attributes.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/attributes.jade b/modules/web-console/frontend/app/modules/states/configuration/clusters/attributes.jade
index 29e7a79..b41b97c 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/attributes.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/attributes.jade
@@ -43,8 +43,8 @@ include ../../../../../app/helpers/jade/mixins.jade
                         .group-content(ng-show='(#{userAttributes} && #{userAttributes}.length > 0) || tableNewItemActive(attributesTbl)')
                             table.links-edit(id='attributes' st-table=userAttributes)
                                 tbody
-                                    tr(ng-repeat='item in #{userAttributes}')
-                                        td.col-sm-12(ng-show='!tableEditing(attributesTbl, $index)')
+                                    tr(ng-repeat='item in #{userAttributes} track by $index')
+                                        td.col-sm-12(ng-hide='tableEditing(attributesTbl, $index)')
                                             a.labelFormField(ng-click='tableStartEdit(backupItem, attributesTbl, $index)') {{item.name}} = {{item.value}}
                                             +btn-remove('tableRemove(backupItem, attributesTbl, $index)', '"Remove attribute"')
                                         td.col-sm-12(ng-show='tableEditing(attributesTbl, $index)')

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/clusters/binary.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/binary.jade b/modules/web-console/frontend/app/modules/states/configuration/clusters/binary.jade
index c63e2d9..9994087 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/binary.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/binary.jade
@@ -20,18 +20,13 @@ include ../../../../../app/helpers/jade/mixins.jade
 -var model = 'backupItem.binaryConfiguration'
 -var types = model + '.typeConfigurations'
 
-//- Mixin for java name field with enabled condition.
-mixin binary-types-java-class(lbl, model, name, enabled, required, remove, autofocus, tip)
-    +java-class(lbl, model, name, enabled, required, tip)
-        if (remove)
-            +table-remove-button(types, 'Remove type configuration')
-
 .panel.panel-default(ng-form=form novalidate)
     .panel-heading(bs-collapse-toggle ng-click='ui.loadPanel("#{form}")')
         ignite-form-panel-chevron
         label Binary configuration
         ignite-form-field-tooltip.tipLabel
-            | Configuration of specific binary types
+            | Configuration of specific binary types#[br]
+            | #[a(href="https://apacheignite.readme.io/docs/binary-marshaller" target="_blank") More info]
         ignite-form-revert
     .panel-collapse(role='tabpanel' bs-collapse-target id=form)
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
@@ -45,8 +40,6 @@ mixin binary-types-java-class(lbl, model, name, enabled, required, remove, autof
                 .settings-row
                     +java-class('Serializer:', model + '.serializer', '"serializer"', 'true', 'false', 'Class with custom serialization logic for binary objects')
                 .settings-row
-                    -var form = 'binaryTypeConfigurations';
-
                     +ignite-form-group()
                         ignite-form-field-label
                             | Type configurations
@@ -59,15 +52,22 @@ mixin binary-types-java-class(lbl, model, name, enabled, required, remove, autof
                         .group-content(ng-repeat='model in #{types} track by $index')
                             hr(ng-if='$index !== 0')
                             .settings-row
-                                +binary-types-java-class('Type name:', 'model.typeName', '"typeName" + $index', 'true', 'true', true, 'true', 'Type name')
+                                +java-class-autofocus('Type name:', 'model.typeName', '"typeName" + $index', 'true', 'true', 'true', 'Type name')
+                                    +table-remove-button(types, 'Remove type configuration')
                             .settings-row
-                                +binary-types-java-class('ID mapper:', 'model.idMapper', '"idMapper" + $index', 'true', 'false', false, 'false',
+                                +java-class('ID mapper:', 'model.idMapper', '"idMapper" + $index', 'true', 'false',
                                     'Maps given from BinaryNameMapper type and filed name to ID that will be used by Ignite in internals<br/>\
-                                    Ignite never writes full strings for field or type/class names. Instead, for performance reasons, Ignite writes integer hash codes for type/class and field names. It has been tested that hash code conflicts for the type/class names or the field names within the same type are virtually non - existent and, to gain performance, it is safe to work with hash codes. For the cases when hash codes for different types or fields actually do collide <b>BinaryIdMapper</b> allows to override the automatically generated hash code IDs for the type and field names')
+                                    Ignite never writes full strings for field or type/class names.\
+                                    Instead, for performance reasons, Ignite writes integer hash codes for type/class and field names.\
+                                    It has been tested that hash code conflicts for the type/class names or the field names within the same type are virtually non - existent and,\
+                                    to gain performance, it is safe to work with hash codes.\
+                                    For the cases when hash codes for different types or fields actually do collide <b>BinaryIdMapper</b> allows to override the automatically generated hash code IDs for the type and field names')
                             .settings-row
-                                +binary-types-java-class('Name mapper:', 'model.nameMapper', '"nameMapper" + $index', 'true', 'false', false, 'false', 'Maps type/class and field names to different names')
+                                +java-class('Name mapper:', 'model.nameMapper', '"nameMapper" + $index', 'true', 'false',
+                                    'Maps type/class and field names to different names')
                             .settings-row
-                                +binary-types-java-class('Serializer:', 'model.serializer', '"serializer" + $index', 'true', 'false', false, 'false', 'Class with custom serialization logic for binary object')
+                                +java-class('Serializer:', 'model.serializer', '"serializer" + $index', 'true', 'false',
+                                    'Class with custom serialization logic for binary object')
                             .settings-row
                                 +checkbox('Enum', 'model.enum', 'enum', 'Flag indicating that this type is the enum')
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/clusters/cache-key-cfg.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration/clusters/cache-key-cfg.jade b/modules/web-console/frontend/app/modules/states/configuration/clusters/cache-key-cfg.jade
index 3531c77..45ccc13 100644
--- a/modules/web-console/frontend/app/modules/states/configuration/clusters/cache-key-cfg.jade
+++ b/modules/web-console/frontend/app/modules/states/configuration/clusters/cache-key-cfg.jade
@@ -18,7 +18,6 @@ include ../../../../../app/helpers/jade/mixins.jade
 
 -var form = 'cacheKeyCfg'
 -var model = 'backupItem.cacheKeyConfiguration'
--var items = model;
 
 .panel.panel-default(ng-form=form novalidate)
     .panel-heading(bs-collapse-toggle ng-click='ui.loadPanel("#{form}")')
@@ -31,9 +30,7 @@ include ../../../../../app/helpers/jade/mixins.jade
         .panel-body(ng-if='ui.isPanelLoaded("#{form}")')
             .col-sm-6
                 .settings-row
-                    -var form = form + 'TypeConfigurations'
-
-                    +ignite-form-group(ng-form=form ng-model=model)
+                    +ignite-form-group()
                         ignite-form-field-label
                             | Cache key configuration
                         ignite-form-group-tooltip
@@ -45,8 +42,8 @@ include ../../../../../app/helpers/jade/mixins.jade
                         .group-content(ng-repeat='model in #{model} track by $index')
                             hr(ng-if='$index !== 0')
                             .settings-row
-                                +java-class('Type name:', 'model.typeName', '"cacheKeyTypeName" + $index', 'true', 'true', 'Type name')
-                                    +table-remove-button(items, 'Remove cache key configuration')
+                                +java-class-autofocus('Type name:', 'model.typeName', '"cacheKeyTypeName" + $index', 'true', 'true', 'true', 'Type name')
+                                    +table-remove-button(model, 'Remove cache key configuration')
                             .settings-row
                                 +text('Affinity key field name:', 'model.affinityKeyFieldName', '"affinityKeyFieldName" + $index', true, 'Enter field name', 'Affinity key field name')
             .col-sm-6