# ignite-843 WIP

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

Branch: refs/heads/ignite-843
Commit: b226232b05f37b8efd47cf3f595fa8246bee2951
Parents: ec11f8f
Author: Andrey <anovi...@gridgain.com>
Authored: Fri May 22 14:04:35 2015 +0700
Committer: Andrey <anovi...@gridgain.com>
Committed: Fri May 22 14:04:35 2015 +0700

----------------------------------------------------------------------
 modules/webconfig/nodejs/app.js                 |  79 +++----
 modules/webconfig/nodejs/config/default.json    |   8 +
 modules/webconfig/nodejs/configuration.js       |   5 +
 modules/webconfig/nodejs/db.js                  |  25 ++-
 modules/webconfig/nodejs/package.json           |   6 +-
 .../public/javascripts/controllers/clusters.js  | 210 +++++++++----------
 .../public/javascripts/controllers/common.js    |  37 ++++
 .../public/javascripts/controllers/sidebar.js   |   6 -
 .../nodejs/public/stylesheets/style.css         |   2 +-
 .../nodejs/public/stylesheets/style.less        |  58 +++++
 modules/webconfig/nodejs/routes/auth.js         |  52 +++++
 modules/webconfig/nodejs/routes/clusters.js     |  10 +-
 modules/webconfig/nodejs/routes/pages.js        |  14 +-
 modules/webconfig/nodejs/settings.js            |   3 -
 modules/webconfig/nodejs/views/caches.jade      |   2 +-
 modules/webconfig/nodejs/views/clients.jade     |   2 +-
 modules/webconfig/nodejs/views/clusters.jade    |  17 +-
 .../webconfig/nodejs/views/includes/header.jade |   5 +-
 modules/webconfig/nodejs/views/index.jade       |  18 ++
 .../webconfig/nodejs/views/layout-sidebar.jade  |  22 ++
 modules/webconfig/nodejs/views/layout.jade      |  92 ++++----
 modules/webconfig/nodejs/views/login.jade       |  31 +++
 modules/webconfig/nodejs/views/persistence.jade |   2 +-
 modules/webconfig/nodejs/views/register.jade    |   8 +
 modules/webconfig/nodejs/views/sql.jade         |   2 +-
 25 files changed, 457 insertions(+), 259 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/app.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/app.js b/modules/webconfig/nodejs/app.js
index 344ec96..6e2117b 100644
--- a/modules/webconfig/nodejs/app.js
+++ b/modules/webconfig/nodejs/app.js
@@ -8,11 +8,12 @@ var session = require('express-session')
 
 var pageRoutes = require('./routes/pages');
 var clustersRouter = require('./routes/clusters');
+var authRouter = require('./routes/auth');
 
 var passport = require('passport');
 var LocalStrategy = require('passport-local').Strategy;
 
-var db = require('./db.js');
+var db = require('./db');
 
 var app = express();
 
@@ -25,15 +26,36 @@ app.use(favicon(__dirname + '/public/favicon.ico'));
 app.use(logger('dev'));
 app.use(bodyParser.json());
 app.use(bodyParser.urlencoded({extended: false}));
-app.use(cookieParser());
 app.use(require('less-middleware')(path.join(__dirname, 'public')));
 app.use(express.static(path.join(__dirname, 'public')));
+app.use(cookieParser());
+app.use(session({
+    secret: 'keyboard cat',
+    resave: false,
+    saveUninitialized: true,
+}));
+
+app.use(require('flash')());
 
 app.use(passport.initialize());
 app.use(passport.session());
 
+passport.serializeUser(db.Account.serializeUser());
+passport.deserializeUser(db.Account.deserializeUser());
+
+passport.use(db.Account.createStrategy());
+
+var mustAuthenticated = function (req, res, next) {
+    console.log('isAuthenticated = ' + req.isAuthenticated());
+
+    req.isAuthenticated() ? next() : res.redirect('/');
+};
+
+app.all('/clusters', mustAuthenticated);
+
 app.use('/', pageRoutes);
