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 2016/09/09 03:27:06 UTC

[38/52] ignite git commit: Web Console beta-3.

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/configuration/generator/Pom.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/configuration/generator/Pom.service.js b/modules/web-console/frontend/app/modules/configuration/generator/Pom.service.js
new file mode 100644
index 0000000..627a1e3
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/configuration/generator/Pom.service.js
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+// Java built-in class names.
+import POM_DEPENDENCIES from 'app/data/pom-dependencies.json';
+
+/**
+ * Pom file generation entry point.
+ */
+class GeneratorPom {
+    escapeId(s) {
+        if (typeof (s) !== 'string')
+            return s;
+
+        return s.replace(/[^A-Za-z0-9_\-.]+/g, '_');
+    }
+
+    addProperty(res, tag, val) {
+        res.line('<' + tag + '>' + val + '</' + tag + '>');
+    }
+
+    addDependency(deps, groupId, artifactId, version, jar) {
+        if (!_.find(deps, (dep) => dep.groupId === groupId && dep.artifactId === artifactId))
+            deps.push({groupId, artifactId, version, jar});
+    }
+
+    addResource(res, dir, exclude) {
+        res.startBlock('<resource>');
+        if (dir)
+            this.addProperty(res, 'directory', dir);
+
+        if (exclude) {
+            res.startBlock('<excludes>');
+            this.addProperty(res, 'exclude', exclude);
+            res.endBlock('</excludes>');
+        }
+
+        res.endBlock('</resource>');
+    }
+
+    artifact(res, cluster, igniteVersion) {
+        this.addProperty(res, 'groupId', 'org.apache.ignite');
+        this.addProperty(res, 'artifactId', this.escapeId(cluster.name) + '-project');
+        this.addProperty(res, 'version', igniteVersion);
+
+        res.needEmptyLine = true;
+    }
+
+    dependencies(res, cluster, deps) {
+        if (!res)
+            res = $generatorCommon.builder();
+
+        res.startBlock('<dependencies>');
+
+        _.forEach(deps, (dep) => {
+            res.startBlock('<dependency>');
+
+            this.addProperty(res, 'groupId', dep.groupId);
+            this.addProperty(res, 'artifactId', dep.artifactId);
+            this.addProperty(res, 'version', dep.version);
+
+            if (dep.jar) {
+                this.addProperty(res, 'scope', 'system');
+                this.addProperty(res, 'systemPath', '${project.basedir}/jdbc-drivers/' + dep.jar);
+            }
+
+            res.endBlock('</dependency>');
+        });
+
+        res.endBlock('</dependencies>');
+
+        return res;
+    }
+
+    build(res, cluster, excludeGroupIds) {
+        res.startBlock('<build>');
+        res.startBlock('<resources>');
+        this.addResource(res, 'src/main/java', '**/*.java');
+        this.addResource(res, 'src/main/resources');
+        res.endBlock('</resources>');
+
+        res.startBlock('<plugins>');
+        res.startBlock('<plugin>');
+        this.addProperty(res, 'artifactId', 'maven-dependency-plugin');
+        res.startBlock('<executions>');
+        res.startBlock('<execution>');
+        this.addProperty(res, 'id', 'copy-libs');
+        this.addProperty(res, 'phase', 'test-compile');
+        res.startBlock('<goals>');
+        this.addProperty(res, 'goal', 'copy-dependencies');
+        res.endBlock('</goals>');
+        res.startBlock('<configuration>');
+        this.addProperty(res, 'excludeGroupIds', excludeGroupIds.join(','));
+        this.addProperty(res, 'outputDirectory', 'target/libs');
+        this.addProperty(res, 'includeScope', 'compile');
+        this.addProperty(res, 'excludeTransitive', 'true');
+        res.endBlock('</configuration>');
+        res.endBlock('</execution>');
+        res.endBlock('</executions>');
+        res.endBlock('</plugin>');
+        res.startBlock('<plugin>');
+        this.addProperty(res, 'artifactId', 'maven-compiler-plugin');
+        this.addProperty(res, 'version', '3.1');
+        res.startBlock('<configuration>');
+        this.addProperty(res, 'source', '1.7');
+        this.addProperty(res, 'target', '1.7');
+        res.endBlock('</configuration>');
+        res.endBlock('</plugin>');
+        res.endBlock('</plugins>');
+        res.endBlock('</build>');
+
+        res.endBlock('</project>');
+    }
+
+    /**
+     * Add dependency for specified store factory if not exist.
+     * @param storeDeps Already added dependencies.
+     * @param storeFactory Store factory to add dependency.
+     */
+    storeFactoryDependency(storeDeps, storeFactory) {
+        if (storeFactory.dialect && (!storeFactory.connectVia || storeFactory.connectVia === 'DataSource')) {
+            const dep = POM_DEPENDENCIES[storeFactory.dialect];
+
+            this.addDependency(storeDeps, dep.groupId, dep.artifactId, dep.version, dep.jar);
+        }
+    }
+
+    /**
+     * Generate pom.xml.
+     *
+     * @param cluster Cluster  to take info about dependencies.
+     * @param igniteVersion Ignite version for Ignite dependencies.
+     * @param res Resulting output with generated pom.
+     * @returns {string} Generated content.
+     */
+    generate(cluster, igniteVersion, res) {
+        const caches = cluster.caches;
+        const deps = [];
+        const storeDeps = [];
+        const excludeGroupIds = ['org.apache.ignite'];
+
+        const blobStoreFactory = {cacheStoreFactory: {kind: 'CacheHibernateBlobStoreFactory'}};
+
+        if (!res)
+            res = $generatorCommon.builder();
+
+        _.forEach(caches, (cache) => {
+            if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind)
+                this.storeFactoryDependency(storeDeps, cache.cacheStoreFactory[cache.cacheStoreFactory.kind]);
+
+            if (_.get(cache, 'nodeFilter.kind') === 'Exclude')
+                this.addDependency(deps, 'org.apache.ignite', 'ignite-extdata-p2p', igniteVersion);
+        });
+
+        res.line('<?xml version="1.0" encoding="UTF-8"?>');
+
+        res.needEmptyLine = true;
+
+        res.line('<!-- ' + $generatorCommon.mainComment() + ' -->');
+
+        res.needEmptyLine = true;
+
+        res.startBlock('<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">');
+
+        res.line('<modelVersion>4.0.0</modelVersion>');
+
+        res.needEmptyLine = true;
+
+        this.artifact(res, cluster, igniteVersion);
+
+        this.addDependency(deps, 'org.apache.ignite', 'ignite-core', igniteVersion);
+
+        this.addDependency(deps, 'org.apache.ignite', 'ignite-spring', igniteVersion);
+        this.addDependency(deps, 'org.apache.ignite', 'ignite-indexing', igniteVersion);
+        this.addDependency(deps, 'org.apache.ignite', 'ignite-rest-http', igniteVersion);
+
+        let dep = POM_DEPENDENCIES[cluster.discovery.kind];
+
+        if (dep)
+            this.addDependency(deps, 'org.apache.ignite', dep.artifactId, igniteVersion);
+
+        if (cluster.discovery.kind === 'Jdbc') {
+            const store = cluster.discovery.Jdbc;
+
+            if (store.dataSourceBean && store.dialect)
+                this.storeFactoryDependency(storeDeps, cluster.discovery.Jdbc);
+        }
+
+        if (_.find(cluster.igfss, (igfs) => igfs.secondaryFileSystemEnabled))
+            this.addDependency(deps, 'org.apache.ignite', 'ignite-hadoop', igniteVersion);
+
+        if (_.find(caches, blobStoreFactory))
+            this.addDependency(deps, 'org.apache.ignite', 'ignite-hibernate', igniteVersion);
+
+        if (cluster.logger && cluster.logger.kind) {
+            dep = POM_DEPENDENCIES[cluster.logger.kind];
+
+            if (dep)
+                this.addDependency(deps, 'org.apache.ignite', dep.artifactId, igniteVersion);
+        }
+
+        this.dependencies(res, cluster, deps.concat(storeDeps));
+
+        res.needEmptyLine = true;
+
+        this.build(res, cluster, excludeGroupIds);
+
+        return res;
+    }
+}
+
+export default ['GeneratorPom', GeneratorPom];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/configuration/generator/Xml.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/configuration/generator/Xml.service.js b/modules/web-console/frontend/app/modules/configuration/generator/Xml.service.js
new file mode 100644
index 0000000..58d1ce0
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/configuration/generator/Xml.service.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+// TODO IGNITE-2052: need move $generatorXml to services.
+export default ['GeneratorXml', () => {
+    return $generatorXml;
+}];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/configuration/sidebar.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/configuration/sidebar.directive.js b/modules/web-console/frontend/app/modules/configuration/sidebar.directive.js
new file mode 100644
index 0000000..e51553b
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/configuration/sidebar.directive.js
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+export default ['igniteSidebar', ['igniteSidebar', (igniteSidebar) => {
+    function controller() {
+        const ctrl = this;
+
+        ctrl.items = igniteSidebar;
+    }
+
+    return {
+        restrict: 'A',
+        controller,
+        controllerAs: 'sidebar'
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/dialog/dialog-content.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/dialog/dialog-content.directive.js b/modules/web-console/frontend/app/modules/dialog/dialog-content.directive.js
new file mode 100644
index 0000000..98e9903
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/dialog/dialog-content.directive.js
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+export default ['igniteDialogContent', [() => {
+    const link = ($scope, $element, $attrs, igniteDialog) => {
+        igniteDialog.content = $element.html();
+
+        $element.hide();
+    };
+
+    return {
+        scope: {},
+        restrict: 'E',
+        link,
+        require: '^igniteDialog'
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/dialog/dialog-title.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/dialog/dialog-title.directive.js b/modules/web-console/frontend/app/modules/dialog/dialog-title.directive.js
new file mode 100644
index 0000000..ed4adb8
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/dialog/dialog-title.directive.js
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+export default ['igniteDialogTitle', [() => {
+    const link = ($scope, $element, $attrs, igniteDialog) => {
+        igniteDialog.title = $element.text();
+
+        $element.hide();
+    };
+
+    return {
+        scope: {},
+        restrict: 'E',
+        link,
+        require: '^igniteDialog'
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/dialog/dialog.controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/dialog/dialog.controller.js b/modules/web-console/frontend/app/modules/dialog/dialog.controller.js
new file mode 100644
index 0000000..05518d3
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/dialog/dialog.controller.js
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+export default ['$rootScope', '$scope', 'IgniteDialog', function($root, $scope, IgniteDialog) {
+    const ctrl = this;
+
+    const dialog = new IgniteDialog({
+        scope: $scope
+    });
+
+    ctrl.show = () => {
+        dialog.$promise.then(dialog.show);
+    };
+
+    $scope.$watch(() => ctrl.title, () => {
+        $scope.title = ctrl.title;
+    });
+
+    $scope.$watch(() => ctrl.content, () => {
+        $scope.content = ctrl.content;
+    });
+
+    $root.$on('$stateChangeStart', () => {
+        dialog.hide();
+    });
+}];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/dialog/dialog.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/dialog/dialog.directive.js b/modules/web-console/frontend/app/modules/dialog/dialog.directive.js
new file mode 100644
index 0000000..7aab10f
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/dialog/dialog.directive.js
@@ -0,0 +1,32 @@
+/*
+ * 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 controller from './dialog.controller';
+
+const template = '<a ng-click="ctrl.show()"><span ng-transclude=""></span></a>';
+
+export default ['igniteDialog', [() => {
+    return {
+        restrict: 'E',
+        template,
+        controller,
+        controllerAs: 'ctrl',
+        replace: true,
+        transclude: true,
+        require: '^igniteDialog'
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/dialog/dialog.factory.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/dialog/dialog.factory.js b/modules/web-console/frontend/app/modules/dialog/dialog.factory.js
new file mode 100644
index 0000000..e15891f
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/dialog/dialog.factory.js
@@ -0,0 +1,32 @@
+/*
+ * 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 templateUrl from './dialog.jade';
+
+export default ['IgniteDialog', ['$modal', ($modal) => {
+    const defaults = {
+        templateUrl,
+        placement: 'center',
+        show: false
+    };
+
+    return function(options) {
+        options = _.extend({}, defaults, options);
+
+        return $modal(options);
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/dialog/dialog.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/dialog/dialog.jade b/modules/web-console/frontend/app/modules/dialog/dialog.jade
new file mode 100644
index 0000000..0043709
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/dialog/dialog.jade
@@ -0,0 +1,26 @@
+//-
+    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(tabindex='-1' role='dialog')
+    .modal-dialog
+        .modal-content
+            .modal-header
+                button.close(ng-click='$hide()' aria-hidden='true') &times;
+                h4.modal-title {{title}}
+            .modal-body(ng-show='content')
+                p(ng-bind-html='content' style='text-align: left;')
+            .modal-footer
+                button.btn.btn-primary(id='confirm-btn-confirm' ng-click='$hide()') Ok

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/dialog/dialog.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/dialog/dialog.module.js b/modules/web-console/frontend/app/modules/dialog/dialog.module.js
new file mode 100644
index 0000000..c9ba9f9
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/dialog/dialog.module.js
@@ -0,0 +1,32 @@
+/*
+ * 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 igniteDialog from './dialog.directive';
+import igniteDialogTitle from './dialog-title.directive';
+import igniteDialogContent from './dialog-content.directive';
+import IgniteDialog from './dialog.factory';
+
+angular
+.module('ignite-console.dialog', [
+
+])
+.factory(...IgniteDialog)
+.directive(...igniteDialog)
+.directive(...igniteDialogTitle)
+.directive(...igniteDialogContent);

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/bs-select-placeholder.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/bs-select-placeholder.directive.js b/modules/web-console/frontend/app/modules/form/field/bs-select-placeholder.directive.js
new file mode 100644
index 0000000..83f438d
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/bs-select-placeholder.directive.js
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+// Override AngularStrap "bsSelect" in order to dynamically change placeholder and class.
+export default ['bsSelect', [() => {
+    const link = (scope, $element, attrs, [ngModel]) => {
+        if (!ngModel)
+            return;
+
+        const $render = ngModel.$render;
+
+        ngModel.$render = () => {
+            $render();
+
+            const value = ngModel.$viewValue;
+
+            if (_.isNil(value) || (attrs.multiple && !value.length)) {
+                $element.html(attrs.placeholder);
+
+                $element.addClass('placeholder');
+            }
+            else
+                $element.removeClass('placeholder');
+        };
+    };
+
+    return {
+        priority: 1,
+        restrict: 'A',
+        link,
+        require: ['?ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/down.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/down.directive.js b/modules/web-console/frontend/app/modules/form/field/down.directive.js
new file mode 100644
index 0000000..659933e
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/down.directive.js
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+export default ['igniteFormFieldDown', ['$tooltip', ($tooltip) => {
+    const controller = ['$element', function($element) {
+        $tooltip($element, { title: 'Move item down' });
+
+        this.down = () => {
+            const i = this.models.indexOf(this.model);
+
+            this.models.splice(i, 1);
+            this.models.splice(i + 1, 0, this.model);
+        };
+    }];
+
+    return {
+        restrict: 'A',
+        bindToController: {
+            model: '=ngModel',
+            models: '=models'
+        },
+        controller,
+        controllerAs: '$ctrl'
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/feedback.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/feedback.scss b/modules/web-console/frontend/app/modules/form/field/feedback.scss
new file mode 100644
index 0000000..08d0aef
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/feedback.scss
@@ -0,0 +1,37 @@
+/*
+ * 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 "../../../../public/stylesheets/variables";
+
+.form-field-feedback {
+    position: relative;
+    width: 0;
+    height: 28px;
+    float: right;
+    z-index: 2;
+
+    color: $brand-primary;
+    line-height: $input-height;
+    pointer-events: initial;
+    text-align: center;
+
+    &:before {
+        position: absolute;
+        right: 0;
+        width: 38px;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/field.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/field.scss b/modules/web-console/frontend/app/modules/form/field/field.scss
new file mode 100644
index 0000000..5717766
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/field.scss
@@ -0,0 +1,43 @@
+/*
+ * 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 "../../../../public/stylesheets/variables";
+
+.indexField {
+    float: left;
+    line-height: 28px;
+    margin-right: 5px;
+    color: $brand-primary;
+}
+
+.form-field-save {
+    position: relative;
+    width: 0;
+    height: 28px;
+    float: right;
+    z-index: 2;
+
+    line-height: $input-height;
+    pointer-events: initial;
+    text-align: center;
+
+    &:before {
+        position: absolute;
+        right: 0;
+        width: 38px;
+    }    
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/form-control-feedback.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/form-control-feedback.directive.js b/modules/web-console/frontend/app/modules/form/field/form-control-feedback.directive.js
new file mode 100644
index 0000000..797ba69
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/form-control-feedback.directive.js
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+export default ['formFieldFeedback', [() => {
+    const link = ($scope, $element, $attrs, [form]) => {
+        let name = $scope.name;
+
+        if (_.isNil(name))
+            name = $attrs.name;
+
+        const err = $attrs.igniteError;
+        const msg = $attrs.igniteErrorMessage;
+
+        if (name && err && msg) {
+            form.$errorMessages = form.$errorMessages || {};
+            form.$errorMessages[name] = form.$errorMessages[name] || {};
+            form.$errorMessages[name][err] = msg;
+        }
+    };
+
+    return {
+        restrict: 'C',
+        link,
+        require: ['^form']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/input/autofocus.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/input/autofocus.directive.js b/modules/web-console/frontend/app/modules/form/field/input/autofocus.directive.js
new file mode 100644
index 0000000..8ffc9a0
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/input/autofocus.directive.js
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+export default ['igniteFormFieldInputAutofocus', ['$timeout', ($timeout) => {
+    const link = (scope, el, attrs) => {
+        if (_.isUndefined(attrs.igniteFormFieldInputAutofocus) || attrs.igniteFormFieldInputAutofocus !== 'true')
+            return;
+
+        $timeout(() => el.focus(), 100);
+    };
+
+    return {
+        restrict: 'A',
+        link
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/input/select.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/input/select.scss b/modules/web-console/frontend/app/modules/form/field/input/select.scss
new file mode 100644
index 0000000..55bbd58
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/input/select.scss
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+.select.dropdown-menu.ng-leave {
+    transition: none !important; /* disable transitions */
+    animation: none 0s !important; /* disable keyframe animations */
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/input/text.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/input/text.scss b/modules/web-console/frontend/app/modules/form/field/input/text.scss
new file mode 100644
index 0000000..c76bebd
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/input/text.scss
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+.checkbox label .input-tip {
+	position: initial;
+}
+
+.input-tip .fa-floppy-o {
+	position: absolute;
+    top: 0;
+    right: 0;
+    z-index: 2;
+
+    width: 34px;
+    height: 34px;
+
+    text-align: center;
+
+    display: inline-block;
+    line-height: 28px;
+    pointer-events: initial;
+}
+
+.input-tip .form-control-feedback {
+    height: auto;
+}
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/label.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/label.directive.js b/modules/web-console/frontend/app/modules/form/field/label.directive.js
new file mode 100644
index 0000000..97ba598
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/label.directive.js
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+export default ['igniteFormFieldLabel', [() => {
+    return {
+        restrict: 'E',
+        compile() {
+            return {
+                post($scope, $element, $attrs, [form, field], $transclude) {
+                    $transclude($scope, function(clone) {
+                        const text = clone.text();
+
+                        if (/(.*):$/.test(text))
+                            field.name = /(.*):$/.exec(text)[1];
+
+                        const $label = $element.parent().parent().find('.group-legend > label, .ignite-field > label');
+
+                        if ($label[0] && $element[0].id) {
+                            const id = $element[0].id;
+
+                            $label[0].id = id.indexOf('+') >= 0 ? $scope.$eval(id) : id;
+                        }
+
+                        $label.append(clone);
+                    });
+                }
+            };
+        },
+        replace: true,
+        transclude: true,
+        require: ['^form', '?^igniteFormField']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/tooltip.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/tooltip.directive.js b/modules/web-console/frontend/app/modules/form/field/tooltip.directive.js
new file mode 100644
index 0000000..5005280
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/tooltip.directive.js
@@ -0,0 +1,49 @@
+/*
+ * 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 template = '<i class="tipField fa fa-question-circle"></i>';
+
+export default ['igniteFormFieldTooltip', ['$tooltip', ($tooltip) => {
+    const link = ($scope, $element, $attrs, [form, field], $transclude) => {
+        const content = Array.prototype.slice
+            .apply($transclude($scope))
+            .reduce((html, el) => html += el.outerHTML || el.textContent || el, '');
+
+        $tooltip($element, { title: content });
+
+        if (field)
+            $element.attr('id', field.for + 'Tooltip');
+
+        // TODO cleanup css styles.
+        if ($element.hasClass('tipLabel'))
+            $element.removeClass('tipField');
+
+        if ($element.parent('label').length)
+            $element.addClass('tipLabel').removeClass('tipField');
+    };
+
+    return {
+        priority: 1,
+        restrict: 'E',
+        scope: {},
+        template,
+        link,
+        replace: true,
+        transclude: true,
+        require: ['^form', '?^igniteFormField']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/field/up.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/field/up.directive.js b/modules/web-console/frontend/app/modules/form/field/up.directive.js
new file mode 100644
index 0000000..aba1cbe
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/field/up.directive.js
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+export default ['igniteFormFieldUp', ['$tooltip', ($tooltip) => {
+    const controller = ['$element', function($element) {
+        $tooltip($element, { title: 'Move item up' });
+
+        this.up = () => {
+            const idx = this.models.indexOf(this.model);
+
+            this.models.splice(idx, 1);
+            this.models.splice(idx - 1, 0, this.model);
+        };
+    }];
+
+    return {
+        restrict: 'A',
+        bindToController: {
+            model: '=ngModel',
+            models: '=models'
+        },
+        controller,
+        controllerAs: '$ctrl'
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/form.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/form.module.js b/modules/web-console/frontend/app/modules/form/form.module.js
new file mode 100644
index 0000000..23eafcd
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/form.module.js
@@ -0,0 +1,96 @@
+/*
+ * 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';
+
+// Fields styles.
+import './field/field.scss';
+import './field/feedback.scss';
+import './field/input/text.scss';
+import './field/input/select.scss';
+
+// Panel.
+import igniteFormPanel from './panel/panel.directive';
+import igniteFormPanelField from './panel/field.directive';
+import igniteFormPanelChevron from './panel/chevron.directive';
+import igniteFormRevert from './panel/revert.directive';
+
+// Field.
+import igniteFormFieldLabel from './field/label.directive';
+import igniteFormFieldTooltip from './field/tooltip.directive';
+import placeholder from './field/bs-select-placeholder.directive';
+
+// Group.
+import igniteFormGroupAdd from './group/add.directive';
+import igniteFormGroupTooltip from './group/tooltip.directive';
+
+// Validators.
+import ipaddress from './validator/ipaddress.directive';
+import javaKeywords from './validator/java-keywords.directive';
+import javaPackageSpecified from './validator/java-package-specified.directive';
+import javaBuiltInClass from './validator/java-built-in-class.directive';
+import javaIdentifier from './validator/java-identifier.directive';
+import javaPackageName from './validator/java-package-name.directive';
+import propertyValueSpecified from './validator/property-value-specified.directive';
+import propertyUnique from './validator/property-unique.directive';
+import unique from './validator/unique.directive';
+import uuid from './validator/uuid.directive';
+
+// Helpers.
+import igniteFormFieldInputAutofocus from './field/input/autofocus.directive';
+import igniteFormControlFeedback from './field/form-control-feedback.directive';
+import igniteFormFieldUp from './field/up.directive';
+import igniteFormFieldDown from './field/down.directive';
+
+angular
+.module('ignite-console.Form', [
+
+])
+// Panel.
+.directive(...igniteFormPanel)
+.directive(...igniteFormPanelField)
+.directive(...igniteFormPanelChevron)
+.directive(...igniteFormRevert)
+// Field.
+.directive(...igniteFormFieldLabel)
+.directive(...igniteFormFieldTooltip)
+.directive(...placeholder)
+// Group.
+.directive(...igniteFormGroupAdd)
+.directive(...igniteFormGroupTooltip)
+// Validators.
+.directive(...ipaddress)
+.directive(...javaKeywords)
+.directive(...javaPackageSpecified)
+.directive(...javaBuiltInClass)
+.directive(...javaIdentifier)
+.directive(...javaPackageName)
+.directive(...propertyValueSpecified)
+.directive(...propertyUnique)
+.directive(...unique)
+.directive(...uuid)
+// Helpers.
+.directive(...igniteFormFieldInputAutofocus)
+.directive(...igniteFormControlFeedback)
+.directive(...igniteFormFieldUp)
+.directive(...igniteFormFieldDown)
+// Generator of globally unique identifier.
+.factory('IgniteFormGUID', [() => {
+    let guid = 0;
+
+    return () => `form-field-${guid++}`;
+}]);

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/group/add.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/group/add.directive.js b/modules/web-console/frontend/app/modules/form/group/add.directive.js
new file mode 100644
index 0000000..7e9a50c
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/group/add.directive.js
@@ -0,0 +1,40 @@
+/*
+ * 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 template = '<i class="group-legend-btn fa fa-plus"></i>';
+
+export default ['igniteFormGroupAdd', ['$tooltip', ($tooltip) => {
+    const link = ($scope, $element, $attrs, $ctrls, $transclude) => {
+        const content = Array.prototype.slice
+            .apply($transclude($scope))
+            .reduce((html, el) => html += el.outerHTML || el.textContent || el, '');
+
+        $tooltip($element, { title: content });
+
+        $element.closest('.group').find('.group-legend').append($element);
+    };
+
+    return {
+        restrict: 'E',
+        scope: {},
+        template,
+        link,
+        replace: true,
+        transclude: true,
+        require: ['^form']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/group/tooltip.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/group/tooltip.directive.js b/modules/web-console/frontend/app/modules/form/group/tooltip.directive.js
new file mode 100644
index 0000000..3e470e1
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/group/tooltip.directive.js
@@ -0,0 +1,40 @@
+/*
+ * 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 template = '<i class="group-legend-btn fa fa-question-circle"></i>';
+
+export default ['igniteFormGroupTooltip', ['$tooltip', ($tooltip) => {
+    const link = ($scope, $element, $attrs, $ctrls, $transclude) => {
+        const content = Array.prototype.slice
+            .apply($transclude($scope))
+            .reduce((html, el) => html += el.outerHTML || el.textContent || el, '');
+
+        $tooltip($element, { title: content });
+
+        $element.closest('.group').find('.group-legend').append($element);
+    };
+
+    return {
+        restrict: 'E',
+        scope: {},
+        template,
+        link,
+        replace: true,
+        transclude: true,
+        require: ['^form']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/panel/chevron.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/panel/chevron.directive.js b/modules/web-console/frontend/app/modules/form/panel/chevron.directive.js
new file mode 100644
index 0000000..6af560b
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/panel/chevron.directive.js
@@ -0,0 +1,53 @@
+/*
+ * 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 template = `<i class='fa' ng-class='isOpen ? "fa-chevron-circle-down" : "fa-chevron-circle-right"'></i>`; // eslint-disable-line quotes
+
+export default ['igniteFormPanelChevron', [() => {
+    const controller = [() => {}];
+
+    const link = ($scope, $element, $attrs, [bsCollapseCtrl]) => {
+        const $target = $element.parent().parent().find('.panel-collapse');
+
+        bsCollapseCtrl.$viewChangeListeners.push(function() {
+            const index = bsCollapseCtrl.$targets.reduce((acc, el, i) => {
+                if (el[0] === $target[0])
+                    acc.push(i);
+
+                return acc;
+            }, [])[0];
+
+            $scope.isOpen = false;
+
+            const active = bsCollapseCtrl.$activeIndexes();
+
+            if ((active instanceof Array) && active.indexOf(index) !== -1 || active === index)
+                $scope.isOpen = true;
+        });
+    };
+
+    return {
+        restrict: 'E',
+        scope: {},
+        link,
+        template,
+        controller,
+        replace: true,
+        transclude: true,
+        require: ['^bsCollapse']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/panel/field.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/panel/field.directive.js b/modules/web-console/frontend/app/modules/form/panel/field.directive.js
new file mode 100644
index 0000000..5dc7b07
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/panel/field.directive.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.
+ */
+
+export default ['igniteFormPanelField', ['$parse', 'IgniteLegacyTable', ($parse, LegacyTable) => {
+    const link = (scope, element, attrs, [ngModelCtrl, formCtrl]) => {
+        formCtrl.$defaults = formCtrl.$defaults || {};
+
+        const { name, ngModel } = attrs;
+        const getter = () => $parse(ngModel)(scope);
+
+        const saveDefault = () => {
+            formCtrl.$defaults[name] = _.cloneDeep(getter());
+        };
+
+        const resetDefault = () => {
+            ngModelCtrl.$viewValue = formCtrl.$defaults[name];
+
+            ngModelCtrl.$valid = true;
+            ngModelCtrl.$invalid = false;
+            ngModelCtrl.$error = {};
+            ngModelCtrl.$render();
+        };
+
+        if (!(_.isNull(formCtrl.$defaults[name]) || _.isUndefined(formCtrl.$defaults[name])))
+            resetDefault();
+        else
+            saveDefault();
+
+        scope.tableReset = () => {
+            if (!LegacyTable.tableSaveAndReset())
+                LegacyTable.tableReset();
+        };
+
+        scope.$watch(() => formCtrl.$pristine, () => {
+            if (!formCtrl.$pristine)
+                return;
+
+            saveDefault();
+            resetDefault();
+        });
+
+        scope.$watch(() => ngModelCtrl.$modelValue, () => {
+            if (!formCtrl.$pristine)
+                return;
+
+            saveDefault();
+        });
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel', '^form']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/panel/panel.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/panel/panel.directive.js b/modules/web-console/frontend/app/modules/form/panel/panel.directive.js
new file mode 100644
index 0000000..b8e7c25
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/panel/panel.directive.js
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+export default ['form', [() => {
+    const link = (scope, $element, $attrs, [form]) => {
+        const $form = $element.parent().closest('form');
+
+        scope.$watch(() => {
+            return $form.hasClass('ng-pristine');
+        }, (value) => {
+            if (!value)
+                return;
+
+            form.$setPristine();
+        });
+    };
+
+    return {
+        restrict: 'E',
+        link,
+        require: ['^form']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/panel/revert.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/panel/revert.directive.js b/modules/web-console/frontend/app/modules/form/panel/revert.directive.js
new file mode 100644
index 0000000..2076b0d
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/panel/revert.directive.js
@@ -0,0 +1,54 @@
+/*
+ * 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 template = '<i ng-show="form.$dirty" class="fa fa-undo pull-right" ng-click="revert($event)"></i>';
+
+export default ['igniteFormRevert', ['$tooltip', 'IgniteLegacyTable', ($tooltip, LegacyTable) => {
+    const link = (scope, $element, $attrs, [form]) => {
+        $tooltip($element, { title: 'Undo unsaved changes' });
+
+        scope.form = form;
+
+        scope.revert = (e) => {
+            e.stopPropagation();
+
+            LegacyTable.tableReset();
+
+            _.forOwn(form.$defaults, (value, name) => {
+                const field = form[name];
+
+                if (field) {
+                    field.$viewValue = value;
+                    field.$setViewValue(value);
+                    field.$setPristine();
+                    field.$render();
+                }
+            });
+
+            form.$setPristine();
+        };
+    };
+
+    return {
+        restrict: 'E',
+        scope: { },
+        template,
+        link,
+        replace: true,
+        require: ['^form']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/ipaddress.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/ipaddress.directive.js b/modules/web-console/frontend/app/modules/form/validator/ipaddress.directive.js
new file mode 100644
index 0000000..77e63f6
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/ipaddress.directive.js
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+export default ['ipaddress', ['IgniteInetAddress', (InetAddress) => {
+    const onlyDigits = (str) => (/^\d+$/.test(str));
+
+    const strictParseInt = (str) => onlyDigits(str) ? parseInt(str, 10) : Number.NaN;
+
+    const parse = (commonIpAddress) => {
+        const [ipOrHost, portRange] = commonIpAddress.split(':');
+        const ports = _.isUndefined(portRange) ? [] : portRange.split('..').map(strictParseInt);
+
+        return {ipOrHost, ports};
+    };
+
+    const link = (scope, el, attrs, [ngModel]) => {
+        const isEmpty = (modelValue) => {
+            return ngModel.$isEmpty(modelValue) || _.isUndefined(attrs.ipaddress) || attrs.ipaddress !== 'true';
+        };
+
+        const portRange = !_.isNil(attrs.ipaddressWithPortRange);
+
+        if (attrs.ipaddressWithPort) {
+            ngModel.$validators.ipaddressPort = (modelValue) => {
+                if (isEmpty(modelValue) || modelValue.indexOf(':') === -1)
+                    return true;
+
+                if ((modelValue.match(/:/g) || []).length > 1)
+                    return false;
+
+                const {ports} = parse(modelValue);
+
+                if (ports.length !== 1)
+                    return portRange;
+
+                return InetAddress.validPort(ports[0]);
+            };
+        }
+
+        if (portRange) {
+            ngModel.$validators.ipaddressPortRange = (modelValue) => {
+                if (isEmpty(modelValue) || modelValue.indexOf('..') === -1)
+                    return true;
+
+                const {ports} = parse(modelValue);
+
+                if (ports.length !== 2)
+                    return false;
+
+                return InetAddress.validPort(ports[0]) && InetAddress.validPort(ports[1]) && ports[0] < ports[1];
+            };
+        }
+
+        ngModel.$validators.ipaddress = (modelValue) => {
+            if (isEmpty(modelValue))
+                return true;
+
+            const {ipOrHost, ports} = parse(modelValue);
+
+            if (attrs.ipaddressWithPort || attrs.ipaddressWithPortRange || ports.length === 0)
+                return InetAddress.validHost(ipOrHost);
+
+            return false;
+        };
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/java-built-in-class.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/java-built-in-class.directive.js b/modules/web-console/frontend/app/modules/form/validator/java-built-in-class.directive.js
new file mode 100644
index 0000000..1a4b504
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/java-built-in-class.directive.js
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+export default ['javaBuiltInClass', ['JavaTypes', (JavaTypes) => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        if (_.isUndefined(attrs.javaBuiltInClass) || !attrs.javaBuiltInClass)
+            return;
+
+        ngModel.$validators.javaBuiltInClass = (value) => JavaTypes.nonBuiltInClass(value);
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/java-identifier.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/java-identifier.directive.js b/modules/web-console/frontend/app/modules/form/validator/java-identifier.directive.js
new file mode 100644
index 0000000..5cbf7fb
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/java-identifier.directive.js
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+export default ['javaIdentifier', ['JavaTypes', (JavaTypes) => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        if (_.isUndefined(attrs.javaIdentifier) || !attrs.javaIdentifier)
+            return;
+
+        ngModel.$validators.javaIdentifier = (value) => JavaTypes.validIdentifier(value);
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/java-keywords.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/java-keywords.directive.js b/modules/web-console/frontend/app/modules/form/validator/java-keywords.directive.js
new file mode 100644
index 0000000..d97e59a
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/java-keywords.directive.js
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+export default ['javaKeywords', ['JavaTypes', (JavaTypes) => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        if (_.isUndefined(attrs.javaKeywords) || !attrs.javaKeywords)
+            return;
+
+        const packageOnly = attrs.javaPackageName === 'package-only';
+
+        ngModel.$validators.javaKeywords = (value) => {
+            if (value) {
+                if (!JavaTypes.validIdentifier(value) || (!packageOnly && !JavaTypes.packageSpecified(value)))
+                    return true;
+
+                return _.findIndex(value.split('.'), JavaTypes.isKeywords) < 0;
+            }
+
+            return true;
+        };
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/java-package-name.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/java-package-name.directive.js b/modules/web-console/frontend/app/modules/form/validator/java-package-name.directive.js
new file mode 100644
index 0000000..ac38179
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/java-package-name.directive.js
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+export default ['javaPackageName', ['JavaTypes', (JavaTypes) => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        if (_.isUndefined(attrs.javaPackageName) || !attrs.javaPackageName)
+            return;
+
+        ngModel.$validators.javaPackageName = (value) => JavaTypes.validPackage(value);
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/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
new file mode 100644
index 0000000..451d7ec
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/java-package-specified.directive.js
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+export default ['javaPackageSpecified', ['JavaTypes', (JavaTypes) => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        if (_.isUndefined(attrs.javaPackageSpecified))
+            return;
+
+        const allowBuiltIn = attrs.javaPackageSpecified === 'allow-built-in';
+
+        ngModel.$validators.javaPackageSpecified = (value) => !value || !JavaTypes.validIdentifier(value) || JavaTypes.packageSpecified(value) ||
+                (allowBuiltIn && !JavaTypes.nonBuiltInClass(value));
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/property-unique.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/property-unique.directive.js b/modules/web-console/frontend/app/modules/form/validator/property-unique.directive.js
new file mode 100644
index 0000000..8cfae89
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/property-unique.directive.js
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+export default ['ignitePropertyUnique', ['$parse', ($parse) => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        if (_.isUndefined(attrs.ignitePropertyUnique) || !attrs.ignitePropertyUnique)
+            return;
+
+        ngModel.$validators.ignitePropertyUnique = (value) => {
+            const arr = $parse(attrs.ignitePropertyUnique)(scope);
+
+            // Return true in case if array not exist, array empty.
+            if (!value || !arr || !arr.length)
+                return true;
+
+            const key = value.split('=')[0];
+            const idx = _.findIndex(arr, (item) => item.split('=')[0] === key);
+
+            // In case of new element check all items.
+            if (attrs.name === 'new')
+                return idx < 0;
+
+            // Check for $index in case of editing in-place.
+            return (_.isNumber(scope.$index) && (idx < 0 || scope.$index === idx));
+        };
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/property-value-specified.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/property-value-specified.directive.js b/modules/web-console/frontend/app/modules/form/validator/property-value-specified.directive.js
new file mode 100644
index 0000000..d113a4f
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/property-value-specified.directive.js
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+export default ['ignitePropertyValueSpecified', [() => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        if (_.isUndefined(attrs.ignitePropertyValueSpecified) || !attrs.ignitePropertyValueSpecified)
+            return;
+
+        ngModel.$validators.ignitePropertyValueSpecified = (value) => value ? value.indexOf('=') > 0 : true;
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/unique.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/unique.directive.js b/modules/web-console/frontend/app/modules/form/validator/unique.directive.js
new file mode 100644
index 0000000..0e6af18
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/unique.directive.js
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+export default ['igniteUnique', ['$parse', ($parse) => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        if (_.isUndefined(attrs.igniteUnique) || !attrs.igniteUnique)
+            return;
+
+        const isNew = _.startsWith(attrs.name, 'new');
+        const property = attrs.igniteUniqueProperty;
+
+        ngModel.$validators.igniteUnique = (value) => {
+            const arr = $parse(attrs.igniteUnique)(scope);
+
+            // Return true in case if array not exist, array empty.
+            if (!arr || !arr.length)
+                return true;
+
+            const idx = _.findIndex(arr, (item) => (property ? item[property] : item) === value);
+
+            // In case of new element check all items.
+            if (isNew)
+                return idx < 0;
+
+            // Check for $index in case of editing in-place.
+            return (_.isNumber(scope.$index) && (idx < 0 || scope.$index === idx));
+        };
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/form/validator/uuid.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/form/validator/uuid.directive.js b/modules/web-console/frontend/app/modules/form/validator/uuid.directive.js
new file mode 100644
index 0000000..0704175
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/form/validator/uuid.directive.js
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+export default ['uuid', ['JavaTypes', (JavaTypes) => {
+    const link = (scope, el, attrs, [ngModel]) => {
+        const isEmpty = (modelValue) => {
+            return ngModel.$isEmpty(modelValue) || _.isUndefined(attrs.uuid) || attrs.uuid !== 'true';
+        };
+
+        ngModel.$validators.uuid = (modelValue) => {
+            if (isEmpty(modelValue))
+                return true;
+
+            return JavaTypes.validUUID(modelValue);
+        };
+    };
+
+    return {
+        restrict: 'A',
+        link,
+        require: ['ngModel']
+    };
+}]];

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/frontend/app/modules/getting-started/GettingStarted.provider.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/getting-started/GettingStarted.provider.js b/modules/web-console/frontend/app/modules/getting-started/GettingStarted.provider.js
new file mode 100644
index 0000000..cf9f561
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/getting-started/GettingStarted.provider.js
@@ -0,0 +1,112 @@
+/*
+ * 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';
+
+// Getting started pages.
+import PAGES from 'app/data/getting-started.json';
+
+angular
+    .module('ignite-console.getting-started', [])
+    .provider('igniteGettingStarted', function() {
+        const items = PAGES;
+
+        this.push = (before, data) => {
+            const idx = _.findIndex(items, {title: before});
+
+            if (idx < 0)
+                items.push(data);
+            else
+                items.splice(idx, 0, data);
+        };
+
+        this.update = (before, data) => {
+            const idx = _.findIndex(items, {title: before});
+
+            if (idx >= 0)
+                items[idx] = data;
+        };
+
+        this.$get = [function() {
+            return items;
+        }];
+    })
+    .service('gettingStarted', ['$rootScope', '$modal', 'igniteGettingStarted', function($root, $modal, igniteGettingStarted) {
+        const _model = igniteGettingStarted;
+
+        let _page = 0;
+
+        const scope = $root.$new();
+
+        scope.ui = {
+            showGettingStarted: false
+        };
+
+        function _fillPage() {
+            scope.title = _model[_page].title;
+            scope.message = _model[_page].message.join(' ');
+        }
+
+        scope.isFirst = () => _page === 0;
+
+        scope.isLast = () => _page === _model.length - 1;
+
+        scope.next = () => {
+            _page += 1;
+
+            _fillPage();
+        };
+
+        scope.prev = () => {
+            _page -= 1;
+
+            _fillPage();
+        };
+
+        const dialog = $modal({templateUrl: '/templates/getting-started.html', scope, placement: 'center', show: false, backdrop: 'static'});
+
+        scope.close = () => {
+            try {
+                localStorage.showGettingStarted = scope.ui.showGettingStarted;
+            }
+            catch (ignore) {
+                // No-op.
+            }
+
+            dialog.hide();
+        };
+
+        return {
+            tryShow: (force) => {
+                try {
+                    scope.ui.showGettingStarted = _.isNil(localStorage.showGettingStarted)
+                        || localStorage.showGettingStarted === 'true';
+                }
+                catch (ignore) {
+                    // No-op.
+                }
+
+                if (force || scope.ui.showGettingStarted) {
+                    _page = 0;
+
+                    _fillPage();
+
+                    dialog.$promise.then(dialog.show);
+                }
+            }
+        };
+    }]);