-app.use('/rest', clustersRouter);
+app.use('/rest/clusters', clustersRouter);
+app.use('/rest/auth', authRouter);
 
 // catch 404 and forward to error handler
 app.use(function (req, res, next) {
@@ -72,55 +94,4 @@ app.use(function (err, req, res, next) {
 //    next();
 //});
 
-app.use(session({
-    secret: 'keyboard cat',
-    resave: false,
-    saveUninitialized: true
-}));
-
-passport.serializeUser(function(user, done) {
-    done(null, user._id);
-});
-
-passport.deserializeUser(function(id, done) {
-    db.User.findById(id, function(err, user) {
-        done(err, user);
-    });
-});
-
-// passport/login.js
-passport.use('login', new LocalStrategy({
-        passReqToCallback : true
-    },
-    function(req, username, password, done) {
-        // check in mongo if a user with username exists or not
-        db.User.findOne({ 'username' :  username },
-            function(err, user) {
-                // In case of any error, return using the done method
-                if (err)
-                    return done(err);
-
-                // Username does not exist, log error & redirect back
-                if (!user) {
-                    console.log('User Not Found with username ' + username);
-
-                    return done(null, false,
-                        req.flash('message', 'User Not found.'));
-                }
-
-                // User exists but wrong password, log the error
-                if (!isValidPassword(user, password)) {
-                    console.log('Invalid Password');
-
-                    return done(null, false,
-                        req.flash('message', 'Invalid Password'));
-                }
-
-                // User and password both match, return user from done method 
which will be treated like success
-                return done(null, user);
-            }
-        );
-    })
-);
-
 module.exports = app;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/config/default.json
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/config/default.json 
b/modules/webconfig/nodejs/config/default.json
new file mode 100644
index 0000000..922fe9d
--- /dev/null
+++ b/modules/webconfig/nodejs/config/default.json
@@ -0,0 +1,8 @@
+{
+    "express": {
+        "port": 3000
+    },
+    "mongoDB": {
+        "url": "mongodb://localhost/web-configurator"
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/configuration.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/configuration.js 
b/modules/webconfig/nodejs/configuration.js
new file mode 100644
index 0000000..9482235
--- /dev/null
+++ b/modules/webconfig/nodejs/configuration.js
@@ -0,0 +1,5 @@
+var config = require('nconf');
+
+config.file({'file': 'config/default.json'});
+
+module.exports = config
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/db.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/db.js b/modules/webconfig/nodejs/db.js
index 59534f5..59808f7 100644
--- a/modules/webconfig/nodejs/db.js
+++ b/modules/webconfig/nodejs/db.js
@@ -1,29 +1,34 @@
-var settings = require('./settings.js');
+var config = require('./configuration.js');
 
 // mongoose for mongodb
-var mongoose = require('mongoose');
+var mongoose = require('mongoose'),
+    Schema = mongoose.Schema,
+    ObjectId = mongoose.Types.ObjectId,
+    passportLocalMongoose = require('passport-local-mongoose');
 
 // connect to mongoDB database on modulus.io
-mongoose.connect(settings.url, {server: {poolSize: 4}});
+mongoose.connect(config.get('mongoDB:url'), {server: {poolSize: 4}});
 
 // define user model.
-exports.User = mongoose.model('User', new mongoose.Schema({
-    username: String,
-    password: String,
+var AccountSchema = new Schema({
     email: String
-}));
+});
+
+AccountSchema.plugin(passportLocalMongoose);
+
+exports.Account = mongoose.model('Account', AccountSchema);
 
 // define cache model.
-exports.Cache =  mongoose.model('Cache', new mongoose.Schema({
+exports.Cache =  mongoose.model('Cache', new Schema({
     name: String,
     mode: { type: String, enum: ['PARTITIONED', 'REPLICATED', 'LOCAL'] },
     backups: Number
 }));
 
 // define cluster model.
-exports.Cluster =  mongoose.model('Cluster', new mongoose.Schema({
+exports.Cluster =  mongoose.model('Cluster', new Schema({
     name : String,
     caches : [String],
     discovery : { type: String, enum: ['TcpDiscoveryVmIpFinder', 
'TcpDiscoveryMulticastIpFinder'] },
     addresses : [String]
-}));
\ No newline at end of file
+}));

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/package.json
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/package.json 
b/modules/webconfig/nodejs/package.json
index 6c6e62d..a8e4c77 100644
--- a/modules/webconfig/nodejs/package.json
+++ b/modules/webconfig/nodejs/package.json
@@ -9,21 +9,21 @@
   "author": "GridGain",
   "license": "Apache License, Version 2.0",
   "dependencies": {
-    "admin-lte": "^2.1.0-alpha-1",
     "angularjs": "0.0.1",
     "body-parser": "~1.12.0",
-    "bootstrap": "^3.3.4",
     "cookie-parser": "~1.3.4",
     "debug": "~2.1.1",
     "express": "~4.12.2",
     "express-session": "^1.11.1",
+    "flash": "^1.1.0",
     "jade": "~1.9.2",
-    "jquery": "^2.1.4",
     "less-middleware": "1.0.x",
     "mongoose": "^4.0.2",
     "morgan": "~1.5.1",
+    "nconf": "^0.7.1",
     "passport": "^0.2.1",
     "passport-local": "^1.0.0",
+    "passport-local-mongoose": "^1.0.0",
     "serve-favicon": "~2.2.0"
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js
----------------------------------------------------------------------
diff --git 
a/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js 
b/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js
index ff144ae..0cfbe1b 100644
--- a/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js
+++ b/modules/webconfig/nodejs/public/javascripts/controllers/clusters.js
@@ -1,129 +1,121 @@
-angular.module('ignite-web-configurator', ['ngTable', 'mgcrea.ngStrap'])
-    .controller('activeLink', ['$scope', function($scope) {
-        $scope.isActive = function(path) {
-            return window.location.pathname.substr(0, path.length) == path;
-        };
-    }])
-    .controller('clustersController', ['$scope', '$modal', '$http', '$filter', 
'ngTableParams', function($scope, $modal, $http, $filter, ngTableParams) {
-        $scope.edit = { };
-
-        $scope.editRow = {};
-        $scope.editIdx = false;
-
-        $scope.discoveries = [
-            {value: 'TcpDiscoveryVmIpFinder', label: 'Static IPs'},
-            {value: 'TcpDiscoveryMulticastIpFinder', label: 'Multicast'}
-        ];
-
-        $scope.discoveryAsString = function(value) {
-            for (var i in $scope.discoveries) {
-                if ($scope.discoveries[i].value == value)
-                    return $scope.discoveries[i].label;
-            }
-
-            return 'Wrong discovery';
-        };
-
-        // when landing on the page, get all settings and show them
-        $http.get('/rest/cluster')
-            .success(function(data) {
-                $scope.clusters = data;
-
-                $scope.clustersTable = new ngTableParams({
-                    page: 1,                    // show first page
-                    count: Number.MAX_VALUE,        // count per page
-                    sorting: {
-                        name: 'asc'             // initial sorting
-                    }
-                }, {
-                    total: $scope.clusters.length, // length of data
-                    counts: [],
-                    getData: function($defer, params) {
-                        // use build-in angular filter
-                        var orderedData = params.sorting() ?
-                            $filter('orderBy')($scope.clusters, 
params.orderBy()) :
-                            $scope.clusters;
-
-                        $defer.resolve(orderedData.slice((params.page() - 1) * 
params.count(), params.page() * params.count()));
-                    }
-                });
-            })
-            .error(function(data) {
-                $scope.text = data;
+configuratorModule.controller('clustersController', ['$scope', '$modal', 
'$http', '$filter', 'ngTableParams', function($scope, $modal, $http, $filter, 
ngTableParams) {
+    $scope.edit = { };
+
+    $scope.editRow = {};
+    $scope.editIdx = false;
+
+    $scope.discoveries = [
+        {value: 'TcpDiscoveryVmIpFinder', label: 'Static IPs'},
+        {value: 'TcpDiscoveryMulticastIpFinder', label: 'Multicast'}
+    ];
+
+    $scope.discoveryAsString = function(value) {
+        for (var i in $scope.discoveries) {
+            if ($scope.discoveries[i].value == value)
+                return $scope.discoveries[i].label;
+        }
+
+        return 'Wrong discovery';
+    };
+
+    // when landing on the page, get all settings and show them
+    $http.get('/rest/clusters')
+        .success(function(data) {
+            $scope.clusters = data;
+
+            $scope.clustersTable = new ngTableParams({
+                page: 1,                    // show first page
+                count: Number.MAX_VALUE,        // count per page
+                sorting: {
+                    name: 'asc'             // initial sorting
+                }
+            }, {
+                total: $scope.clusters.length, // length of data
+                counts: [],
+                getData: function($defer, params) {
+                    // use build-in angular filter
+                    var orderedData = params.sorting() ?
+                        $filter('orderBy')($scope.clusters, params.orderBy()) :
+                        $scope.clusters;
+
+                    $defer.resolve(orderedData.slice((params.page() - 1) * 
params.count(), params.page() * params.count()));
+                }
             });
+        })
+        .error(function(data) {
+            $scope.text = data;
+        });
 
-        // Pre-fetch an external template populated with a custom scope
-        var myOtherModal = $modal({scope: $scope, template: '/cluster/edit', 
show: false});
+    // Pre-fetch an external template populated with a custom scope
+    var myOtherModal = $modal({scope: $scope, template: '/cluster/edit', show: 
false});
 
-        $scope.submit = function() {
-            if ($scope.editIdx !== false) {
-                var cluster = $scope.clusters[$scope.editIdx];
+    $scope.submit = function() {
+        if ($scope.editIdx !== false) {
+            var cluster = $scope.clusters[$scope.editIdx];
 
-                var data = {
-                    _id: cluster._id,
-                    name: cluster.name,
-                    caches: ['cache1', 'cache2', 'cache2'],
-                    discovery: cluster.discovery,
-                    addresses: ['127.0.0.1', '192.168.1.1']
-                };
+            var data = {
+                _id: cluster._id,
+                name: cluster.name,
+                discovery: cluster.discovery,
+                addresses: ['127.0.0.1', '192.168.1.1']
+            };
 
-                $scope.editIdx = false;
+            $scope.editIdx = false;
 
-                $http.post('/rest/cluster/save', data)
-                    .success(function (data) {
-                        myOtherModal.hide();
+            $http.post('/rest/clusters/save', data)
+                .success(function (data) {
+                    myOtherModal.hide();
 
-                        $scope.clusters = data;
+                    $scope.clusters = data;
 
-                        $scope.clustersTable.reload();
-                    })
-                    .error(function (data) {
-                        console.log('Error: ' + data);
-                    });
-            }
-        };
+                    $scope.clustersTable.reload();
+                })
+                .error(function (data) {
+                    console.log('Error: ' + data);
+                });
+        }
+    };
 
-        $scope.add = function () {
-            $scope.clusters.push({});
+    $scope.add = function () {
+        $scope.clusters.push({});
 
-            $scope.clustersTable.reload();
+        $scope.clustersTable.reload();
 
-            // Show when some event occurs (use $promise property to ensure 
the template has been loaded)
-            //myOtherModal.$promise.then(myOtherModal.show);
-        };
+        // Show when some event occurs (use $promise property to ensure the 
template has been loaded)
+        //myOtherModal.$promise.then(myOtherModal.show);
+    };
 
-        $scope.beginEdit = function (cluster) {
-            $scope.editIdx = $scope.clusters.indexOf(cluster);
+    $scope.beginEdit = function (cluster) {
+        $scope.editIdx = $scope.clusters.indexOf(cluster);
 
-            $scope.editRow = angular.copy(cluster);
+        $scope.editRow = angular.copy(cluster);
 
-            //// Show when some event occurs (use $promise property to ensure 
the template has been loaded)
-            //myOtherModal.$promise.then(myOtherModal.show);
-        };
+        //// Show when some event occurs (use $promise property to ensure the 
template has been loaded)
+        //myOtherModal.$promise.then(myOtherModal.show);
+    };
 
-        $scope.revert = function () {
-            if ($scope.editIdx !== false) {
-                $scope.clusters[$scope.editIdx] = $scope.editRow;
+    $scope.revert = function () {
+        if ($scope.editIdx !== false) {
+            $scope.clusters[$scope.editIdx] = $scope.editRow;
 
-                $scope.editIdx = false;
+            $scope.editIdx = false;
 
-                $scope.clustersTable.reload();
-            }
+            $scope.clustersTable.reload();
+        }
 
-            //// Show when some event occurs (use $promise property to ensure 
the template has been loaded)
-            //myOtherModal.$promise.then(myOtherModal.show);
-        };
+        //// Show when some event occurs (use $promise property to ensure the 
template has been loaded)
+        //myOtherModal.$promise.then(myOtherModal.show);
+    };
 
-        $scope.delete = function (cluster) {
-            $http.post('/rest/cluster/remove', { _id: cluster._id })
-                .success(function(data) {
-                    $scope.clusters = data;
+    $scope.delete = function (cluster) {
+        $http.post('/rest/clusters/remove', { _id: cluster._id })
+            .success(function(data) {
+                $scope.clusters = data;
 
-                    $scope.clustersTable.reload();
-                })
-                .error(function(data) {
-                    console.log('Error: ' + data);
-                });
-        };
-    }])
-;
\ No newline at end of file
+                $scope.clustersTable.reload();
+            })
+            .error(function(data) {
+                console.log('Error: ' + data);
+            });
+    };
+}]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/public/javascripts/controllers/common.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/public/javascripts/controllers/common.js 
b/modules/webconfig/nodejs/public/javascripts/controllers/common.js
new file mode 100644
index 0000000..b1a0eaa
--- /dev/null
+++ b/modules/webconfig/nodejs/public/javascripts/controllers/common.js
@@ -0,0 +1,37 @@
+var configuratorModule =  angular.module('ignite-web-configurator', 
['ngTable', 'mgcrea.ngStrap']);
+
+configuratorModule.controller('activeLink', ['$scope', function($scope) {
+    $scope.isActive = function(path) {
+        return window.location.pathname.substr(0, path.length) == path;
+    };
+}]);
+
+configuratorModule.controller('auth', ['$scope', '$modal', '$http', 
function($scope, $modal, $http) {
+    $scope.action = 'login';
+
+    $scope.message = '';
+
+    $scope.valid = false;
+
+    // Pre-fetch an external template populated with a custom scope
+    var authModal = $modal({scope: $scope, template: '/login', show: false});
+
+    $scope.login = function() {
+        // Show when some event occurs (use $promise property to ensure the 
template has been loaded)
+        authModal.$promise.then(authModal.show);
+    };
+
+    $scope.auth = function(action, user_info) {
+        if (action == 'signup') {
+            $http.post('/rest/auth/register', user_info)
+                .error(function (data) {
+                    $scope.message = data;
+                });
+        }
+
+        $http.post('/rest/auth/login', user_info)
+            .error(function (data) {
+                $scope.message = data;
+            });
+    };
+}]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/public/javascripts/controllers/sidebar.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/public/javascripts/controllers/sidebar.js 
b/modules/webconfig/nodejs/public/javascripts/controllers/sidebar.js
deleted file mode 100644
index c05f393..0000000
--- a/modules/webconfig/nodejs/public/javascripts/controllers/sidebar.js
+++ /dev/null
@@ -1,6 +0,0 @@
-angular.module('ignite-web-configurator', [])
-    .controller('activeLink', ['$scope', function($scope) {
-        $scope.isActive = function(path) {
-            return window.location.pathname.substr(0, path.length) == path;
-        };
-    }]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/public/stylesheets/style.css
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/public/stylesheets/style.css 
b/modules/webconfig/nodejs/public/stylesheets/style.css
index 3c2a159..073adf6 100644
--- a/modules/webconfig/nodejs/public/stylesheets/style.css
+++ b/modules/webconfig/nodejs/public/stylesheets/style.css
@@ -1 +1 @@
-.main-header .logo{height:auto}.main-sidebar{padding-top:60px}.navbar-default 
.navbar-brand,.navbar-default 
.navbar-brand:hover{position:absolute;width:100%;left:0;text-align:center}.modal-backdrop.am-fade{opacity:.5;transition:opacity
 .15s 
linear}.modal-backdrop.am-fade.ng-enter{opacity:0}.modal-backdrop.am-fade.ng-enter.ng-enter-active{opacity:.5}.modal-backdrop.am-fade.ng-leave{opacity:.5}.modal-backdrop.am-fade.ng-leave.ng-leave-active{opacity:0}.modal.center
 
.modal-dialog{position:fixed;top:40%;left:50%;min-width:320px;max-width:630px;width:50%;-webkit-transform:translateX(-50%)
 translateY(-50%);transform:translateX(-50%) translateY(-50%)}.ng-table 
th.text-right{text-align:right}.ng-table th.text-left{text-align:left}.ng-table 
th.text-center{text-align:center}.fa.fa-remove{color:red}.border-left{-webkit-box-shadow:1px
 0 0px 0px #eee inset;box-shadow:1px 0 0px 0px #eee 
inset}.border-right{-webkit-box-shadow:1px 0 0px 0px #eee;box-shadow:1px 0 0px 
0px #eee}.theme-line{background-
 color:#f9f9f9}.theme-line header{background-color:#fff}.theme-line header 
.search-bar{width:90%;margin:30px auto 
0;-webkit-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 0 0 5px 
rgba(0,0,0,0.1),0 0 0 1px rgba(0,0,0,0.1);box-shadow:0 0 0 5px 
rgba(0,0,0,0.1),0 0 0 1px rgba(0,0,0,0.1);position:relative}.theme-line header 
.search-bar.focus{-webkit-box-shadow:0 0 0 5px rgba(0,0,0,0.2),0 0 0 1px 
rgba(0,0,0,0.1);box-shadow:0 0 0 5px rgba(0,0,0,0.2),0 0 0 1px 
rgba(0,0,0,0.1)}.theme-line header .search-bar 
.fa{position:absolute;top:10px;left:14px;font-size:21px;color:#ccc;z-index:10}.theme-line
 header .search-bar .twitter-typeahead{width:100%}.theme-line header 
.search-bar 
input{-webkit-border-radius:5px;border-radius:5px;height:100%;border:0 
none;-webkit-box-shadow:0 2px 2px rgba(0,0,0,0.1) inset;box-shadow:0 2px 2px 
rgba(0,0,0,0.1) inset;color:#444;width:100%;padding:13px 13px 13px 
50px;font-size:14px}.theme-line header .search-bar 
input.tt-hint{color:#bbb}.theme-line header .sea
 rch-bar input:active .theme-line header .search-bar input:focus{outline:0 
none;-webkit-box-shadow:0 2px 2px rgba(0,0,0,0.2) inset;box-shadow:0 2px 2px 
rgba(0,0,0,0.2) inset}.theme-line header .search-bar 
.tt-dropdown-menu,.theme-solid header .search-bar 
.tt-dropdown-menu{width:100%;text-align:left}.theme-line header .search-bar 
.tt-dropdown-menu h3{padding:0 
45px;color:#ccc;font-weight:bold;font-size:12px;margin:10px 0 
4px;text-transform:uppercase}.theme-line header .search-bar .tt-dropdown-menu 
.tt-suggestions{display:block}.theme-line header .search-bar .tt-dropdown-menu 
.tt-suggestions .tt-suggestion{cursor:pointer;font-size:14px;padding:4px 
45px}.theme-line header .search-bar .tt-dropdown-menu .tt-suggestions 
.tt-suggestion 
p{color:#333;white-space:nowrap;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis}.theme-line
 header .search-bar .tt-cursor{background-color:#eee}.theme-line header 
.search-bar .tt-cursor p{color:#fff}.theme-line header a.btn{border:0 none;padd
 ing:10px 25px;background-color:rgba(0,0,0,0.15)}.theme-line header 
a.btn:hover{background-color:rgba(0,0,0,0.25)}.theme-line header 
a.btn.btn-link{background:transparent;color:rgba(255,255,255,0.8)}.theme-line 
header a.btn.btn-link:hover{color:#fff;text-decoration:none}.theme-line 
.navbar-nav a{background-color:transparent}.theme-line .navbar-nav 
a:hover,.theme-line .navbar-nav a:active,.theme-line .navbar-nav 
a:focus{background-color:transparent}.theme-line .navbar-nav .active 
a{font-weight:bold}.theme-line .main-links{padding-top:50px}.theme-line 
.main-links h3{margin-top:0;font-size:17px}.theme-line .main-links .links 
a{color:#888}.theme-line .main-links .links 
a:hover{text-decoration:none}.theme-line #category-columns,.theme-solid 
#category-columns{margin:50px 30px 0}.theme-line #category-columns 
h4{text-transform:uppercase;font-weight:300;color:#999;font-size:14px}.theme-line
 #category-columns ul{list-style:none;padding:0;margin-bottom:15px}.theme-line 
#category-columns ul li a
 {padding:4px 0;display:block;font-size:16px}.theme-line #category-columns ul 
.view-all{font-size:0.85em}.theme-line 
.docs-header{color:#999;overflow:hidden}.theme-line .docs-header 
h1{color:#444;margin-top:0;font-size:25px}.theme-line .btn-primary{border:0 
none}.theme-line .main-content .nav-horizontal a{-webkit-box-shadow:0 
0;box-shadow:0 0;border:0 
none;background-color:#fff;-webkit-border-radius:0;border-radius:0;color:#aaa;padding:6px;margin:0
 14px}.theme-line .main-content .nav-horizontal 
a:hover{color:#999;border-bottom:4px solid #ddd}.theme-line .main-content 
.nav-horizontal a.active{border-bottom:4px solid #888}.theme-line 
.sidebar-nav{color:#474a54;padding-bottom:30px}.theme-line .sidebar-nav 
ul{padding:0;list-style:none;font-size:13px;margin:3px 0 0}.theme-line 
.sidebar-nav ul li a{padding:3px 
0;display:block;color:#666;position:relative;white-space:nowrap;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis}.theme-line
 .sidebar-nav ul li a:before{top:0;content
 :" 
";display:block;width:6px;height:100%;position:absolute;left:-30px}.theme-line 
.sidebar-nav ul li a:hover{text-decoration:none}.theme-line .sidebar-nav ul li 
a.active{font-weight:bold}.theme-line .sidebar-nav ul li 
.subcategory{padding-left:15px}.theme-line .sidebar-nav 
h4{margin-top:2em;font-weight:normal;text-transform:uppercase;font-size:11px;margin-bottom:10px;color:#bbb}.theme-line
 .sidebar-nav h4:first-child{margin-top:0em}.theme-line .sidebar-nav 
.ask{width:100%;text-align:center;padding:10px}.theme-line .border-left 
.sidebar-nav{padding-left:15px}.theme-line 
.suggest{padding:4px;display:inline-block;font-size:12px}.header{padding:15px}.header
 .has-github{padding-right:136px}.header 
h1.navbar-brand{height:40px;width:200px;padding:0;margin:0;margin-top:5px;margin-right:15px}.header
 h1.navbar-brand a{text-indent:-99999px;background-position:center 
center;display:block;width:100%;height:100%;-webkit-background-size:contain;-moz-background-size:contain;background-size:contain;
 background-repeat:no-repeat}.header 
.nav.navbar-nav.pull-right{position:relative;right:-30px}.header 
.nav.navbar-nav .not-link{padding:15px;display:inline-block}.header 
.nav.navbar-nav .stable,.header .nav.navbar-nav .beta,.header .nav.navbar-nav 
.private{font-size:9px;padding:3px 
5px;display:inline-block;line-height:8px;-webkit-border-radius:3px;border-radius:3px;margin-left:6px;color:#fff;top:-2px;position:relative;opacity:0.6;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";filter:alpha(opacity=60)}.header
 .nav.navbar-nav a:hover>.stable,.header .nav.navbar-nav a:hover>.beta,.header 
.nav.navbar-nav a:hover>.private{opacity:1;-ms-filter:none;filter:none}.header 
.nav.navbar-nav .beta{background-color:#59c3d1}.header .nav.navbar-nav 
.stable{background-color:#41b841}.header .nav.navbar-nav 
.private{background-color:#333}.header #jumbotron{margin:55px 70px 
50px;text-align:center}.header #jumbotron 
h2{margin-top:0;margin-bottom:20px}.header #jumbotron p{margin-bottom:0;
 line-height:1.6em;font-size:16px}.header #jumbotron 
.btn{margin-top:20px}.header 
.searchbox{position:relative;margin-right:15px;top:9px}.header .searchbox 
.fa-search{position:absolute;top:8px;right:10px;color:#777;pointer-events:none}.header
 .searchbox .typeahead{line-height:1.25em;-webkit-transition:.3s 
ease-out;-moz-transition:.3s ease-out;-o-transition:.3s 
ease-out;-ms-transition:.3s ease-out;transition:.3s 
ease-out;background-color:rgba(0,0,0,0.05);-webkit-border-radius:5px;border-radius:5px;width:95px}.header
 .searchbox .typeahead:focus,.header .searchbox .typeahead:active{outline:0 
none}.header .searchbox 
.tt-dropdown-menu{max-width:350px;margin-left:-100px}.header .searchbox 
.tt-dropdown-menu h3{width:100px;float:left;margin:0;padding:8px 0 6px 
15px;font-size:13px;color:#bbb}.header .searchbox .tt-dropdown-menu 
.tt-suggestions{display:block;float:left;width:250px}.header .searchbox 
.tt-dropdown-menu .tt-suggestions .tt-suggestion{font-size:14px}.header 
.searchbox .tt-dropdown
 -menu .tt-suggestions .tt-suggestion 
p{white-space:nowrap;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis}.header
 .searchbox .tt-cursor{background-color:#eee}.header .searchbox .tt-cursor 
p{color:#fff}.header .searchbox input{border:0 
none;display:inline-block;font-size:14px;padding:6px 32px 6px 
12px;margin:0}.header .searchbox input.tt-hint{height:auto}.header 
.searchbox.focus input{width:250px}.theme-line header{border-bottom:8px 
solid}.theme-line header h2{color:#aaa}.theme-line header 
p{color:#666}.theme-line .navbar-nav{color:#888}.theme-line .navbar-nav 
a{color:#bbb}.theme-line.lumosity-light .searchbox .tt-cursor 
p{color:rgba(0,0,0,0.7)}.theme-line 
header{border-bottom-color:#ec1c24}.theme-line header 
a.btn{background-color:#ec1c24}.theme-line header 
a.btn:hover{background-color:#950d12}.theme-line header .navbar-nav 
a:hover,.theme-line header .navbar-nav .open>a{color:#ec1c24}.theme-line header 
.navbar-nav .tt-cursor{background-color:#ec1c24}.theme-line .nav
 bar-nav .active a{font-weight:bold;color:#ec1c24}.theme-line .navbar-nav 
.active a:hover{color:#950d12}.theme-line .main-links .links 
a:hover{color:#ec1c24}.theme-line .main-content a{color:#ec1c24}.theme-line 
.main-content a:hover{color:#950d12}.theme-line .sidebar-nav ul li 
a.active:before{background-color:#ec1c24}.theme-line .sidebar-nav ul li 
a.active{color:#ec1c24}.theme-line .sidebar-nav ul li a:hover,.theme-line 
.sidebar-nav ul li a.active:hover{color:#950d12}.theme-line 
.btn-primary{background-color:#ec1c24}.theme-line 
.btn-primary:hover{background-color:#950d12}.theme-line .main-content 
.nav-horizontal a.active{border-color:#ec1c24;color:#ec1c24}.theme-line 
.main-content .nav-horizontal a:hover{color:#950d12}.theme-line .main-content 
.nav-horizontal a.active:hover{border-color:#950d12}.theme-line header 
.navbar-nav a.active,.theme-line #versions-list li a:hover strong,.theme-line 
#versions-list li a.active .current,.theme-line #versions-list li a:active 
.current{color:#ec1c
 24}.theme-line.body-threes .section-right .threes-nav 
.btn-default:hover,.theme-line.page-docs.body-threes .section-right .threes-nav 
.pull-right a:hover{color:#ec1c24;border-color:#ec1c24}.body-overlap 
.main-content{margin-top:30px}.body-box .main-content,.body-overlap 
.main-content{padding:30px;-webkit-box-shadow:0 0 0 1px 
rgba(0,0,0,0.1);box-shadow:0 0 0 1px 
rgba(0,0,0,0.1);background-color:#fff}body{font-weight:400;font-family:Roboto 
Slab, serif}h1,h2,h3,h4,h5,h6{font-weight:700;font-family:Roboto Slab, 
serif}.submit-vote.submit-vote-parent.voted 
a.submit-vote-button,.submit-vote.submit-vote-parent 
a.submit-vote-button:hover{background-color:#ec1c24}div.submit-vote.submit-vote-parent.voted
 a.submit-vote-button:hover{background-color:#950d12}a,.link 
.title{color:#ec1c24}a:hover,.link:hover .title{color:#950d12}.header 
h1.navbar-brand 
a{background-image:url(https://www.filepicker.io/api/file/QagunjDGRFul2JgNCAli)}.header
 h1.navbar-brand{width:96px}.block-edit-parameters{text-align
 
:right;padding-bottom:5px}.ng-table-pager{display:none}.container-footer{margin-top:20px}.vcenter{vertical-align:middle}
\ No newline at end of file
+.main-header .logo{height:auto}.main-sidebar{padding-top:60px}.navbar-default 
.navbar-brand,.navbar-default 
.navbar-brand:hover{position:absolute;width:100%;left:0;text-align:center}.modal-backdrop.am-fade{opacity:.5;transition:opacity
 .15s 
linear}.modal-backdrop.am-fade.ng-enter{opacity:0}.modal-backdrop.am-fade.ng-enter.ng-enter-active{opacity:.5}.modal-backdrop.am-fade.ng-leave{opacity:.5}.modal-backdrop.am-fade.ng-leave.ng-leave-active{opacity:0}.modal.center
 
.modal-dialog{position:fixed;top:40%;left:50%;min-width:320px;max-width:630px;width:50%;-webkit-transform:translateX(-50%)
 translateY(-50%);transform:translateX(-50%) translateY(-50%)}.ng-table 
th.text-right{text-align:right}.ng-table th.text-left{text-align:left}.ng-table 
th.text-center{text-align:center}.fa.fa-remove{color:red}.border-left{-webkit-box-shadow:1px
 0 0px 0px #eee inset;box-shadow:1px 0 0px 0px #eee 
inset}.border-right{-webkit-box-shadow:1px 0 0px 0px #eee;box-shadow:1px 0 0px 
0px #eee}.theme-line{background-
 color:#f9f9f9}.theme-line header{background-color:#fff}.theme-line header 
.search-bar{width:90%;margin:30px auto 
0;-webkit-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 0 0 5px 
rgba(0,0,0,0.1),0 0 0 1px rgba(0,0,0,0.1);box-shadow:0 0 0 5px 
rgba(0,0,0,0.1),0 0 0 1px rgba(0,0,0,0.1);position:relative}.theme-line header 
.search-bar.focus{-webkit-box-shadow:0 0 0 5px rgba(0,0,0,0.2),0 0 0 1px 
rgba(0,0,0,0.1);box-shadow:0 0 0 5px rgba(0,0,0,0.2),0 0 0 1px 
rgba(0,0,0,0.1)}.theme-line header .search-bar 
.fa{position:absolute;top:10px;left:14px;font-size:21px;color:#ccc;z-index:10}.theme-line
 header .search-bar .twitter-typeahead{width:100%}.theme-line header 
.search-bar 
input{-webkit-border-radius:5px;border-radius:5px;height:100%;border:0 
none;-webkit-box-shadow:0 2px 2px rgba(0,0,0,0.1) inset;box-shadow:0 2px 2px 
rgba(0,0,0,0.1) inset;color:#444;width:100%;padding:13px 13px 13px 
50px;font-size:14px}.theme-line header .search-bar 
input.tt-hint{color:#bbb}.theme-line header .sea
 rch-bar input:active .theme-line header .search-bar input:focus{outline:0 
none;-webkit-box-shadow:0 2px 2px rgba(0,0,0,0.2) inset;box-shadow:0 2px 2px 
rgba(0,0,0,0.2) inset}.theme-line header .search-bar 
.tt-dropdown-menu,.theme-solid header .search-bar 
.tt-dropdown-menu{width:100%;text-align:left}.theme-line header .search-bar 
.tt-dropdown-menu h3{padding:0 
45px;color:#ccc;font-weight:bold;font-size:12px;margin:10px 0 
4px;text-transform:uppercase}.theme-line header .search-bar .tt-dropdown-menu 
.tt-suggestions{display:block}.theme-line header .search-bar .tt-dropdown-menu 
.tt-suggestions .tt-suggestion{cursor:pointer;font-size:14px;padding:4px 
45px}.theme-line header .search-bar .tt-dropdown-menu .tt-suggestions 
.tt-suggestion 
p{color:#333;white-space:nowrap;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis}.theme-line
 header .search-bar .tt-cursor{background-color:#eee}.theme-line header 
.search-bar .tt-cursor p{color:#fff}.theme-line header a.btn{border:0 none;padd
 ing:10px 25px;background-color:rgba(0,0,0,0.15)}.theme-line header 
a.btn:hover{background-color:rgba(0,0,0,0.25)}.theme-line header 
a.btn.btn-link{background:transparent;color:rgba(255,255,255,0.8)}.theme-line 
header a.btn.btn-link:hover{color:#fff;text-decoration:none}.theme-line 
.navbar-nav a{background-color:transparent}.theme-line .navbar-nav 
a:hover,.theme-line .navbar-nav a:active,.theme-line .navbar-nav 
a:focus{background-color:transparent}.theme-line .navbar-nav .active 
a{font-weight:bold}.theme-line .main-links{padding-top:50px}.theme-line 
.main-links h3{margin-top:0;font-size:17px}.theme-line .main-links .links 
a{color:#888}.theme-line .main-links .links 
a:hover{text-decoration:none}.theme-line #category-columns,.theme-solid 
#category-columns{margin:50px 30px 0}.theme-line #category-columns 
h4{text-transform:uppercase;font-weight:300;color:#999;font-size:14px}.theme-line
 #category-columns ul{list-style:none;padding:0;margin-bottom:15px}.theme-line 
#category-columns ul li a
 {padding:4px 0;display:block;font-size:16px}.theme-line #category-columns ul 
.view-all{font-size:0.85em}.theme-line 
.docs-header{color:#999;overflow:hidden}.theme-line .docs-header 
h1{color:#444;margin-top:0;font-size:25px}.theme-line .btn-primary{border:0 
none}.theme-line .main-content .nav-horizontal a{-webkit-box-shadow:0 
0;box-shadow:0 0;border:0 
none;background-color:#fff;-webkit-border-radius:0;border-radius:0;color:#aaa;padding:6px;margin:0
 14px}.theme-line .main-content .nav-horizontal 
a:hover{color:#999;border-bottom:4px solid #ddd}.theme-line .main-content 
.nav-horizontal a.active{border-bottom:4px solid #888}.theme-line 
.sidebar-nav{color:#474a54;padding-bottom:30px}.theme-line .sidebar-nav 
ul{padding:0;list-style:none;font-size:13px;margin:3px 0 0}.theme-line 
.sidebar-nav ul li a{padding:3px 
0;display:block;color:#666;position:relative;white-space:nowrap;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis}.theme-line
 .sidebar-nav ul li a:before{top:0;content
 :" 
";display:block;width:6px;height:100%;position:absolute;left:-30px}.theme-line 
.sidebar-nav ul li a:hover{text-decoration:none}.theme-line .sidebar-nav ul li 
a.active{font-weight:bold}.theme-line .sidebar-nav ul li 
.subcategory{padding-left:15px}.theme-line .sidebar-nav 
h4{margin-top:2em;font-weight:normal;text-transform:uppercase;font-size:11px;margin-bottom:10px;color:#bbb}.theme-line
 .sidebar-nav h4:first-child{margin-top:0em}.theme-line .sidebar-nav 
.ask{width:100%;text-align:center;padding:10px}.theme-line .border-left 
.sidebar-nav{padding-left:15px}.theme-line 
.suggest{padding:4px;display:inline-block;font-size:12px}.header{padding:15px}.header
 .has-github{padding-right:136px}.header 
h1.navbar-brand{height:40px;width:200px;padding:0;margin:0;margin-top:5px;margin-right:15px}.header
 h1.navbar-brand a{text-indent:-99999px;background-position:center 
center;display:block;width:100%;height:100%;-webkit-background-size:contain;-moz-background-size:contain;background-size:contain;
 background-repeat:no-repeat}.header 
.nav.navbar-nav.pull-right{position:relative;right:-30px}.header 
.nav.navbar-nav .not-link{padding:15px;display:inline-block}.header 
.nav.navbar-nav .stable,.header .nav.navbar-nav .beta,.header .nav.navbar-nav 
.private{font-size:9px;padding:3px 
5px;display:inline-block;line-height:8px;-webkit-border-radius:3px;border-radius:3px;margin-left:6px;color:#fff;top:-2px;position:relative;opacity:0.6;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";filter:alpha(opacity=60)}.header
 .nav.navbar-nav a:hover>.stable,.header .nav.navbar-nav a:hover>.beta,.header 
.nav.navbar-nav a:hover>.private{opacity:1;-ms-filter:none;filter:none}.header 
.nav.navbar-nav .beta{background-color:#59c3d1}.header .nav.navbar-nav 
.stable{background-color:#41b841}.header .nav.navbar-nav 
.private{background-color:#333}.header #jumbotron{margin:55px 70px 
50px;text-align:center}.header #jumbotron 
h2{margin-top:0;margin-bottom:20px}.header #jumbotron p{margin-bottom:0;
 line-height:1.6em;font-size:16px}.header #jumbotron 
.btn{margin-top:20px}.header 
.searchbox{position:relative;margin-right:15px;top:9px}.header .searchbox 
.fa-search{position:absolute;top:8px;right:10px;color:#777;pointer-events:none}.header
 .searchbox .typeahead{line-height:1.25em;-webkit-transition:.3s 
ease-out;-moz-transition:.3s ease-out;-o-transition:.3s 
ease-out;-ms-transition:.3s ease-out;transition:.3s 
ease-out;background-color:rgba(0,0,0,0.05);-webkit-border-radius:5px;border-radius:5px;width:95px}.header
 .searchbox .typeahead:focus,.header .searchbox .typeahead:active{outline:0 
none}.header .searchbox 
.tt-dropdown-menu{max-width:350px;margin-left:-100px}.header .searchbox 
.tt-dropdown-menu h3{width:100px;float:left;margin:0;padding:8px 0 6px 
15px;font-size:13px;color:#bbb}.header .searchbox .tt-dropdown-menu 
.tt-suggestions{display:block;float:left;width:250px}.header .searchbox 
.tt-dropdown-menu .tt-suggestions .tt-suggestion{font-size:14px}.header 
.searchbox .tt-dropdown
 -menu .tt-suggestions .tt-suggestion 
p{white-space:nowrap;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis}.header
 .searchbox .tt-cursor{background-color:#eee}.header .searchbox .tt-cursor 
p{color:#fff}.header .searchbox input{border:0 
none;display:inline-block;font-size:14px;padding:6px 32px 6px 
12px;margin:0}.header .searchbox input.tt-hint{height:auto}.header 
.searchbox.focus input{width:250px}.theme-line header{border-bottom:8px 
solid}.theme-line header h2{color:#aaa}.theme-line header 
p{color:#666}.theme-line .navbar-nav{color:#888}.theme-line .navbar-nav 
a{color:#bbb}.theme-line.lumosity-light .searchbox .tt-cursor 
p{color:rgba(0,0,0,0.7)}.theme-line 
header{border-bottom-color:#ec1c24}.theme-line header 
a.btn{background-color:#ec1c24}.theme-line header 
a.btn:hover{background-color:#950d12}.theme-line header .navbar-nav 
a:hover,.theme-line header .navbar-nav .open>a{color:#ec1c24}.theme-line header 
.navbar-nav .tt-cursor{background-color:#ec1c24}.theme-line .nav
 bar-nav .active a{font-weight:bold;color:#ec1c24}.theme-line .navbar-nav 
.active a:hover{color:#950d12}.theme-line .main-links .links 
a:hover{color:#ec1c24}.theme-line .main-content a{color:#ec1c24}.theme-line 
.main-content a:hover{color:#950d12}.theme-line .sidebar-nav ul li 
a.active:before{background-color:#ec1c24}.theme-line .sidebar-nav ul li 
a.active{color:#ec1c24}.theme-line .sidebar-nav ul li a:hover,.theme-line 
.sidebar-nav ul li a.active:hover{color:#950d12}.theme-line 
.btn-primary{background-color:#ec1c24}.theme-line 
.btn-primary:hover{background-color:#950d12}.theme-line .main-content 
.nav-horizontal a.active{border-color:#ec1c24;color:#ec1c24}.theme-line 
.main-content .nav-horizontal a:hover{color:#950d12}.theme-line .main-content 
.nav-horizontal a.active:hover{border-color:#950d12}.theme-line header 
.navbar-nav a.active,.theme-line #versions-list li a:hover strong,.theme-line 
#versions-list li a.active .current,.theme-line #versions-list li a:active 
.current{color:#ec1c
 24}.theme-line.body-threes .section-right .threes-nav 
.btn-default:hover,.theme-line.page-docs.body-threes .section-right .threes-nav 
.pull-right a:hover{color:#ec1c24;border-color:#ec1c24}.body-overlap 
.main-content{margin-top:30px}.body-box .main-content,.body-overlap 
.main-content{padding:30px;-webkit-box-shadow:0 0 0 1px 
rgba(0,0,0,0.1);box-shadow:0 0 0 1px 
rgba(0,0,0,0.1);background-color:#fff}body{font-weight:400;font-family:Roboto 
Slab, serif}h1,h2,h3,h4,h5,h6{font-weight:700;font-family:Roboto Slab, 
serif}.submit-vote.submit-vote-parent.voted 
a.submit-vote-button,.submit-vote.submit-vote-parent 
a.submit-vote-button:hover{background-color:#ec1c24}div.submit-vote.submit-vote-parent.voted
 a.submit-vote-button:hover{background-color:#950d12}a,.link 
.title{color:#ec1c24}a:hover,.link:hover .title{color:#950d12}.header 
h1.navbar-brand 
a{background-image:url(https://www.filepicker.io/api/file/QagunjDGRFul2JgNCAli)}.header
 h1.navbar-brand{width:96px}.block-edit-parameters{text-align
 
:right;padding-bottom:5px}.ng-table-pager{display:none}.container-footer{margin-top:20px}.vcenter{vertical-align:middle}.modal{display:block;overflow:hidden}.modal
 .close{position:absolute;top:24px;right:24px;float:none}.modal 
.modal-dialog{width:610px}.modal 
.modal-content{-webkit-border-radius:0;border-radius:0;background-color:#f7f7f7}.modal
 .modal-content 
.modal-header{background-color:#fff;text-align:center;color:#555;padding:24px;font-family:"myriad-pro",sans-serif}.modal
 .modal-content .modal-header 
h4{font-family:"myriad-pro",sans-serif;font-size:22px}.modal .modal-content 
.modal-header h4 
.fa{display:block;font-size:41px;color:#ddd;margin-bottom:5px}.modal 
.modal-content .modal-header 
p{margin:0;color:#aaa;font-size:1em;margin-top:3px}.modal .modal-content 
.modal-spacer{padding:10px 10px 0 10px}.modal .modal-content 
.modal-footer{margin-top:0}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/public/stylesheets/style.less
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/public/stylesheets/style.less 
b/modules/webconfig/nodejs/public/stylesheets/style.less
index 7b488ab..e6f7b8f 100644
--- a/modules/webconfig/nodejs/public/stylesheets/style.less
+++ b/modules/webconfig/nodejs/public/stylesheets/style.less
@@ -745,4 +745,62 @@ a:hover, .link:hover .title {
 
 .border-right-remove {
 
+}
+
+/* Modal */
+.modal {
+  display: block;
+  overflow: hidden;
+}
+
+.modal .close {
+  position: absolute;
+  top: 24px;
+  right: 24px;
+  float: none;
+}
+
+.modal .modal-dialog {
+  width: 610px;
+}
+
+.modal .modal-content {
+  -webkit-border-radius: 0;
+  border-radius: 0;
+  background-color: #f7f7f7;
+}
+
+.modal .modal-content .modal-header {
+  background-color: #fff;
+  text-align: center;
+  color: #555;
+  padding: 24px;
+  font-family: "myriad-pro", sans-serif;
+}
+
+.modal .modal-content .modal-header h4 {
+  font-family: "myriad-pro", sans-serif;
+  font-size: 22px;
+}
+
+.modal .modal-content .modal-header h4 .fa {
+  display: block;
+  font-size: 41px;
+  color: #ddd;
+  margin-bottom: 5px;
+}
+
+.modal .modal-content .modal-header p {
+  margin: 0;
+  color: #aaa;
+  font-size: 1em;
+  margin-top: 3px;
+}
+
+.modal .modal-content .modal-spacer {
+  padding: 10px 10px 0 10px;
+}
+
+.modal .modal-content .modal-footer {
+  margin-top: 0;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/routes/auth.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/routes/auth.js 
b/modules/webconfig/nodejs/routes/auth.js
new file mode 100644
index 0000000..1ac7a86
--- /dev/null
+++ b/modules/webconfig/nodejs/routes/auth.js
@@ -0,0 +1,52 @@
+var passport = require('passport');
+var router = require('express').Router();
+
+var db = require('../db');
+
+router.post('/register', function(req, res, next) {
+    console.log('registering user');
+
+    var account = new db.Account(req.body);
+
+    db.Account.register(account, req.body.password, function(err) {
+        if (err) {
+            console.log('error while user register!', err);
+
+            return next(err);
+        }
+
+        console.log('user registered!');
+
+        passport.authenticate('local')(req, res, function () {
+
+
+            res.redirect('/clusters');
+        });
+    });
+});
+
+router.post('/login', passport.authenticate('local', function(req, res) {
+    req.redirect('/clusters');
+}));
+//
+//router.post('/register', function(req, res, next) {
+//    var user = new db.Account(req.body);
+//
+//    user.save(function(err) {
+//        return err
+//            ? next(err)
+//            : req.logIn(user, function(err) {
+//            return err
+//                ? next(err)
+//                : res.redirect('/clusters');
+//        });
+//    });
+//});
+
+router.get('/logout', function(req, res) {
+    req.logout();
+
+    res.redirect('/');
+});
+
+module.exports = router;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/routes/clusters.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/routes/clusters.js 
b/modules/webconfig/nodejs/routes/clusters.js
index e52b8e0..e8d14e5 100644
--- a/modules/webconfig/nodejs/routes/clusters.js
+++ b/modules/webconfig/nodejs/routes/clusters.js
@@ -1,8 +1,8 @@
-var db = require('../db');
-
 var express = require('express');
 var router = express.Router();
 
+var db = require('../db');
+
 function selectAll(res) {
     db.Cluster.find(function(err, clusters) {
         // if there is an error retrieving, send the error. nothing after 
res.send(err) will execute
@@ -13,11 +13,11 @@ function selectAll(res) {
     });
 }
 
-router.get('/cluster', function(req, res) {
+router.get('/', function(req, res) {
     selectAll(res);
 });
 
-router.post('/cluster/save', function(req, res) {
+router.post('/save', function(req, res) {
     if (req.body._id) {
         var clusterId = req.body._id;
 
@@ -42,7 +42,7 @@ router.post('/cluster/save', function(req, res) {
     }
 });
 
-router.post('/cluster/remove', function(req, res) {
+router.post('/remove', function(req, res) {
     db.Cluster.remove(req.body, function (err) {
         if (err)
             res.send(err);

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/routes/pages.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/routes/pages.js 
b/modules/webconfig/nodejs/routes/pages.js
index 8f1e702..b28b63a 100644
--- a/modules/webconfig/nodejs/routes/pages.js
+++ b/modules/webconfig/nodejs/routes/pages.js
@@ -2,9 +2,21 @@ var jade = require('jade');
 var express = require('express');
 var router = express.Router();
 
+/* GET login page. */
+router.get('/login', function(req, res) {
+    res.render('login');
+});
+
+/* GET register page. */
+router.get('/register', function(req, res) {
+    res.render('register');
+});
+
 /* GET home page. */
 router.get('/', function(req, res) {
-  res.redirect('clusters');
+    console.log(req.user);
+
+    res.render('index');
 });
 
 /* GET clusters page. */

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/settings.js
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/settings.js 
b/modules/webconfig/nodejs/settings.js
deleted file mode 100644
index 0c5cefc..0000000
--- a/modules/webconfig/nodejs/settings.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
-    'url' : 'mongodb://localhost/web-configurator'
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/caches.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/caches.jade 
b/modules/webconfig/nodejs/views/caches.jade
index 60f5890..1852190 100644
--- a/modules/webconfig/nodejs/views/caches.jade
+++ b/modules/webconfig/nodejs/views/caches.jade
@@ -1,4 +1,4 @@
-extends layout
+extends layout-sidebar
 block head
 block content
     div.docs-header

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/clients.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/clients.jade 
b/modules/webconfig/nodejs/views/clients.jade
index 6a33b9a..1643b4a 100644
--- a/modules/webconfig/nodejs/views/clients.jade
+++ b/modules/webconfig/nodejs/views/clients.jade
@@ -1,4 +1,4 @@
-extends layout
+extends layout-sidebar
 block head
 block content
     div.docs-header

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/clusters.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/clusters.jade 
b/modules/webconfig/nodejs/views/clusters.jade
index c6c72f8..3abc8c2 100644
--- a/modules/webconfig/nodejs/views/clusters.jade
+++ b/modules/webconfig/nodejs/views/clusters.jade
@@ -1,10 +1,11 @@
-extends layout
-block head
-    
script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-sanitize.js')
-    
script(src='//cdnjs.cloudflare.com/ajax/libs/angular-strap/2.2.2/angular-strap.js')
-    
script(src='//cdnjs.cloudflare.com/ajax/libs/angular-strap/2.2.2/angular-strap.tpl.min.js')
+extends layout-sidebar
 
+append css
     link(rel='stylesheet', href='/stylesheets/ng-table.css')
+
+append scripts
+    script(src='/javascripts/controllers/clusters.js')
+
 block content
     div.docs-header
         h1 Clusters
@@ -33,7 +34,8 @@ block content
                         span.pull-right(type='button' 
ng-click='beginEdit(cluster); edit.discovery = true' 
data-animation="am-fade-and-scale")
                             i(class=['fa', 'fa-pencil'])
                     div.input-group(ng-if='edit.discovery && editIdx == 
$index')
-                        button(class=['btn', 'btn-default', 'form-control', 
'pull-right'] style='width: 85%' data-placement='bottom-center' 
ng-model='cluster.discovery' data-template='/select' data-placeholder='Choose 
discovery' bs-options='discovery.value as discovery.label for discovery in 
discoveries' bs-select)
+                        button(
+                        class=['btn', 'btn-default', 'form-control', 
'pull-right'] style='width: 85%' data-placement='bottom-center' 
ng-model='cluster.discovery' data-template='/select' data-placeholder='Choose 
discovery' bs-options='discovery.value as discovery.label for discovery in 
discoveries' bs-select)
                             span.caret
                         span.input-group-addon
                             i(class=['fa', 'fa-repeat'] ng-click='revert(); 
edit.discovery = false')
@@ -42,6 +44,3 @@ block content
                     center
                         span(type='button' ng-click='delete(cluster)')
                             i(class=['fa', 'fa-remove'])
-block body
-    script(src='/javascripts/ng-table.js')
-    script(src='/javascripts/controllers/clusters.js')
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/includes/header.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/includes/header.jade 
b/modules/webconfig/nodejs/views/includes/header.jade
index 689f7cc..7c5678c 100644
--- a/modules/webconfig/nodejs/views/includes/header.jade
+++ b/modules/webconfig/nodejs/views/includes/header.jade
@@ -3,7 +3,10 @@ header.header(id='header')
         h1.navbar-brand
             a(href='/') Apache Ignite Web Configurator
         div(class=['navbar-collapse', 'collapse'])
-            ul(class=['nav', 'navbar-nav', 'pull-right'])
+            ul(class=['nav', 'navbar-nav'])
+                li
+                    a.active(href='/') Home
+            ul(class=['nav', 'navbar-nav', 'pull-right'] ng-controller='auth')
                 li(ng-show='user')
                     // TODO
                 li(ng-show='!user' class=['nav-login'])

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/index.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/index.jade 
b/modules/webconfig/nodejs/views/index.jade
new file mode 100644
index 0000000..3bac889
--- /dev/null
+++ b/modules/webconfig/nodejs/views/index.jade
@@ -0,0 +1,18 @@
+extends layout
+block container
+    div.row
+        div(class=['col-sm-2', 'border-right', 'section-left'])
+            div.sidebar-nav
+                ul(ng-controller='activeLink' class="menu")
+                    li
+                        a(ng-class="{active: isActive('/clusters')}" 
href='/clusters') Clusters
+                    li
+                        a(ng-class="{active: isActive('/caches')}" 
href='/caches') Caches
+                    li
+                        a(ng-class="{active: isActive('/persistence')}" 
href='/persistence') Persistence
+                    li
+                        a(ng-class="{active: isActive('/sql')}" href='/sql') 
SQL
+                    li
+                        a(ng-class="{active: isActive('/clients')}" 
href='/clients') Clients
+        div(class=['col-sm-10', 'border-left', 'section-right'])
+            div.docs-content

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/layout-sidebar.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/layout-sidebar.jade 
b/modules/webconfig/nodejs/views/layout-sidebar.jade
new file mode 100644
index 0000000..f35f02d
--- /dev/null
+++ b/modules/webconfig/nodejs/views/layout-sidebar.jade
@@ -0,0 +1,22 @@
+extends layout
+
+append scripts
+
+block container
+    div.row
+       div(class=['col-sm-2', 'border-right', 'section-left'])
+          div.sidebar-nav
+             ul(ng-controller='activeLink' class="menu")
+                li
+                   a(ng-class="{active: isActive('/clusters')}" 
href='/clusters') Clusters
+                li
+                   a(ng-class="{active: isActive('/caches')}" href='/caches') 
Caches
+                li
+                   a(ng-class="{active: isActive('/persistence')}" 
href='/persistence') Persistence
+                li
+                   a(ng-class="{active: isActive('/sql')}" href='/sql') SQL
+                li
+                   a(ng-class="{active: isActive('/clients')}" 
href='/clients') Clients
+       div(class=['col-sm-10', 'border-left', 'section-right'])
+          div.docs-content
+             block content
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/layout.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/layout.jade 
b/modules/webconfig/nodejs/views/layout.jade
index 2281679..abe310e 100644
--- a/modules/webconfig/nodejs/views/layout.jade
+++ b/modules/webconfig/nodejs/views/layout.jade
@@ -1,56 +1,42 @@
 doctype html
 html(ng-app='ignite-web-configurator')
-   head
+    head
         title= title
-        //add bootstrap dependencies from CDNjs. Using a CDN to distribute 
these files puts less pressure
-        //on our web server and will be delivered faster to the client than 
our own server.
-        link(rel='stylesheet', 
href='//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.css')
-
-        // Font Awesome Icons
-        link(rel='stylesheet', 
href='//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.css')
-        link(rel='stylesheet', 
href='//fonts.googleapis.com/css?family=Roboto+Slab:700:serif|Roboto+Slab:400:serif')
-
-        // AdminLTE
-        //link(rel='stylesheet', href='/stylesheets/AdminLTE.css')
-        script(src='//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js')
-        //script(src='/javascripts/AdminLTE.js')
-
-        
script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js')
-
-        script(src='/javascripts/bundle.js')
-
-        // Site style
-        //link(rel='stylesheet', href='/stylesheets/skin-ignite.css')
-        link(rel='stylesheet', href='/stylesheets/style.css')
-
-        block head
-   body(class=['theme-line', 'body-overlap'])
-       div.wrapper
-           include includes/header
-
-           div(class=['container', 'body-container'])
-               div.main-content
-                   div.main-head
-                       div.row
-                           div(class=['col-sm-2', 'border-right', 
'section-left'])
-                               div.sidebar-nav
-                                   ul(ng-controller='activeLink' class="menu")
-                                       li
-                                           a(ng-class="{active: 
isActive('/clusters')}" href='/clusters') Clusters
-                                       li
-                                           a(ng-class="{active: 
isActive('/caches')}" href='/caches') Caches
-                                       li
-                                           a(ng-class="{active: 
isActive('/persistence')}" href='/persistence') Persistence
-                                       li
-                                           a(ng-class="{active: 
isActive('/sql')}" href='/sql') SQL
-                                       li
-                                           a(ng-class="{active: 
isActive('/clients')}" href='/clients') Clients
-                           div(class=['col-sm-10', 'border-left', 
'section-right'])
-                               div.docs-content
-                                   block content
-
-           include includes/footer
-       <!-- Custom script load -->
-       script(src='/javascripts/controllers/sidebar.js')
-
-       block body
\ No newline at end of file
+
+        block css
+            // Bootstrap
+            link(rel='stylesheet', 
href='//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.css')
+
+            // Font Awesome Icons
+            link(rel='stylesheet', 
href='//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.css')
+
+            // Font
+            link(rel='stylesheet', 
href='//fonts.googleapis.com/css?family=Roboto+Slab:700:serif|Roboto+Slab:400:serif')
+
+            link(rel='stylesheet', href='/stylesheets/style.css')
+
+        block scripts
+            
script(src='//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js')
+
+            
script(src='//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js')
+            
script(src='//cdnjs.cloudflare.com/ajax/libs/angular-strap/2.2.2/angular-strap.js')
+            
script(src='//cdnjs.cloudflare.com/ajax/libs/angular-strap/2.2.2/angular-strap.tpl.min.js')
+
+            script(src='/javascripts/ng-table.js')
+
+            script(src='/javascripts/bundle.js')
+
+            script(src='/javascripts/controllers/common.js')
+
+    body(class=['theme-line', 'body-overlap'])
+        div.wrapper
+            include includes/header
+
+            div(class=['container', 'body-container'])
+                div.main-content
+                    div.main-head
+                        block container
+
+            include includes/footer
+
+        block body-script
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/login.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/login.jade 
b/modules/webconfig/nodejs/views/login.jade
new file mode 100644
index 0000000..1ef6b3c
--- /dev/null
+++ b/modules/webconfig/nodejs/views/login.jade
@@ -0,0 +1,31 @@
+div(class=['modal', 'center'] tabindex='-1' role='dialog')
+    .modal-dialog
+        .modal-content
+            .modal-header
+                button.close(type='button', ng-click='$hide()', 
aria-hidden='true') &times;
+                h4.modal-title
+                    i.fa.fa-group
+                    | Authentication
+                p Log in or register in order to collaborate
+            .modal-body
+                form.form-horizontal(name='loginForm')
+                    .modal-body.row
+                        .col-xs-10.login.col-xs-offset-1
+                            div.form-group(ng-show="action == 'signup'")
+                                label.col-sm-3.control-label Full Name
+                                .controls.col-sm-9
+                                    input.form-control(type='text', 
ng-model='user_info.username', placeholder='John Smith', 
focus-me="action=='signup'", ng-required="action=='signup'")
+                            div.form-group
+                                label.col-sm-3.control-label Email
+                                .controls.col-sm-9
+                                    input.form-control(type='email', 
ng-model='user_info.email', placeholder='y...@domain.com', 
focus-me="action=='login'", required)
+                            div.form-group
+                                label.col-sm-3.control-label Password
+                                .controls.col-sm-9
+                                    input.form-control(type='password', 
ng-model='user_info.password', placeholder='Password', required)
+                    .modal-footer
+                        a.show-signup.ng-hide(ng-show="action != 'login'", 
ng-click="action = 'login';") log in
+                        a.show-signup(ng-show="action != 'signup'", 
ng-click="action = 'signup';") sign up
+                        | &nbsp;or&nbsp;
+                        button.btn.btn-primary(ng-click='auth(action, 
user_info)' ng-show="action == 'login'" ng-disabled='loginForm.$invalid') Log In
+                        button.btn.btn-primary(ng-click='auth(action, 
user_info)' ng-show="action == 'signup'" ng-disabled='loginForm.$invalid') Sign 
Up

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/persistence.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/persistence.jade 
b/modules/webconfig/nodejs/views/persistence.jade
index 95086d6..c45264d 100644
--- a/modules/webconfig/nodejs/views/persistence.jade
+++ b/modules/webconfig/nodejs/views/persistence.jade
@@ -1,4 +1,4 @@
-extends layout
+extends layout-sidebar
 block head
 block content
     div.docs-header

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/register.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/register.jade 
b/modules/webconfig/nodejs/views/register.jade
new file mode 100644
index 0000000..972c8f6
--- /dev/null
+++ b/modules/webconfig/nodejs/views/register.jade
@@ -0,0 +1,8 @@
+h4 Register your account
+form(action='/register' method='post')
+   fieldset
+      label(for='email') Email
+      input(type='email' name='email' placeholder='Your Email')
+      label(for='password') Password
+      input(type='password' name='password' placeholder='Your Password')
+      button(type='Register')
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b226232b/modules/webconfig/nodejs/views/sql.jade
----------------------------------------------------------------------
diff --git a/modules/webconfig/nodejs/views/sql.jade 
b/modules/webconfig/nodejs/views/sql.jade
index fc680ef..f1295d8 100644
--- a/modules/webconfig/nodejs/views/sql.jade
+++ b/modules/webconfig/nodejs/views/sql.jade
@@ -1,4 +1,4 @@
-extends layout
+extends layout-sidebar
 block head
 block content
     div.docs-header

Reply via email to