├── public ├── js │ ├── custom.min.js │ ├── angular-loding-bar.min.js │ ├── angular-chart.min.js │ ├── angular-timer.min.js │ └── angular-animate.min.js ├── css │ ├── login_signup.css │ ├── loading-bar-min.css │ ├── tests.css │ ├── main.css │ ├── dashboard.css │ └── bootstrap-social.css ├── views │ ├── allusers.html │ ├── main.html │ ├── edit-test.html │ ├── create-test.html │ ├── login.html │ ├── signup.html │ ├── dashboard.html │ ├── forgot-pass.html │ ├── userperformances.html │ ├── result.html │ ├── available-tests.html │ ├── index.html │ ├── live-test.html │ └── question-operations.html ├── angular │ ├── app.js │ ├── services │ │ ├── authService.js │ │ └── queryService.js │ ├── controllers │ │ ├── resultsController.js │ │ ├── userController.js │ │ ├── dashController.js │ │ ├── livetestController.js │ │ └── testController.js │ └── routes.js └── index.html ├── config ├── config.js ├── auth.js ├── facebook-auth.js └── google-auth.js ├── libs └── responseGenerator.js ├── app ├── models │ ├── SocialUser.js │ ├── User.js │ ├── Question.js │ ├── Test.js │ └── Performance.js └── controllers │ ├── user.controller.js │ └── test.controller.js ├── package.json ├── middlewares └── auth.js ├── app.js └── README.md /public/js/custom.min.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'db': 'mongodb://localhost/examGround', 3 | 'jwtsecret': '97hw9a73hr2q@#$#@mo8afjoeidha0e8doh', 4 | 'username': '', 5 | 'pass': '' 6 | } 7 | -------------------------------------------------------------------------------- /libs/responseGenerator.js: -------------------------------------------------------------------------------- 1 | module.exports.generate = (error, message, status, data)=>{ 2 | 3 | const myResponse = { 4 | error: error, 5 | message: message, 6 | status: status, 7 | data: data 8 | }; 9 | 10 | return myResponse; 11 | 12 | } -------------------------------------------------------------------------------- /app/models/SocialUser.js: -------------------------------------------------------------------------------- 1 | //Including Mongoose model... 2 | const mongoose = require('mongoose'); 3 | //creating object 4 | const Schema = mongoose.Schema; 5 | 6 | //Schema for user 7 | const socialUserSchema = new Schema({ 8 | 9 | id : {type: String }, 10 | name : {type: String }, 11 | email : {type: String} 12 | }); 13 | 14 | //model for userschema 15 | module.exports = mongoose.model('SocialUser' , socialUserSchema); -------------------------------------------------------------------------------- /config/auth.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | facebookAuth: { 3 | clientID: '149050199145264', 4 | clientSecret: '0483572fb4a327cca5f44e5524c3bee2', 5 | callbackURL: 'http://localhost:3000/auth/facebook/callback' 6 | }, 7 | googleAuth: { 8 | clientID: '236387109532-r34qnq79a4hm2v4bvdbf1a7p0sctbkse.apps.googleusercontent.com', 9 | clientSecret: '6LIPociSukgHY8usX4uf9ZKY', 10 | callbackURL: 'http://localhost:3000/auth/google/callback' 11 | } 12 | 13 | }; -------------------------------------------------------------------------------- /app/models/User.js: -------------------------------------------------------------------------------- 1 | //Including Mongoose model... 2 | const mongoose = require('mongoose'); 3 | //creating object 4 | const Schema = mongoose.Schema; 5 | 6 | //Schema for user 7 | const userSchema = new Schema({ 8 | 9 | name : {type: String, required: true }, 10 | email : {type: String, required: true }, 11 | mobileNumber : {type: Number, required: true }, 12 | password : {type: String , required: true } 13 | 14 | }); 15 | 16 | module.exports=mongoose.model('User',userSchema); -------------------------------------------------------------------------------- /app/models/Question.js: -------------------------------------------------------------------------------- 1 | //Including Mongoose model... 2 | const mongoose = require('mongoose'); 3 | 4 | //creating object 5 | const Schema = mongoose.Schema; 6 | 7 | const QuestionSchema = new Schema({ 8 | question: { type: String, require:true}, 9 | optionA: { type: String, require:true }, 10 | optionB: { type: String, require:true}, 11 | optionC: { type: String, require:true }, 12 | optionD: { type: String, require:true}, 13 | answer :{type:Number, require:true} 14 | }); 15 | 16 | module.exports = mongoose.model('Question', QuestionSchema); -------------------------------------------------------------------------------- /public/css/login_signup.css: -------------------------------------------------------------------------------- 1 | #hr { 2 | width: 100%; 3 | text-align: center; 4 | border-bottom: 1px solid #000; 5 | line-height: 0.1em; 6 | margin: 10px 0 20px; 7 | } 8 | 9 | #hr span { 10 | background:#fff; 11 | padding:0 10px; 12 | } 13 | .login-form{ 14 | margin-top: 15vh; 15 | } 16 | .signup-form{ 17 | margin-top: 8vh; 18 | } 19 | .backbtn{ 20 | margin-top: 20px; 21 | } 22 | body{ 23 | 24 | background: #C9D6FF; /* fallback for old browsers */ 25 | background: -webkit-linear-gradient(to right, #E2E2E2, #C9D6FF); /* Chrome 10-25, Safari 5.1-6 */ 26 | background: linear-gradient(to right, #E2E2E2, #C9D6FF); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/models/Test.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'), 2 | Question = require('./Question.js'), 3 | QuestionSchema = mongoose.model('Question').schema; 4 | 5 | 6 | const Schema = mongoose.Schema; 7 | 8 | 9 | const testSchema = new Schema({ 10 | 11 | testid: { 12 | type: String 13 | }, 14 | title: { 15 | type: String 16 | }, 17 | description: { 18 | type: String 19 | }, 20 | time: { 21 | type: String 22 | }, 23 | instructions: { 24 | type: String 25 | }, 26 | testAttemptedBy: [], 27 | questions: [QuestionSchema], 28 | created: { 29 | type: Date, 30 | default: Date.now 31 | } 32 | }); 33 | 34 | module.exports = mongoose.model('Test', testSchema); -------------------------------------------------------------------------------- /public/views/allusers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
#EmailScoreCorrect AnswersTime Taken
{{$index+1}}{{user.userEmail}}{{user.score}}{{user.totalCorrectAnswers}}{{user.timeTaken}}s
23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ExamGround", 3 | "version": "1.0.0", 4 | "description": "A Live Test Taking System developed using MEAN Stack.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start":"nodemon app.js -e .html,.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Rahul Jhawar", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^1.0.3", 14 | "cors": "^2.8.4", 15 | "express": "^4.16.2", 16 | "express-session": "^1.15.6", 17 | "express-sessions": "^1.0.6", 18 | "jsonwebtoken": "^8.1.0", 19 | "mongoose": "^4.13.9", 20 | "nodemailer": "^4.4.1", 21 | "passport": "^0.4.0", 22 | "passport-facebook": "^2.1.1", 23 | "passport-google-oauth2": "^0.1.6", 24 | "random-number": "0.0.9", 25 | "randomstring": "^1.1.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/models/Performance.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const PerformanceSchema = new Schema({ 5 | 6 | userEmail: { 7 | type: String, required:true 8 | }, 9 | testId:{ 10 | type: String 11 | }, 12 | score: { 13 | type: Number, 14 | default: 0 15 | }, 16 | questionCount:{ 17 | type: Number, 18 | default: 0 19 | }, 20 | timeTaken: { 21 | type: Number, 22 | default: 0 23 | }, 24 | totalCorrectAnswers: { 25 | type: Number, 26 | default: 0 27 | }, 28 | totalSkipped: { 29 | type: Number, 30 | default: 0 31 | } 32 | 33 | 34 | }) 35 | module.exports = mongoose.model('Performance', PerformanceSchema) 36 | -------------------------------------------------------------------------------- /public/angular/app.js: -------------------------------------------------------------------------------- 1 | const myApp = angular.module('ExamGround',['ui.router','ngAnimate','timer','chart.js','angular-loading-bar']) 2 | .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) { 3 | cfpLoadingBarProvider.includeSpinner = false; 4 | }]) 5 | .directive('stringToNumber', function() { 6 | return { 7 | require: 'ngModel', 8 | link: function(scope, element, attrs, ngModel) { 9 | ngModel.$parsers.push(function(value) { 10 | return '' + value; 11 | }); 12 | ngModel.$formatters.push(function(value) { 13 | return parseFloat(value); 14 | }); 15 | } 16 | }; 17 | }); 18 | 19 | // filter for showing live questions 20 | myApp.filter('startFrom', function() { 21 | return function(input, start) { 22 | start = +start; 23 | return input.slice(start); 24 | } 25 | }); -------------------------------------------------------------------------------- /middlewares/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const responseGenerator = require('./../libs/responseGenerator.js'); 3 | const config = require('./../config/config.js'); 4 | 5 | module.exports.verifyToken = (req,res,next)=> { 6 | const token = req.body.token || req.query.token || req.headers['x-access-token']; 7 | if (token) { 8 | // verifies secret and checks exp 9 | jwt.verify(token, config.jwtsecret, (err, decoded)=> { 10 | if (err) { //failed verification. 11 | const response = responseGenerator.generate(true, "Failed to Authenticate", 403, null); 12 | res.json(response); 13 | }else{ 14 | req.user = decoded; 15 | next(); //no error, proceed 16 | } 17 | }); 18 | } else { 19 | // forbidden without token 20 | return res.status(403).send({ 21 | success: false, 22 | message: 'No token provided.' 23 | }); 24 | } 25 | } -------------------------------------------------------------------------------- /public/views/main.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 | 6 | 7 | 10 | Sign up 11 |
12 |
13 |
14 | 15 | 16 | 17 | 18 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /public/angular/services/authService.js: -------------------------------------------------------------------------------- 1 | //Authorization Factory to manage token 2 | myApp.factory('authService' , ['$window' , ($window)=>{ 3 | 4 | let authToken = {}; 5 | 6 | //accesing local storage through $window service 7 | let store = $window.localStorage; 8 | let key = 'auth-token'; 9 | 10 | 11 | 12 | // Function to check if user is currently logged in 13 | authToken.isLoggedIn = ()=> { 14 | // CHeck if token is in local storage 15 | if (authToken.getToken()) { 16 | return true; // Return true if in storage 17 | } else { 18 | return false; // Return false if not in storage 19 | } 20 | }; 21 | //function to get token from local storage 22 | authToken.getToken = ()=>{ 23 | //console.log(store.getItem(key)); 24 | return store.getItem(key); 25 | } 26 | 27 | //function to set token to local storage 28 | authToken.setToken = (token)=>{ 29 | if(token){ 30 | store.setItem(key, token); 31 | }else{ 32 | store.removeItem(key); 33 | } 34 | } 35 | 36 | return authToken; 37 | 38 | 39 | }]); -------------------------------------------------------------------------------- /public/views/edit-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Edit Test Details

4 |
5 |
6 |
7 |
8 |
9 | Please Fill In The Details 10 |
11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /public/views/create-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Create New Test

4 |
5 |
6 |
7 |
8 |
9 | Please Fill In The Details 10 |
11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /public/css/loading-bar-min.css: -------------------------------------------------------------------------------- 1 | #loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner 400ms linear infinite;-moz-animation:loading-bar-spinner 400ms linear infinite;-ms-animation:loading-bar-spinner 400ms linear infinite;-o-animation:loading-bar-spinner 400ms linear infinite;animation:loading-bar-spinner 400ms linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}} -------------------------------------------------------------------------------- /config/facebook-auth.js: -------------------------------------------------------------------------------- 1 | const FacebookStrategy = require('passport-facebook').Strategy; 2 | const SocialUser = require('../app/models/SocialUser'); 3 | const configAuth = require('./auth'); 4 | const config = require('./config'); 5 | const jwt = require('jsonwebtoken'); 6 | 7 | module.exports = (passport) =>{ 8 | passport.serializeUser( (user, done) =>{ 9 | 10 | done(null, user.id); 11 | }); 12 | passport.deserializeUser( (id, done)=> { 13 | SocialUser.findById(id, (err, user)=> { 14 | done(err, user); 15 | }); 16 | }); 17 | 18 | passport.use(new FacebookStrategy({ 19 | clientID: configAuth.facebookAuth.clientID, 20 | clientSecret: configAuth.facebookAuth.clientSecret, 21 | callbackURL: configAuth.facebookAuth.callbackURL, 22 | profileFields: ['id', 'emails', 'name'] 23 | }, 24 | (accessToken, refreshToken, profile, done)=> { 25 | //console.log(profile); 26 | SocialUser.findOne({ 27 | 'id': profile.id 28 | }, (err, user)=> { 29 | if (err) 30 | return done(err); 31 | if (user) { 32 | token=jwt.sign({id:user.id,email:user.email,name:user.name}, config.jwtsecret, { expiresIn: '1h' }); 33 | return done(null, user); 34 | } else { 35 | let newSocialUser = new SocialUser(); 36 | newSocialUser.id = profile.id; 37 | newSocialUser.name = profile.name.givenName + ' ' + profile.name.familyName; 38 | newSocialUser.email = profile.emails[0].value; 39 | newSocialUser.save(function (err) { 40 | if (err) 41 | throw err; 42 | token=jwt.sign({id:newSocialUser.id ,email:newSocialUser.email,name:newSocialUser.name}, config.jwtsecret, { expiresIn: '1h' }); 43 | return done(null, newSocialUser); 44 | }); 45 | } 46 | }); 47 | })); 48 | }; -------------------------------------------------------------------------------- /public/views/login.html: -------------------------------------------------------------------------------- 1 |
2 | Go Back To HomePage 3 |
4 |
5 | 53 |
54 |
-------------------------------------------------------------------------------- /public/views/signup.html: -------------------------------------------------------------------------------- 1 |
2 | Go Back To HomePage 3 |
4 | 54 |
55 | -------------------------------------------------------------------------------- /public/angular/controllers/resultsController.js: -------------------------------------------------------------------------------- 1 | myApp.controller('resultsController',['$http','$window','$scope','$stateParams','$filter','apiService','authService','$rootScope','$location','$state',function($http,$window,$scope,$stateParams,$filter,apiService,authService,$rootScope,$location,$state){ 2 | 3 | var result=this; 4 | var testid=$stateParams.tid; 5 | 6 | // get the result of the user 7 | this.getResult=()=>{ 8 | apiService.getusertestdetails(testid).then(function successCallBack(response){ 9 | 10 | result.performance=response.data.data; 11 | result.email=result.performance.userEmail ; 12 | //evualuating the no. of wrong answers and the percentile 13 | result.wrongAns = result.noOfQuestions - ( result.performance.totalCorrectAnswers + result.performance.totalSkipped ); 14 | result.percentile=(result.performance.score/(result.noOfQuestions*2))*100; 15 | //console.log(result.performance); 16 | 17 | // chart.js configurations 18 | result.options={ 19 | legend : { display : true,position : 'bottom'} 20 | }; 21 | result.labels =[ result.performance.totalCorrectAnswers + " Correct Answers" , result.wrongAns + " Wrong Answers", result.performance.totalSkipped +" Skipped"]; 22 | result.data=[result.performance.totalCorrectAnswers,result.wrongAns,result.performance.totalSkipped ]; 23 | 24 | // evaluating the rank of the user 25 | var sorted = result.testAttemptedBy.slice().sort(function(a,b){return b.score-a.score}); 26 | result.rank = sorted.findIndex(x => x.email===result.email) + 1; 27 | }, 28 | function errorCallback(response) { 29 | alert("some error occurred. Check the console."); 30 | console.log(response); 31 | }); 32 | 33 | }; 34 | // getting the testtime and other required values 35 | this.getTestTime=()=>{ 36 | apiService.viewTest(testid).then(function successCallBack(response){ 37 | result.testname=response.data.data.title; 38 | result.testtime=response.data.data.time; 39 | result.noOfQuestions=response.data.data.questions.length; 40 | result.testAttemptedBy=response.data.data.testAttemptedBy; 41 | result.noOfStudents = result.testAttemptedBy.length; 42 | 43 | 44 | }); 45 | 46 | //call the function 47 | result.getResult(); 48 | 49 | 50 | }; 51 | 52 | 53 | //call the function 54 | this.getTestTime(); 55 | 56 | 57 | 58 | }]); 59 | 60 | 61 | -------------------------------------------------------------------------------- /config/google-auth.js: -------------------------------------------------------------------------------- 1 | 2 | const GoogleStrategy = require('passport-google-oauth2').Strategy; 3 | const SocialUser = require('../app/models/SocialUser'); 4 | const configAuth = require('./auth'); 5 | const config = require('./config'); 6 | const jwt = require('jsonwebtoken'); 7 | 8 | module.exports = (passport)=> { 9 | passport.serializeUser( (user, done)=> { 10 | //console.log(user); 11 | done(null, user.id); 12 | }); 13 | passport.deserializeUser( (id, done) =>{ 14 | SocialUser.findById(id, (err, user)=> { 15 | done(err, user); 16 | }); 17 | }); 18 | 19 | passport.use(new GoogleStrategy({ 20 | clientID: configAuth.googleAuth.clientID, 21 | clientSecret: configAuth.googleAuth.clientSecret, 22 | callbackURL: configAuth.googleAuth.callbackURL 23 | //passReqToCallback: true 24 | }, 25 | (accessToken, refreshToken, profile, done)=> { 26 | //console.log("callback function fired!"); 27 | //console.log(profile); 28 | // make the code asynchronous 29 | // User.findOne won't fire until we have all our data back from Google 30 | process.nextTick(function() { 31 | console.log(profile); 32 | SocialUser.findOne({ 33 | 'id': profile.id 34 | }, (err, user) =>{ 35 | if (err) 36 | return done(err); 37 | if (user) { 38 | token=jwt.sign({id:user.id,email:user.email,name:user.name}, config.jwtsecret, { expiresIn: '1h' }); 39 | return done(null, user); 40 | } else { 41 | let newSocialUser = new SocialUser(); 42 | newSocialUser.id = profile.id; 43 | newSocialUser.name = profile.displayName; 44 | newSocialUser.email = profile.emails[0].value; 45 | newSocialUser.save(function (err) { 46 | if (err) 47 | throw err; 48 | token=jwt.sign({id:newSocialUser.id ,email:newSocialUser.email,name:newSocialUser.name}, config.jwtsecret, { expiresIn: '1h' }); 49 | return done(null, newSocialUser); 50 | }); 51 | //console.log(newSocialUser); 52 | } 53 | }); 54 | }); 55 | })); 56 | }; -------------------------------------------------------------------------------- /public/views/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 53 |
54 |
55 | 56 | 57 | 58 |
59 |
60 |
61 |
62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /public/views/forgot-pass.html: -------------------------------------------------------------------------------- 1 |
2 | 39 |
40 |
41 |
42 | 69 |
70 | 71 |
72 | -------------------------------------------------------------------------------- /public/views/userperformances.html: -------------------------------------------------------------------------------- 1 |
Click To Get the OverAll Performance
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
#NameEmailMobileLogin Type
{{$index+1}}{{user.name}}{{user.email}}{{user.mobileNumber}}NALocalFacebookGoogle
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ExamGround 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /public/js/angular-loding-bar.min.js: -------------------------------------------------------------------------------- 1 | 2 | !function(){"use strict";angular.module("angular-loading-bar",["cfp.loadingBarInterceptor"]),angular.module("chieffancypants.loadingBar",["cfp.loadingBarInterceptor"]),angular.module("cfp.loadingBarInterceptor",["cfp.loadingBar"]).config(["$httpProvider",function(a){var b=["$q","$cacheFactory","$timeout","$rootScope","$log","cfpLoadingBar",function(b,c,d,e,f,g){function h(){d.cancel(j),g.complete(),l=0,k=0}function i(b){var d,e=c.get("$http"),f=a.defaults;!b.cache&&!f.cache||b.cache===!1||"GET"!==b.method&&"JSONP"!==b.method||(d=angular.isObject(b.cache)?b.cache:angular.isObject(f.cache)?f.cache:e);var g=void 0!==d?void 0!==d.get(b.url):!1;return void 0!==b.cached&&g!==b.cached?b.cached:(b.cached=g,g)}var j,k=0,l=0,m=g.latencyThreshold;return{request:function(a){return a.ignoreLoadingBar||i(a)||(e.$broadcast("cfpLoadingBar:loading",{url:a.url}),0===k&&(j=d(function(){g.start()},m)),k++,g.set(l/k)),a},response:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,l>=k?(e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),h()):g.set(l/k)),a):(f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),a)},responseError:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,l>=k?(e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),h()):g.set(l/k)),b.reject(a)):(f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),b.reject(a))}}}];a.interceptors.push(b)}]),angular.module("cfp.loadingBar",[]).provider("cfpLoadingBar",function(){this.autoIncrement=!0,this.includeSpinner=!0,this.includeBar=!0,this.latencyThreshold=100,this.startSize=.02,this.parentSelector="body",this.spinnerTemplate='
',this.loadingBarTemplate='
',this.$get=["$injector","$document","$timeout","$rootScope",function(a,b,c,d){function e(){if(k||(k=a.get("$animate")),c.cancel(m),!r){var e=b[0],g=e.querySelector?e.querySelector(n):b.find(n)[0];g||(g=e.getElementsByTagName("body")[0]);var h=angular.element(g),i=g.lastChild&&angular.element(g.lastChild);d.$broadcast("cfpLoadingBar:started"),r=!0,v&&k.enter(o,h,i),u&&k.enter(q,h,o),f(w)}}function f(a){if(r){var b=100*a+"%";p.css("width",b),s=a,t&&(c.cancel(l),l=c(function(){g()},250))}}function g(){if(!(h()>=1)){var a=0,b=h();a=b>=0&&.25>b?(3*Math.random()+3)/100:b>=.25&&.65>b?3*Math.random()/100:b>=.65&&.9>b?2*Math.random()/100:b>=.9&&.99>b?.005:0;var c=h()+a;f(c)}}function h(){return s}function i(){s=0,r=!1}function j(){k||(k=a.get("$animate")),f(1),c.cancel(m),m=c(function(){var a=k.leave(o,i);a&&a.then&&a.then(i),k.leave(q),d.$broadcast("cfpLoadingBar:completed")},500)}var k,l,m,n=this.parentSelector,o=angular.element(this.loadingBarTemplate),p=o.find("div").eq(0),q=angular.element(this.spinnerTemplate),r=!1,s=0,t=this.autoIncrement,u=this.includeSpinner,v=this.includeBar,w=this.startSize;return{start:e,set:f,status:h,inc:g,complete:j,autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}(); -------------------------------------------------------------------------------- /public/views/result.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 |
Test Information
7 |
8 |
{{resultCtrl.noOfStudents}}

Students

9 |
{{resultCtrl.testname}}

Test Name

10 |
{{resultCtrl.noOfQuestions}}

Questions

11 |
{{resultCtrl.testtime}} Min

Time

12 | 13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 | 24 |

{{resultCtrl.rank}}

25 |

Rank

26 |
27 |
28 |
29 |
30 | 31 |
32 | 33 |
34 | 35 |

{{resultCtrl.performance.score}}

36 |

Marks Scored

37 |
38 |
39 |
40 |
41 |
42 |
43 |
Percentile
44 | 45 |
46 | {{resultCtrl.percentile| number:2}}% 47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 | 59 |
60 | 61 | 63 | 64 |
65 |
66 |
67 | 68 |
69 |
70 |
71 |
72 |
Time Statics
73 |
74 |
{{resultCtrl.noOfQuestions}}

Questions

75 |
{{resultCtrl.performance.timeTaken}}s

Time Taken

76 |
{{resultCtrl.performance.timeTaken/resultCtrl.noOfQuestions | number:2}}s

Avg Speed

77 |
78 |
79 |
80 |
81 | 82 | -------------------------------------------------------------------------------- /public/css/tests.css: -------------------------------------------------------------------------------- 1 | .modal-body{ 2 | max-height: calc(100vh - 200px); 3 | overflow-y: auto; 4 | } 5 | 6 | 7 | .preview { 8 | margin-bottom: 20px ; 9 | } 10 | 11 | .questionsBox { 12 | display: block; 13 | border: solid 1px #e3e3e3; 14 | padding: 10px 20px 0px; 15 | box-shadow: inset 0 0 30px rgba(000,000,000,0.1), inset 0 0 4px rgba(255,255,255,1); 16 | border-radius: 3px; 17 | margin: 0 10px; 18 | }.questions { 19 | background: #007fbe; 20 | color: #FFF; 21 | font-size: 22px; 22 | padding: 8px 30px; 23 | font-weight: 300; 24 | margin: 0 -30px 10px; 25 | position: relative; 26 | } 27 | .questions:after { 28 | display: block; 29 | position: absolute; 30 | top: 100%; 31 | width: 9px; 32 | height: 7px; 33 | content: '.'; 34 | left: 0; 35 | text-align: left; 36 | font-size: 0; 37 | } 38 | .questions:after { 39 | left: auto; 40 | right: 0; 41 | background-position: -10px 0; 42 | } 43 | .questions:before, .questions:after { 44 | background: black; 45 | display: block; 46 | position: absolute; 47 | top: 100%; 48 | width: 9px; 49 | height: 7px; 50 | content: '.'; 51 | left: 0; 52 | text-align: left; 53 | font-size: 0; 54 | } 55 | .answerList { 56 | margin-bottom: 15px; 57 | } 58 | 59 | 60 | ol, ul { 61 | list-style: none; 62 | } 63 | .answerList li:first-child { 64 | border-top-width: 0; 65 | } 66 | 67 | .answerList li { 68 | padding: 3px 0; 69 | } 70 | .answerList label { 71 | display: block; 72 | padding: 6px; 73 | border-radius: 6px; 74 | border: solid 1px #dde7e8; 75 | font-weight: 400; 76 | font-size: 13px; 77 | cursor: pointer; 78 | font-family: Arial, sans-serif; 79 | } 80 | input[type=checkbox], input[type=radio] { 81 | margin: 4px 0 0; 82 | margin-top: 1px; 83 | line-height: normal; 84 | } 85 | .questionsRow { 86 | background: #dee3e6; 87 | margin: 0 -20px; 88 | padding: 10px 20px; 89 | border-radius: 0 0 3px 3px; 90 | } 91 | .button, .greyButton { 92 | background-color: #f2f2f2; 93 | color: #888888; 94 | display: inline-block; 95 | border: solid 3px #cccccc; 96 | vertical-align: middle; 97 | text-shadow: 0 1px 0 #ffffff; 98 | line-height: 27px; 99 | min-width: 160px; 100 | text-align: center; 101 | padding: 5px 20px; 102 | text-decoration: none; 103 | border-radius: 0px; 104 | text-transform: capitalize; 105 | } 106 | .questionsRow span { 107 | float: right; 108 | display: inline-block; 109 | line-height: 30px; 110 | border: solid 1px #aeb9c0; 111 | padding: 0 10px; 112 | background: #FFF; 113 | color: #007fbe; 114 | } 115 | .question-pallete{ 116 | position: relative; 117 | border:1px solid grey; 118 | padding: 10px 10px; 119 | } 120 | .square{ 121 | display: inline-flex; 122 | flex-flow: column wrap; 123 | margin:10px 10px; 124 | height:30px; 125 | width: 30px; 126 | border:1px solid grey; 127 | } 128 | .info{ 129 | display: inline-flex; 130 | align-items: center; 131 | position: absolute; 132 | right:5px; 133 | bottom:0; 134 | } 135 | .info-square-1{ 136 | display: block; 137 | background-color: #fff59d; 138 | height:15px; 139 | width:15px; 140 | } 141 | .info-square-2{ 142 | display: block; 143 | margin-left: 10px; 144 | border:1px solid grey; 145 | height:15px; 146 | width:15px; 147 | } 148 | .square a{ 149 | display: table-cell; 150 | display: flex; 151 | align-items: center; 152 | justify-content: center; 153 | height: 30px; 154 | width: 30px; 155 | } 156 | .box-color{ 157 | background-color: #fff59d; 158 | } 159 | .preview timer{ 160 | position: absolute; 161 | color: #fff; 162 | z-index:10000; 163 | top:21px; 164 | right:150px; 165 | } 166 | 167 | -------------------------------------------------------------------------------- /public/angular/controllers/userController.js: -------------------------------------------------------------------------------- 1 | myApp.controller('userController',['$http','apiService','$rootScope','authService','$location','$window',function($http,apiService,$rootScope,authService,$location,$window){ 2 | 3 | var user = this; 4 | this.notify=''; 5 | this.show=true; 6 | this.showOtpForm=false; 7 | this.showPasswordResetForm=false; 8 | //clear the form after submit 9 | this.resetForm=()=>{ 10 | user.firstname=''; 11 | user.lastname=''; 12 | user.email=''; 13 | user.mnumber=''; 14 | user.password=''; 15 | }; 16 | //function to register user 17 | this.registerUser=()=>{ 18 | 19 | user.notify=''; 20 | var userData={ 21 | name : user.firstname+' '+user.lastname, 22 | email : user.email, 23 | mnumber : user.mnumber, 24 | password : user.password 25 | }; 26 | apiService.signUp(userData).then( function successCallback(response){ 27 | //console.log(response); 28 | user.notify=response.data.message; 29 | user.resetForm(); 30 | 31 | 32 | }, 33 | 34 | function errorCallback(response) { 35 | alert("some error occurred. Check the console."); 36 | console.log(response); 37 | }); 38 | 39 | 40 | }; 41 | //function to login user 42 | this.loginUser=()=>{ 43 | user.notify=''; 44 | var userData={ 45 | email : user.email, 46 | password : user.password 47 | 48 | }; 49 | apiService.login(userData).then( function successCallback(response){ 50 | //console.log(response); 51 | if(!response.data.error){ 52 | 53 | authService.setToken(response.data.token); 54 | $location.path('/dashboard'); 55 | } 56 | user.notify=response.data.message; 57 | }, 58 | 59 | function errorCallback(response) { 60 | alert("some error occurred. Check the console."); 61 | console.log(response); 62 | }); 63 | 64 | }; 65 | 66 | //function to send resetpassword request 67 | this.sendOtpToEmail=()=>{ 68 | user.notify=''; 69 | var userData={email:user.email}; 70 | 71 | apiService.forgotPasswordOtpSend(userData).then(function successCallback(response){ 72 | //console.log(response); 73 | if(!response.data.error){ 74 | user.showOtpForm=true; 75 | 76 | } 77 | 78 | user.notify=response.data.message; 79 | //userData.$setPristine(); 80 | 81 | }, 82 | function errorCallback(response) { 83 | alert("some error occurred. Check the console."); 84 | console.log(response); 85 | }); 86 | 87 | 88 | }; 89 | 90 | //function to verify otp 91 | this.verifyOtp=()=>{ 92 | var otp={otp:user.otp}; 93 | user.notify=''; 94 | apiService.verifySentOtp(otp).then(function successCallback(response){ 95 | if(!response.data.error){ 96 | user.show=false; 97 | user.showPasswordResetForm=true; 98 | } 99 | 100 | user.notify=response.data.message; 101 | //otp.$setPristine(); 102 | 103 | }, 104 | function errorCallback(response) { 105 | alert("some error occurred. Check the console."); 106 | console.log(response); 107 | }); 108 | 109 | 110 | }; 111 | 112 | //function to reset password 113 | this.resetPassword=()=>{ 114 | user.notify=''; 115 | var newPassword={password : user.password,cpassword:user.cpassword}; 116 | apiService.resetPassword(newPassword).then(function successCallback(response){ 117 | 118 | console.log(response); 119 | user.notify=response.data.message; 120 | //newPassword.$setPristine(); 121 | 122 | }, 123 | function errorCallback(response) { 124 | alert("some error occurred. Check the console."); 125 | console.log(response); 126 | }); 127 | 128 | 129 | }; 130 | 131 | 132 | }]); 133 | 134 | //Social login controller 135 | myApp.controller('socialLoginController',['$stateParams','authService','$location',function($stateParams,authService, $location){ 136 | //console.log($routeParams.token); 137 | authService.setToken($stateParams.token); 138 | $location.path('/dashboard'); 139 | }]); -------------------------------------------------------------------------------- /public/views/available-tests.html: -------------------------------------------------------------------------------- 1 | 2 |

Test Available

3 |
4 |
5 |
6 | 7 |
8 |

{{test.title}} 9 | {{test.time}} Min 10 |

11 |

{{test.description}}

12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 22 | 23 |
24 |
25 |
26 | 27 |

No Test Found (:

28 | 29 | 30 | 31 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /public/views/index.html: -------------------------------------------------------------------------------- 1 |
2 |

Hello,{{name}}

3 |
4 | 5 | 6 |
7 |
8 |
9 |
10 |
11 | 12 |
13 |
{{dashCtrl.testArrayLength}} Test Available!
14 |
15 | 16 | View Details 17 | 18 | 19 | 20 | 21 |
22 |
23 |
24 |
25 |
26 |
27 | 28 |
29 |
Performance Of Users
30 |
31 | 32 | View Details 33 | 34 | 35 | 36 | 37 |
38 |
39 |
40 |
41 |
42 | Registered Users 43 | 44 |
45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
#NameEmailMobileLogin Type
{{$index+1}}{{user.name}}{{user.email}}{{user.mobileNumber}}NALocalFacebookGoogle
70 |
71 |
72 |
73 |
74 |
75 | My Performance
76 |
77 |
78 |
79 |
No. Of Tests Given: {{dashCtrl.myfilteredPerformances.length}}
80 |
Best Score: {{dashCtrl.mybestPerformance*100}}%
81 |
Least Score: {{dashCtrl.myworstPerformance*100}}%
82 |
83 |
84 |
My Average Performance
85 |
86 | {{dashCtrl.myavgPerformance * 100}}% 87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |

No Tests Given !!!

96 |
97 |
98 |
99 | -------------------------------------------------------------------------------- /public/css/main.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | li{ 4 | list-style: none; 5 | } 6 | 7 | 8 | .d-flex{ 9 | margin-bottom: 0 !important; 10 | } 11 | .navbar-brand{ 12 | font-size: 28px; 13 | letter-spacing: 2px; 14 | font-family: 'Anton', sans-serif; 15 | 16 | } 17 | .text-dark{ 18 | font-size: 20px; 19 | 20 | } 21 | .text-dark:hover{ 22 | text-decoration: none; 23 | } 24 | /* Banner */ 25 | 26 | #banner { 27 | background-color: #00cdcf; 28 | color: #ffffff; 29 | padding: 8em 0 6em 0; 30 | text-align: center; 31 | min-height:70vh; 32 | } 33 | 34 | #banner h1, #banner h2, #banner h3, #banner h4, #banner h5, #banner h6 { 35 | color: #ffffff; 36 | } 37 | 38 | 39 | #banner h2 { 40 | border-style: double solid; 41 | border-width: 4px 1px; 42 | font-size: 2.4em; 43 | line-height: 1.35em; 44 | margin: 0 auto 1em; 45 | padding: 1.1em; 46 | position: relative; 47 | width: 40%; 48 | } 49 | 50 | #banner p { 51 | font-size: 1.5em; 52 | font-family: 'Courgette', cursive; 53 | } 54 | .btn-success{ 55 | background-color: #009a9c !important; 56 | padding:10px 20px 10px 20px; 57 | border-radius: 0; 58 | } 59 | @media screen and (max-width: 1680px) { 60 | 61 | #banner { 62 | padding: 8em 0 6em 0; 63 | } 64 | 65 | } 66 | 67 | @media screen and (max-width: 1280px) { 68 | 69 | #banner { 70 | padding: 6em 0 4em 0; 71 | } 72 | 73 | #banner h2 { 74 | width: 50%; 75 | } 76 | 77 | } 78 | 79 | @media screen and (max-width: 980px) { 80 | 81 | #banner { 82 | padding: 4em 2em 2em 2em; 83 | } 84 | 85 | #banner h2 { 86 | width: 75%; 87 | } 88 | 89 | } 90 | 91 | @media screen and (max-width: 736px) { 92 | 93 | #banner { 94 | padding: 3em 1.5em 1em 1.5em; 95 | } 96 | 97 | #banner h2 { 98 | font-size: 2em; 99 | } 100 | 101 | #banner p { 102 | font-size: 1.25em; 103 | } 104 | 105 | } 106 | 107 | @media screen and (max-width: 480px) { 108 | 109 | #banner { 110 | padding: 3em 1em 1em 1em; 111 | } 112 | 113 | #banner h2 { 114 | font-size: 1.65em; 115 | width: 100%; 116 | } 117 | 118 | } 119 | 120 | /* Main */ 121 | 122 | #main { 123 | padding: 4em 0 2em 0; 124 | } 125 | 126 | @media screen and (max-width: 736px) { 127 | 128 | #main { 129 | padding: 3em 0 1em 0; 130 | } 131 | 132 | } 133 | 134 | /* Footer */ 135 | 136 | #footer { 137 | background-color: #f7f7f7; 138 | color: #444; 139 | padding: 2em 0 1em 0; 140 | position: relative; 141 | } 142 | 143 | #footer a { 144 | color: #00cdcf; 145 | } 146 | 147 | 148 | 149 | #footer:before { 150 | background-color: #ddd; 151 | content: ''; 152 | height: 1px; 153 | left: 30%; 154 | position: absolute; 155 | top: 0; 156 | width: 40%; 157 | } 158 | 159 | #footer .copyright { 160 | color: #bbb; 161 | font-size: 0.9em; 162 | margin: 0 0 2em 0; 163 | padding: 0; 164 | text-align: center; 165 | text-transform: uppercase; 166 | } 167 | 168 | #footer .copyright li { 169 | border-left: solid 1px #e3e3e3; 170 | display: inline-block; 171 | list-style: none; 172 | margin-left: 1.5em; 173 | padding-left: 1.5em; 174 | } 175 | 176 | #footer .copyright li:first-child { 177 | border-left: 0; 178 | margin-left: 0; 179 | padding-left: 0; 180 | } 181 | 182 | @media screen and (max-width: 1280px) { 183 | 184 | #footer { 185 | padding: 4em 2em 3em 2em; 186 | } 187 | 188 | } 189 | 190 | @media screen and (max-width: 980px) { 191 | 192 | #footer { 193 | padding: 3em 2em 2em 2em; 194 | } 195 | 196 | #footer:before { 197 | width: 75%; 198 | left: 12.5%; 199 | } 200 | 201 | } 202 | 203 | @media screen and (max-width: 736px) { 204 | 205 | #footer { 206 | padding: 3em 2em 1em 2em; 207 | } 208 | 209 | #footer:before { 210 | width: 85%; 211 | left: 7.5%; 212 | } 213 | 214 | #footer .copyright li { 215 | display: block; 216 | border-left: 0; 217 | margin-left: 0; 218 | padding-left: 0; 219 | } 220 | 221 | } 222 | 223 | @media screen and (max-width: 480px) { 224 | 225 | #footer { 226 | padding: 2em 1em 0.1em 1em; 227 | } 228 | 229 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 2 | const express = require('express'); 3 | const app = express(); 4 | const http = require('http') 5 | const path = require('path'); 6 | const bodyParser = require('body-parser'); 7 | const mongoose = require('mongoose'); 8 | const session = require('express-session'); 9 | const passport = require('passport'); 10 | 11 | // fs module, by default module for file management in nodejs 12 | const fs = require('fs'); 13 | const config = require('./config/config.js'); 14 | app.set('port', process.env.PORT || 3000); 15 | /*app.use(cors({ 16 | 'allowedHeaders': ['sessionId', 'Content-Type'], 17 | 'exposedHeaders': ['sessionId'], 18 | 'origin': '*', 19 | 'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE', 20 | 'preflightContinue': false 21 | }));*/ 22 | /*app.options('*', function(req, res, next){ 23 | res.end(); 24 | }) 25 | */ 26 | //set the view folder for static files 27 | app.use(express.static(path.join(__dirname, '/public'))); 28 | //body-parser middleware 29 | app.use(bodyParser.json({limit:'10mb',extended:true})); 30 | app.use(bodyParser.urlencoded({limit:'10mb',extended:true})); 31 | 32 | //session middleware 33 | // initialization of session middleware 34 | 35 | 36 | 37 | 38 | //app.options('*', cors()); 39 | //enables cors 40 | 41 | 42 | 43 | mongoose.connect(config.db); 44 | const db = mongoose.connection; 45 | 46 | db.on('connected', () => { 47 | console.log("Conneted to db.."); 48 | }); 49 | //Passport 50 | app.use(passport.initialize()); 51 | app.use(passport.session()); 52 | app.use(session({ 53 | name :'myCustomCookie', 54 | secret: 'myAppSecret', // encryption key 55 | resave: true, 56 | httpOnly : true, 57 | saveUninitialized: true, 58 | cookie: { secure: false } 59 | })); 60 | 61 | require('./config/google-auth')(passport); 62 | require('./config/facebook-auth')(passport); 63 | // include all our model files 64 | fs.readdirSync('./app/models').forEach(function(file){ 65 | // check if the file is js or not 66 | if(file.indexOf('.js')) 67 | // if it is js then include the file from that folder into our express app using require 68 | require('./app/models/'+file); 69 | 70 | });// end for each 71 | 72 | // include controllers 73 | fs.readdirSync('./app/controllers').forEach(function(file){ 74 | if(file.indexOf('.js')){ 75 | // include a file as a route constiable 76 | const route = require('./app/controllers/'+file); 77 | //call controller function of each file and pass your app instance to it 78 | route.controller(app); 79 | 80 | } 81 | 82 | }); 83 | app.use(function(req, res) { 84 | res.sendFile('index.html', { root: __dirname + "/public" }); 85 | }); 86 | 87 | //router to handle any other page request 88 | app.route('*') 89 | 90 | .get((req,res,next)=>{ 91 | 92 | res.statusCode = 404; 93 | next("Path not found"); 94 | 95 | }) 96 | 97 | .post((req,res,next)=>{ 98 | 99 | res.statusCode = 404; 100 | next("Path not found"); 101 | }); 102 | 103 | //end for each 104 | //using the setLoggedInuser middleware as an application level middleware 105 | //so that it processes every request before responding 106 | // middleware to set request user(set new values to the session variable if any changes are made) and check which user is logged in 107 | //check if the user is a legitimate user 108 | 109 | 110 | //application level middleware for error handling of other page request 111 | app.use((err,req,res,next) =>{ 112 | 113 | console.log("this is error handling middleware"); 114 | 115 | if(res.statusCode==404) { 116 | const myResponse = responseGenerator.generate(true,"Page Not Found,Go Back To HomePage",404,null); 117 | res.render('error', { 118 | message: myResponse.message, 119 | status: myResponse.status 120 | }); 121 | } 122 | 123 | else { 124 | console.log(err); 125 | res.send(err); 126 | } 127 | 128 | }); 129 | 130 | app.use(function(err, req, res, next) { 131 | res.status(err.status || 500); 132 | res.render('error', { 133 | message: err.message, 134 | error: err 135 | }); 136 | }); 137 | 138 | const server = http.createServer(app); 139 | server.listen(app.get('port'),(req,res)=>{ 140 | console.log('App listening to port'+app.get('port')); 141 | }); 142 | -------------------------------------------------------------------------------- /public/angular/routes.js: -------------------------------------------------------------------------------- 1 | myApp.config(['$stateProvider','$urlRouterProvider','$locationProvider', function($stateProvider,$urlRouterProvider,$locationProvider) { 2 | 3 | 4 | $stateProvider 5 | .state('main',{ 6 | url:'/', 7 | templateUrl : 'views/main.html' 8 | 9 | }) 10 | .state('login',{ 11 | url :'/login', 12 | templateUrl : 'views/login.html', 13 | controller : 'userController as userCtrl', 14 | resolve: { 15 | "check": function ($location,authService) { 16 | if (authService.getToken()) { 17 | 18 | $location.path('/dashboard'); 19 | 20 | } else { 21 | $location.path('/login'); 22 | 23 | } 24 | } 25 | } 26 | 27 | }) 28 | 29 | .state('signup',{ 30 | url : '/signup', 31 | templateUrl : 'views/signup.html', 32 | controller : 'userController as userCtrl' 33 | 34 | }) 35 | .state('forgotpass',{ 36 | url : '/forgotpass', 37 | templateUrl : 'views/forgot-pass.html', 38 | controller : 'userController as userCtrl' 39 | 40 | }) 41 | .state('dashboard',{ 42 | url : '/dashboard', 43 | templateUrl : 'views/dashboard.html', 44 | controller : 'dashController as dashCtrl', 45 | resolve: { 46 | "check": function ($location,authService) { 47 | if (authService.getToken()) { 48 | 49 | $location.path('/dashboard/index'); 50 | 51 | } else { 52 | $location.path('/login'); 53 | 54 | } 55 | } 56 | } 57 | }) 58 | .state('dashboard.index',{ 59 | url : '/index', 60 | templateUrl : 'views/index.html', 61 | controller : 'dashController as dashCtrl' 62 | }) 63 | .state('dashboard.createtest',{ 64 | url : '/createtest', 65 | templateUrl : 'views/create-test.html', 66 | controller : 'testController as testCtrl' 67 | }) 68 | .state('dashboard.edittest',{ 69 | url : '/edittest/:tid', 70 | templateUrl : 'views/edit-test.html', 71 | controller : 'testController as testCtrl' 72 | }) 73 | .state('dashboard.quesoperation',{ 74 | url : '/quesops', 75 | templateUrl : 'views/question-operations.html', 76 | controller : 'testController as testCtrl' 77 | }) 78 | .state('dashboard.allusers',{ 79 | url : '/allusers/:tid', 80 | templateUrl : 'views/allusers.html', 81 | controller : 'testController as testCtrl' 82 | }) 83 | .state('dashboard.userperformances',{ 84 | url : '/userperformances', 85 | templateUrl : 'views/userperformances.html', 86 | controller : 'dashController as dashCtrl' 87 | }) 88 | .state('dashboard.livetest',{ 89 | url : '/livetest/:tid', 90 | templateUrl : 'views/live-test.html', 91 | controller : 'livetestController as liveCtrl' 92 | }) 93 | .state('dashboard.result',{ 94 | url : '/result/:tid', 95 | templateUrl : 'views/result.html', 96 | controller : 'resultsController as resultCtrl' 97 | }) 98 | .state('dashboard.tests',{ 99 | url : '/tests', 100 | templateUrl : 'views/available-tests.html', 101 | controller : 'testController as testCtrl', 102 | resolve: { 103 | "check": function ($location,authService) { 104 | if (authService.getToken()) { 105 | 106 | $location.path('/dashboard/tests'); 107 | 108 | } else { 109 | $location.path('/login'); 110 | 111 | } 112 | } 113 | } 114 | }) 115 | 116 | .state('facebook',{ 117 | url : '/facebook/:token', 118 | templateUrl : 'views/dashboard.html', 119 | controller : 'socialLoginController as socialCtrl' 120 | }) 121 | .state('google',{ 122 | url : '/google/:token', 123 | templateUrl : 'views/dashboard.html', 124 | controller : 'socialLoginController as socialCtrl' 125 | }) 126 | 127 | $urlRouterProvider.otherwise('/'); 128 | $locationProvider.html5Mode(true); 129 | 130 | 131 | }]); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExamGround, A Live Test Taking System 2 | 3 | https://limitless-stream-31809.herokuapp.com 4 | 5 | ## Assumption 6 | 7 | * Register with email as 'admin@examground.com', to get the admin access of the system. 8 | * For Heroku- 9 | * email: admin@examground.com 10 | * pass : rahul 11 | 12 | 13 | ## Project Description 14 | 15 | ``` 16 | A Multiple choice test taking system in which tests are tracked live and analytics are generated based on that. 17 | 18 | ``` 19 | 20 | ## Features 21 | 22 | 1) User Management System - 23 | 24 | a) Ability to sign up using email, gmail and facebook. 25 | b) Ability to login to the system through email and password combination or using Gmail/Facebook 26 | c) Forgot password functionality to reset password 27 | 28 | 2) User Testing management system - 29 | 30 | a) Once the user logs into the system, a dashboard containing the statistics of all tests he has taken is displayed. The statistics includes the number of tests taken, average score,best and the least score. 31 | b) A particular test can only be taken once by the user.The User will be apprised of which tests he/she had already taken. 32 | c) “Take Test” button is displayed, from which user can go to test taking page on clicking the button for a particular test. 33 | 34 | 3) User test taking system - 35 | 36 | a) Once user starts the test, he first see an instructions screen containing. It may also contain the rules of the test. 37 | b) Once the user reads the instructions and accepts the rules (single accept button), The test timer will start and the screen should display the test questions and options associated with it. 38 | c) User can be able to choose only one option as answer for every question. 39 | d) User can revisit the questions again with the help of the question pallette which apprises of the questions attempted by the user. 40 | e) The test will have a time limit. The test window must automatically close once the timeout occurs irrespective of how many questions have been answered. The system submits the answers automatically. 41 | f) If the user completes the test before the time ends, he can see a submit window which will submit his all answers. In case of timeouts, this window must appear automatically. 42 | g) The system keeps a track of how much time a user is taking for answering each question. 43 | h) On submission of test, result is shown to student. 44 | 45 | 4) Test listing Admin 46 | 47 | a) Admin can be able to create tests in the system 48 | b) Each test has a set of questions, each question containing at least 4 options and overall time limit of the test. 49 | c) Admin can create, edit, delete and view any tests, questions or option. 50 | d) While creating options for any question, admin is be able to set a correct answer. 51 | 52 | 5) User analytics in admin 53 | 54 | a) Admin can be able to view details of users registered in the system 55 | b) Admin can be able to view overall performance of the user in all his tests. 56 | c) Admin can be able to see all the users who have attempted the test. 57 | 58 | 6) Single Page Application 59 | 60 | ## Extra features 61 | 62 | 1) List of students who attempted the test is shown with their details,scores & ranks. 63 | 2) Chart js used to show pie chart. 64 | 3) Secured with JWT. Default JWT expiry time is set to 30 minutes. 65 | 4) Test statistics are also calculated. 66 | 67 | 68 | ## Getting Started 69 | 70 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. 71 | 72 | ### Prerequisites 73 | 74 | 1) Nodejs 75 | 2) Mongodb 76 | 3) NPM 77 | 4) Git 78 | 79 | ### Installing 80 | 81 | Setting Prerequisites 82 | 83 | ``` 84 | 1) Start mongodb by running mongod 85 | 86 | ``` 87 | 88 | Setting up the local server 89 | 90 | ``` 91 | 1) Clone the repository from https://github.com/rahuljhawar/ExamGround 92 | 2) Open terminal and change its location the where you cloned the repo 93 | 3) Run command npm install 94 | 4) After all dependencies are installed. Run command : node app.js, in your terminal 95 | 5) let the server start 96 | ``` 97 | 98 | Getting started 99 | 100 | ``` 101 | 1) Visit http://localhost:3000 on your browser 102 | 2) Select signup to create a new account 103 | 104 | ``` 105 | 106 | ## Built With 107 | 108 | * Angular Js 109 | * Bootstrap 110 | * Node Js 111 | * Postman 112 | * VS Code Editor 113 | 114 | 115 | ## Authors 116 | 117 | * **Rahul Jhawar** 118 | -------------------------------------------------------------------------------- /public/angular/controllers/dashController.js: -------------------------------------------------------------------------------- 1 | myApp.controller('dashController',['$http','$q','apiService','authService','$rootScope','$location','$filter',function($http,$q,apiService,authService,$rootScope,$location,$filter){ 2 | var dash = this; 3 | 4 | //check whether the user is logged in 5 | this.loggedIn=()=>{ 6 | if(authService.isLoggedIn()){ 7 | return 1; 8 | 9 | } 10 | else{ 11 | return 0; 12 | } 13 | }; 14 | //getting the user detail and checking whether the user is admin or not 15 | this.getUserDetail=()=>{ 16 | if(authService.isLoggedIn()){ 17 | apiService.getUser().then(function successCallBack(response){ 18 | if(response.data.error){ 19 | alert("Authentication failed! Token Expired"); 20 | dash.logout(); 21 | 22 | }else{ 23 | //console.log(response); 24 | $rootScope.name=response.data.name; 25 | $rootScope.email=response.data.email; 26 | if(response.data.email === 'admin@examground.com'){ 27 | $rootScope.admin = true; 28 | 29 | }else{ 30 | $rootScope.admin = false; 31 | 32 | } 33 | 34 | 35 | } 36 | }); 37 | } 38 | }; 39 | 40 | //get the no. of tests 41 | 42 | this.getNoOfTests = ()=>{ 43 | apiService.getTests().then(function successCallBack(response){ 44 | //console.log(response); 45 | //store the no. of available test 46 | dash.testArrayLength = response.data.data.length; 47 | 48 | 49 | }, 50 | function errorCallback(response) { 51 | alert("some error occurred. Check the console."); 52 | console.log(response); 53 | }); 54 | 55 | }; 56 | 57 | //get the no of questions in a particular test 58 | this.noOfQuestions = (testid)=>{ 59 | test.testid=parseInt(testid); 60 | test.questions = []; 61 | apiService.viewQuestions(testid).then(function successCallBack(response){ 62 | //console.log(response); 63 | response.data.data.forEach(function(q){ 64 | test.questions.push(q); 65 | 66 | }); 67 | 68 | }, 69 | function errorCallback(response) { 70 | alert("some error occurred. Check the console."); 71 | console.log(response); 72 | }); 73 | 74 | }; 75 | 76 | //get all the users to display in the admin panel 77 | 78 | this.allusers = ()=>{ 79 | 80 | $q.all([ 81 | apiService.getLocalUsers(), 82 | apiService.getSocialUsers() 83 | ]).then(function(users) { 84 | var localusers=users[0].data.data; 85 | var socialusers=users[1].data.data; 86 | dash.alluser=localusers.concat(socialusers); 87 | }); 88 | 89 | }; 90 | 91 | // get my overall performance for all the test 92 | this.myPerformance=()=>{ 93 | apiService.getallperformances().then(function successCallBack(response){ 94 | 95 | var myperformances = response.data.data; 96 | // filtering my performances from all the performances 97 | dash.myfilteredPerformances=$filter('filter')(myperformances,{userEmail:$rootScope.email}); 98 | // Counting percentages for each test 99 | var mytestPercentages = dash.myfilteredPerformances.map(a => ((a.score/(a.questionCount * 2)).toFixed(2))/1 ); 100 | var total=0; 101 | //finding the average of all the test 102 | for(let i = 0; i < mytestPercentages.length; i++) { 103 | total += mytestPercentages[i]; 104 | } 105 | 106 | dash.myavgPerformance = (total / mytestPercentages.length).toFixed(2); 107 | dash.mybestPerformance = Math.max.apply(Math, mytestPercentages); 108 | dash.myworstPerformance = Math.min.apply(Math, mytestPercentages); 109 | }, 110 | function errorCallback(response) { 111 | alert("some error occurred. Check the console."); 112 | console.log(response); 113 | }); 114 | }; 115 | 116 | //getting overall performances of all the users for admin panel 117 | this.userPerformances=(email)=>{ 118 | 119 | apiService.getallperformances().then(function successCallBack(response){ 120 | 121 | var performances = response.data.data; 122 | 123 | dash.filteredPerformances=$filter('filter')(performances,{userEmail:email}); 124 | 125 | var testPercentages = dash.filteredPerformances.map(a => ((a.score/(a.questionCount * 2)).toFixed(2))/1 ); 126 | var total=0; 127 | for(let i = 0; i < testPercentages.length; i++) { 128 | total += testPercentages[i]; 129 | } 130 | 131 | dash.avgPerformance = (total / testPercentages.length).toFixed(2); 132 | dash.bestPerformance = Math.max.apply(Math, testPercentages); 133 | dash.worstPerformance = Math.min.apply(Math, testPercentages); 134 | 135 | $('#performanceModal').modal('show'); 136 | }, 137 | function errorCallback(response) { 138 | alert("some error occurred. Check the console."); 139 | console.log(response); 140 | }); 141 | }; 142 | 143 | //function to logout the user 144 | this.logout=()=>{ 145 | //clear the local storage 146 | delete $rootScope.admin; 147 | delete $rootScope.name; 148 | authService.setToken(); 149 | $location.path('/login'); 150 | } 151 | 152 | 153 | }]); -------------------------------------------------------------------------------- /public/angular/services/queryService.js: -------------------------------------------------------------------------------- 1 | myApp.factory('apiService',($http,authService,$window, $q)=>{ 2 | let requests={}; 3 | 4 | const baseUrl = "http://localhost:3000"; 5 | 6 | 7 | //sign up request 8 | requests.signUp = (userData) =>{ 9 | return $http.post('/signup', userData); 10 | } 11 | 12 | //login requests 13 | requests.login=(loginData)=>{ 14 | return $http.post('/login' , loginData); 15 | }; 16 | 17 | 18 | 19 | //get logged in user 20 | requests.getUser = ()=>{ 21 | if(authService.getToken()){ 22 | return $http.get('/user/currentUser?token='+authService.getToken() , null); 23 | }else{ 24 | return $q.reject({data:"User not authorized..."}); 25 | } 26 | } 27 | 28 | //get all local users 29 | requests.getLocalUsers=()=>{ 30 | return $http.get('/user/allusers?token='+authService.getToken() , null); 31 | } 32 | //get all social users 33 | requests.getSocialUsers=()=>{ 34 | return $http.get('/user/allsocialusers?token='+authService.getToken() , null); 35 | } 36 | 37 | //reset password requests 38 | 39 | requests.forgotPasswordOtpSend=(userData)=>{ 40 | return $http.post('/forgotpassword' , userData); 41 | }; 42 | 43 | requests.verifySentOtp=(otp)=>{ 44 | return $http.post('/verifyotp' , otp); 45 | }; 46 | 47 | requests.resetPassword=(newPassword)=>{ 48 | return $http.post('/resetpassword' , newPassword); 49 | }; 50 | 51 | // request to Create a test by admin 52 | requests.createTest = (data) =>{ 53 | return $http.post('/user/admin/createTest?token=' + authService.getToken(), data); 54 | } 55 | // request to get all the test 56 | requests.getTests = () =>{ 57 | return $http.get('/user/allTests?token=' + authService.getToken()); 58 | } 59 | 60 | // request to get a single test 61 | requests.viewTest = (tid) =>{ 62 | return $http.get('/user/test/'+tid+'?token=' + authService.getToken()); 63 | } 64 | 65 | // request to Create a Question by Admin 66 | requests.createQuestion = (data) =>{ 67 | console.log(data); 68 | return $http.post('/user/test/'+data.id+'/addQuestion?token=' + authService.getToken(), data); 69 | } 70 | 71 | // request to delete a Test by Admin 72 | requests.deleteTest = (data) =>{ 73 | // console.log(data); 74 | return $http.post('/user/test/delete/' + data + '?token=' + authService.getToken()); 75 | } 76 | 77 | // request to get Questions 78 | requests.viewQuestions = (data) =>{ 79 | //console.log(data); 80 | return $http.get('/user/test/' + data + '/getQuestions?token=' + authService.getToken()); 81 | } 82 | 83 | // request to Update test details by Admin 84 | requests.updateTest = (data)=> { 85 | //console.log(data); 86 | return $http.put('/user/test/edit/' + data.id + '?token=' + authService.getToken(), data); 87 | } 88 | 89 | // request to delete question by Admin 90 | requests.deleteQuestion = (tid, qid)=> { 91 | return $http.post('/user/deleteQuestion/' + tid + '/' + qid + '?token=' + authService.getToken()); 92 | } 93 | 94 | // request to Update question by Admin 95 | requests.updateQuestion = (data, qid)=> { 96 | return $http.put('/user/test/editQuestion/' + qid + '?token=' + authService.getToken(), data); 97 | } 98 | 99 | //request to get questions for single test 100 | requests.getQuestionDetail = (tid,qid) =>{ 101 | //console.log(singleTestId); 102 | return $http.get('/user/test/getQuestion/' + tid + '/' + qid + '?token=' + authService.getToken()); 103 | } 104 | 105 | //requests to submit answer for single test 106 | requests.submitAnswer = (data)=> { 107 | return $http.post('/test/' + data.testid + '/' + data.questionid + '/userAnswer?token=' + authService.getToken(), data); 108 | } 109 | 110 | 111 | //requests to get questions for single test 112 | requests.submitTest = (data) =>{ 113 | return $http.post('/user/addPerformance/?token=' + authService.getToken(), data); 114 | } 115 | 116 | //requests to post test attempted by 117 | requests.testAttempted = (attemptdata) =>{ 118 | return $http.post('/user/tests/' + attemptdata.testId + '/attemptedby?token=' + authService.getToken(), attemptdata); 119 | } 120 | //requests to get the entire performances 121 | requests.getallperformances = () =>{ 122 | return $http.get('/user/all/performances/?token=' + authService.getToken()); 123 | } 124 | 125 | 126 | //requests to get performance of a user in a particular test 127 | requests.getusertestdetails = (tid) =>{ 128 | return $http.get('/user/performance/' + tid + '?token=' + authService.getToken()); 129 | } 130 | 131 | //requests to get performance of all user in a particular test 132 | requests.getallusertestdetails = (tid) =>{ 133 | return $http.get('/user/all/performance/' + tid + '?token=' + authService.getToken()); 134 | } 135 | 136 | return requests; 137 | 138 | });//end query service -------------------------------------------------------------------------------- /public/views/live-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | Time Left: {{mminutes}} minute{{minutesS}}, {{sseconds}} second{{secondsS}} 6 |
7 | 8 |
{{liveCtrl.currentPage+1}}. {{question.question}}
9 | 10 |
    11 |
  • 12 | 14 |
  • 15 |
  • 16 | 18 |
  • 19 |
  • 20 | 22 |
  • 23 |
  • 24 | 26 |
  • 27 |
28 |
29 | 30 | 31 | 32 | {{liveCtrl.currentPage+1}} of {{liveCtrl.questions.length}} 33 | 34 | 35 |
36 |
37 | 38 | 39 |
40 |

Question Pallette

41 |
42 |
43 |
44 | 49 | 50 |
51 |  Attempted 52 |  Unanswered 53 |
54 |
55 |
56 |
57 |
58 | 59 | 60 | 74 | 75 | 76 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /public/angular/controllers/livetestController.js: -------------------------------------------------------------------------------- 1 | myApp.controller('livetestController',['$http','$timeout','$scope','$stateParams','$filter','apiService','authService','$rootScope','$location','$state',function($http,$timeout,$scope,$stateParams,$filter,apiService,authService,$rootScope,$location,$state){ 2 | var live=this; 3 | this.score=0; 4 | this.wrongAns=0; 5 | this.skipped=0; 6 | this.useroptions=[]; 7 | this.testid=$stateParams.tid; 8 | 9 | 10 | this.currentPage = 0; 11 | this.pageSize = 1; 12 | this.numberOfPages=Math.ceil(live.questions.length/live.pageSize); 13 | 14 | //get the user detail 15 | this.getUserDetail=()=>{ 16 | apiService.getUser().then(function successCallBack(response){ 17 | live.email=response.data.email; 18 | }); 19 | }; 20 | this.getUserDetail(); 21 | 22 | //get the timing of a particular test 23 | this.getTestTime=()=>{ 24 | apiService.viewTest(live.testid).then(function successCallBack(response){ 25 | live.testtime=response.data.data.time; 26 | }); 27 | }; 28 | this.getTestTime(live.testid); 29 | 30 | //get all the live questionss 31 | this.getLiveQuestions=()=>{ 32 | 33 | live.questions = []; 34 | apiService.viewQuestions(live.testid).then(function successCallBack(response){ 35 | //console.log(response); 36 | response.data.data.forEach(function(q){ 37 | live.questions.push(q); 38 | }); 39 | }, 40 | function errorCallback(response) { 41 | alert("some error occurred. Check the console."); 42 | console.log(response); 43 | }); 44 | }; 45 | this.getLiveQuestions(); 46 | 47 | 48 | // push the user performance data to the attemptedBy array in test model 49 | this.pushToAttemptedUsers=()=>{ 50 | var data = { 51 | testId:live.testid, 52 | email:live.email, 53 | score:live.score 54 | } 55 | apiService.testAttempted(data).then(function(response){ 56 | $state.go('dashboard.result', {tid: live.testid}) ; 57 | }); 58 | }; 59 | //go to a particular question 60 | this.gotoQues=(qno)=>{ 61 | live.currentPage=qno; 62 | } 63 | //store the user answer into an array before submitting 64 | this.answers=(qno,option,qid)=>{ 65 | live.useroptions[qno]={ 66 | qid:qid, 67 | option:option 68 | 69 | } 70 | }; 71 | ///Configuration for dispalying questions one by one 72 | this.currentPage = 0; 73 | this.pageSize = 1; 74 | this.numberOfPages=Math.ceil(live.questions.length/live.pageSize); 75 | 76 | //show a modal 77 | this.showModal=()=>{ 78 | $('#alertModal').modal('show'); 79 | }; 80 | 81 | //submit the questions answers given by the user 82 | this.submitAnswers=()=>{ 83 | 84 | var performanceInfo={ 85 | email:live.email, 86 | testid:live.testid, 87 | score:live.score, 88 | noOfQuestions:live.questions.length, 89 | timetaken:live.timetaken, 90 | totalCorrectAnswers:live.correctAns, 91 | totalSkipped:live.skipped 92 | } 93 | 94 | apiService.submitTest(performanceInfo).then(function successCallBack(response){ 95 | live.pushToAttemptedUsers(); 96 | }, 97 | function errorCallback(response) { 98 | alert("some error occurred. Check the console."); 99 | console.log(response); 100 | }); 101 | 102 | //console.log(live.useroptions); 103 | 104 | }; 105 | 106 | 107 | // calculating the performance of the test based on the answers provided by the user 108 | // checking the useranswers in the useroptions array with the answers in the db 109 | this.calculatePerformance=()=>{ 110 | console.log("calculatePerformance"); 111 | for(i=0;i0&&t.length===r.length)}function i(t,r){return t+r}function u(r,e,a){var n={point:void 0,points:void 0};return function(o){var c=r.chart.getElementAtEvent||r.chart.getPointAtEvent,i=r.chart.getElementsAtEvent||r.chart.getPointsAtEvent;if(i){var u=i.call(r.chart,o),l=c?c.call(r.chart,o)[0]:void 0;a!==!1&&(t.equals(n.points,u)||t.equals(n.point,l))||(n.point=l,n.points=u,r[e](u,o,l))}}}function l(a,n){for(var o=t.copy(n.chartColors||e.getOptions(a).chartColors||r.defaults.global.colors),c=o.length>16&255,a=r>>8&255,n=255&r;return[e,a,n]}function v(t){var r=t.match(/^rgba?\(([\d,.]+)\)$/);if(!r)throw new Error("Cannot parse rgb value");return t=r[1].split(","),t.map(Number)}function C(t){return t.chartData&&t.chartData.length}function y(t){return"function"==typeof t.chartGetColor?t.chartGetColor:s}function b(t,r){var e=l(t,r);return Array.isArray(r.chartData[0])?m(r.chartLabels,r.chartData,r.chartSeries||[],e,r.chartDatasetOverride):w(r.chartLabels,r.chartData,e,r.chartDatasetOverride)}function m(r,e,a,n,o){return{labels:r,datasets:e.map(function(r,e){var c=t.extend({},n[e],{label:a[e],data:r});return o&&o.length>=e&&t.merge(c,o[e]),c})}}function w(r,e,a,n){var o={labels:r,datasets:[{data:e,backgroundColor:a.map(function(t){return t.pointBackgroundColor}),hoverBackgroundColor:a.map(function(t){return t.backgroundColor})}]};return n&&t.merge(o.datasets[0],n),o}function D(r,a){return t.extend({},e.getOptions(r),a.chartOptions)}function A(r,e){r.onclick=e.chartClick?u(e,"chartClick",!1):t.noop,r.onmousemove=e.chartHover?u(e,"chartHover",!0):t.noop}function B(t,r){Array.isArray(r.chartData[0])?r.chart.data.datasets.forEach(function(r,e){r.data=t[e]}):r.chart.data.datasets[0].data=t,r.chart.update(),r.$emit("chart-update",r.chart)}function $(t){return!t||Array.isArray(t)&&!t.length||"object"==typeof t&&!Object.keys(t).length}function k(t,r,e,n){return!n.responsive||0!==e[0].clientHeight||(a(function(){o(t,r,e)},50,!1),!1)}function F(t){t.chart&&(t.chart.destroy(),t.$emit("chart-destroy",t.chart))}return function(r){return{restrict:"CA",scope:{chartGetColor:"=?",chartType:"=",chartData:"=?",chartLabels:"=?",chartOptions:"=?",chartSeries:"=?",chartColors:"=?",chartClick:"=?",chartHover:"=?",chartDatasetOverride:"=?"},link:function(e,a){function i(t,n){if(!t||!t.length||Array.isArray(t[0])&&!t[0].length)return void F(e);var i=r||e.chartType;if(i)return e.chart&&c(t,n)?B(t,e):void o(i,e,a)}function u(n,c){if(!$(n)&&!t.equals(n,c)){var i=r||e.chartType;i&&o(i,e,a)}}function l(r,n){$(r)||t.equals(r,n)||o(r,e,a)}n&&window.G_vmlCanvasManager.initElement(a[0]),e.$watch("chartData",i,!0),e.$watch("chartSeries",u,!0),e.$watch("chartLabels",u,!0),e.$watch("chartOptions",u,!0),e.$watch("chartColors",u,!0),e.$watch("chartDatasetOverride",u,!0),e.$watch("chartType",l,!1),e.$on("$destroy",function(){F(e)}),e.$on("$resize",function(){e.chart&&e.chart.resize()})}}}}r.defaults.global.multiTooltipTemplate="<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value %>",r.defaults.global.tooltips.mode="label",r.defaults.global.elements.line.borderWidth=2,r.defaults.global.elements.rectangle.borderWidth=2,r.defaults.global.legend.display=!1,r.defaults.global.colors=["#97BBCD","#DCDCDC","#F7464A","#46BFBD","#FDB45C","#949FB1","#4D5360"];var n="object"==typeof window.G_vmlCanvasManager&&null!==window.G_vmlCanvasManager&&"function"==typeof window.G_vmlCanvasManager.initElement;return n&&(r.defaults.global.animation=!1),t.module("chart.js",[]).provider("ChartJs",e).factory("ChartJsFactory",["ChartJs","$timeout",a]).directive("chartBase",["ChartJsFactory",function(t){return new t}]).directive("chartLine",["ChartJsFactory",function(t){return new t("line")}]).directive("chartBar",["ChartJsFactory",function(t){return new t("bar")}]).directive("chartHorizontalBar",["ChartJsFactory",function(t){return new t("horizontalBar")}]).directive("chartRadar",["ChartJsFactory",function(t){return new t("radar")}]).directive("chartDoughnut",["ChartJsFactory",function(t){return new t("doughnut")}]).directive("chartPie",["ChartJsFactory",function(t){return new t("pie")}]).directive("chartPolarArea",["ChartJsFactory",function(t){return new t("polarArea")}]).directive("chartBubble",["ChartJsFactory",function(t){return new t("bubble")}]).name}); 11 | //# sourceMappingURL=angular-chart.min.js.map 12 | -------------------------------------------------------------------------------- /public/angular/controllers/testController.js: -------------------------------------------------------------------------------- 1 | myApp.controller('testController',['$http','$window','$stateParams','$filter','apiService','authService','$rootScope','$location','$state',function($http,$window,$stateParams,$filter,apiService,authService,$rootScope,$location,$state){ 2 | 3 | var test = this; 4 | this.tests=[]; 5 | 6 | // to reset the form after submission 7 | this.resetForm=()=>{ 8 | test.testid='', 9 | test.question='', 10 | test.optionA='', 11 | test.optionB='', 12 | test.optionC='', 13 | test.optionD='', 14 | test.answer='' 15 | }; 16 | 17 | // to show test time in instruction modal 18 | this.showTestTimeInModal=(tid)=>{ 19 | //as well as make them global so that it can be used by the livetestcontroller 20 | test.testtime=$window.time; 21 | $rootScope.testid=tid; 22 | $('#instructionModal').modal('show'); 23 | }; 24 | 25 | // directing to the run test page 26 | this.runTest = ()=>{ 27 | $location.path('/dashboard/livetest/'+$rootScope.testid); 28 | } 29 | 30 | // function to create the test 31 | this.createTest=()=>{ 32 | test.notify=''; 33 | var testData={ 34 | title : test.testName, 35 | description : test.testDesc, 36 | time : test.testTime, 37 | instructions : test.testInstructions 38 | }; 39 | apiService.createTest(testData).then( function successCallback(response){ 40 | //console.log(response); 41 | alert(response.data.message); 42 | $location.path('/dashboard/tests'); 43 | 44 | }, 45 | 46 | function errorCallback(response) { 47 | alert("some error occurred. Check the console."); 48 | console.log(response); 49 | }); 50 | 51 | }; 52 | 53 | //get the testid from the parameter 54 | var tid=$stateParams.tid; 55 | 56 | // function to edit test 57 | this.editTest=()=>{ 58 | apiService.viewTest(tid).then( function successCallback(response){ 59 | console.log(response); 60 | test.testName=response.data.data.title; 61 | test.testDesc=response.data.data.description; 62 | test.testTime=response.data.data.time; 63 | test.testInstructions=response.data.data.instructions; 64 | 65 | 66 | }); 67 | }; 68 | //function to submit the edited test 69 | this.submitEditedTest=()=>{ 70 | //console.log(tid); 71 | var testData={ 72 | id : tid, 73 | title : test.testName, 74 | description : test.testDesc, 75 | time : test.testTime, 76 | instructions : test.testInstructions 77 | }; 78 | 79 | apiService.updateTest(testData).then( function successCallback(response){ 80 | //console.log(response); 81 | alert(response.data.message); 82 | $location.path('/dashboard/tests'); 83 | 84 | }, 85 | 86 | function errorCallback(response) { 87 | alert("some error occurred. Check the console."); 88 | console.log(response); 89 | }); 90 | } 91 | 92 | //function to delete the test 93 | this.deleteTest=(testid)=>{ 94 | if(confirm("Are You Sure you want to delete the test?")){ 95 | apiService.deleteTest(testid).then( function successCallback(response){ 96 | alert(response.data.message); 97 | $state.reload(); 98 | }, 99 | function errorCallback(response) { 100 | alert("some error occurred. Check the console."); 101 | console.log(response); 102 | }); 103 | } 104 | }; 105 | 106 | 107 | //get the available tests 108 | 109 | this.viewTests = ()=>{ 110 | apiService.getTests().then(function successCallBack(response){ 111 | //console.log(response); 112 | response.data.data.forEach(function(t){ 113 | test.tests.push(t); 114 | 115 | }); 116 | test.testArrayLength = test.tests.length; 117 | 118 | }, 119 | function errorCallback(response) { 120 | alert("some error occurred. Check the console."); 121 | console.log(response); 122 | }); 123 | 124 | }; 125 | this.viewTests(); 126 | 127 | // function to create question 128 | this.createQuestion=()=>{ 129 | test.notify=''; 130 | var questionData={ 131 | id : test.testid, 132 | question: test.question, 133 | optionA : test.optionA, 134 | optionB : test.optionB, 135 | optionC : test.optionC, 136 | optionD : test.optionD, 137 | answer : test.answer 138 | }; 139 | apiService.createQuestion(questionData).then( function successCallback(response){ 140 | //console.log(response); 141 | alert(response.data.message); 142 | test.resetForm(); 143 | 144 | }, 145 | 146 | function errorCallback(response) { 147 | alert("some error occurred. Check the console."); 148 | console.log(response); 149 | }); 150 | 151 | }; 152 | 153 | //function to view questions present in the test 154 | this.viewQuestions = (testid)=>{ 155 | test.testid=parseInt(testid); 156 | test.questions = []; 157 | apiService.viewQuestions(testid).then(function successCallBack(response){ 158 | //console.log(response); 159 | response.data.data.forEach(function(q){ 160 | test.questions.push(q); 161 | 162 | }); 163 | 164 | }, 165 | function errorCallback(response) { 166 | alert("some error occurred. Check the console."); 167 | console.log(response); 168 | }); 169 | 170 | }; 171 | 172 | //function to delete a question 173 | this.deleteQuestion=(qid)=>{ 174 | 175 | if(confirm("Are You Sure you want to delete the question?")){ 176 | apiService.deleteQuestion(test.testid,qid).then( function successCallback(response){ 177 | alert(response.data.message); 178 | test.viewQuestions(test.testid); 179 | }, 180 | function errorCallback(response) { 181 | alert("some error occurred. Check the console."); 182 | console.log(response); 183 | }); 184 | } 185 | }; 186 | 187 | // function to edit a question 188 | this.editQuestion=(qid)=>{ 189 | apiService.viewQuestions(test.testid).then( function successCallback(response){ 190 | //console.log(response); 191 | var obj = $filter('filter')(response.data.data, {_id: qid}, true)[0]; 192 | test.questionid=obj._id; 193 | test.question=obj.question; 194 | test.optionA =obj.optionA; 195 | test.optionB =obj.optionB; 196 | test.optionC =obj.optionC; 197 | test.optionD =obj.optionD; 198 | test.answer =obj.answer; 199 | 200 | 201 | }); 202 | }; 203 | 204 | // function to submit editted question 205 | this.submitEditedQuestion=()=>{ 206 | test.notify=''; 207 | var questionData={ 208 | question: test.question, 209 | optionA : test.optionA, 210 | optionB : test.optionB, 211 | optionC : test.optionC, 212 | optionD : test.optionD, 213 | answer : test.answer 214 | }; 215 | apiService.updateQuestion(questionData,test.questionid).then( function successCallback(response){ 216 | //console.log(response); 217 | alert(response.data.message); 218 | 219 | }, 220 | 221 | function errorCallback(response) { 222 | alert("some error occurred. Check the console."); 223 | console.log(response); 224 | }); 225 | }; 226 | //function to get the users who attempted the test 227 | this.enrolledUsers=()=>{ 228 | test.loading = true; 229 | apiService.getallusertestdetails(tid).then( function successCallback(response){ 230 | test.testattemptedBy = response.data.data; 231 | //console.log(test.testattemptedBy); 232 | test.loading = false; 233 | }, 234 | 235 | function errorCallback(response) { 236 | alert("some error occurred. Check the console."); 237 | console.log(response); 238 | }); 239 | }; 240 | 241 | }]); -------------------------------------------------------------------------------- /app/controllers/user.controller.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const express = require('express'); 3 | const jwt = require('jsonwebtoken'); 4 | const bcrypt = require('bcrypt'); 5 | const events = require('events'); 6 | const nodemailer = require('nodemailer'); 7 | const passport = require('passport'); 8 | // express userRouter used to define routes 9 | const userRouter = express.Router(); 10 | const userModel = mongoose.model('User'); 11 | //libraries and middlewares 12 | const config = require('./../../config/config.js'); 13 | const responseGenerator = require('./../../libs/responseGenerator'); 14 | const auth = require("./../../middlewares/auth"); 15 | const eventEmitter = new events.EventEmitter(); 16 | const randomstring = require("randomstring"); 17 | 18 | eventEmitter.on('forgot-pass',(data)=>{ 19 | let transporter = nodemailer.createTransport({ 20 | service: 'gmail', 21 | auth: { 22 | user: config.username, 23 | pass: config.pass 24 | } 25 | }); 26 | let mailOptions = { 27 | from: 'ExamGround ', // sender address 28 | to: data.email, // receivers email 29 | subject: 'Forgot Password', // Subject line 30 | html: `

OTP - ${data.otp} 31 |

` // plain text body 32 | }; 33 | 34 | transporter.sendMail(mailOptions, (err, info)=> { 35 | if (err) 36 | console.log(err); 37 | else 38 | console.log("Mail successfully sent" + info); 39 | }); 40 | }); 41 | 42 | 43 | 44 | 45 | module.exports.controller=(app)=>{ 46 | 47 | 48 | //userRouter for user signup 49 | userRouter.post('/signup',(req,res)=>{ 50 | userModel.findOne({'email': req.body.email}, (err, result)=> { 51 | if (err) { 52 | const myResponse = responseGenerator.generate(true, "Error registering your profile.Please try again!", 500, null); 53 | res.send(myResponse); 54 | } else if (result) { 55 | const myResponse = responseGenerator.generate(true, "User already exists", 409, null); 56 | //console.log(result); 57 | res.send(myResponse); 58 | } 59 | else{ 60 | 61 | 62 | const newUser = new userModel({ 63 | name : req.body.name, 64 | email : req.body.email, 65 | mobileNumber : req.body.mnumber, 66 | password : req.body.password 67 | 68 | });// end new user 69 | 70 | //hashing the password using bcrypt 71 | bcrypt.genSalt(10, (err, salt)=> { 72 | bcrypt.hash(newUser.password, salt, (err, hash)=> { 73 | newUser.password = hash; 74 | newUser.save(function (err) { 75 | if (err) { 76 | 77 | const response = responseGenerator.generate(true, "Error registering your profile.Please try again!", 500, null); 78 | res.send(response); 79 | } else { 80 | 81 | const response = responseGenerator.generate(false, "Account created successfully! Now you can Login!!", 200, null); 82 | res.send(response); 83 | } 84 | }); 85 | }); 86 | }); 87 | } 88 | 89 | });//end signup route 90 | }); 91 | //route for login with jwt token encapsulation 92 | userRouter.post('/login',(req,res)=>{ 93 | userModel.findOne({email:req.body.email},(err,foundUser)=>{ 94 | if(foundUser){ 95 | bcrypt.compare(req.body.password, foundUser.password, (err, isMatch) =>{ 96 | if(err) throw err; 97 | 98 | else if(isMatch){ 99 | 100 | let payload = foundUser.toObject(); 101 | delete payload.password; 102 | let token=jwt.sign(payload, config.jwtsecret, { expiresIn: '2h' }); 103 | res.json({ 104 | error:false, 105 | token:token 106 | }); 107 | 108 | } 109 | 110 | else{ 111 | const myResponse = responseGenerator.generate(true,"Incorrect password",500,null); 112 | res.send(myResponse); 113 | } 114 | }); 115 | 116 | }else if(foundUser==null || foundUser==undefined || foundUser.email==undefined){ 117 | const myResponse = responseGenerator.generate(true,"User does not exist!",404,null); 118 | res.send(myResponse); 119 | }else{ 120 | 121 | const myResponse = responseGenerator.generate(true,"Error logging you in! Please try again."+err,500,null); 122 | res.send(myResponse); 123 | } 124 | 125 | 126 | }); 127 | 128 | 129 | 130 | 131 | 132 | });//end of login route 133 | 134 | //route to send otp 135 | userRouter.post('/forgotpassword',(req,res)=>{ 136 | 137 | userModel.findOne({email:req.body.email},(err,foundUser)=>{ 138 | if(err){ 139 | throw err; 140 | }else if(foundUser == null){ 141 | const myResponse = responseGenerator.generate(true,"Email not registered!",404,null); 142 | res.send(myResponse); 143 | }else{ 144 | req.session.otp=randomstring.generate({ length: 6,charset: 'numeric '}); 145 | req.session.email = foundUser.email; 146 | console.log(req.session.email); 147 | eventEmitter.emit('forgot-pass', {email:req.session.email,otp:req.session.otp}); 148 | const myResponse = responseGenerator.generate(false,"OTP sent to the registerd email",200,req.session.otp); 149 | res.send(myResponse); 150 | } 151 | }); 152 | 153 | });//end otp route 154 | 155 | //route to verify otp sent in the mail 156 | userRouter.post('/verifyotp',(req,res)=>{ 157 | if(req.body.otp === req.session.otp){ 158 | console.log('otp verified'); 159 | const myResponse = responseGenerator.generate(false,"Otp verified",200,null); 160 | res.send(myResponse); 161 | }else{ 162 | console.log('otp doesnt match'); 163 | const myResponse = responseGenerator.generate(true,"Otp does not match!",400,null); 164 | res.send(myResponse); 165 | 166 | } 167 | });// end otp verification route 168 | 169 | //route to reset the password 170 | userRouter.post('/resetpassword',(req,res)=>{ 171 | console.log(req.session.email); 172 | if(req.body.password === req.body.cpassword){ 173 | let password = req.body.password; 174 | bcrypt.genSalt(10, (err, salt)=> { 175 | bcrypt.hash(password, salt, (err, hash)=> { 176 | password=hash; 177 | userModel.findOneAndUpdate({email: req.session.email}, {$set:{password:password}},{new:true},(err, docs)=> { 178 | 179 | if (err) throw err; 180 | else if(docs){ 181 | 182 | const response = responseGenerator.generate(false, "Password changed successfully! Now you can Login!!", 200, null); 183 | res.send(response); 184 | } 185 | else{res.send("");} 186 | }); 187 | }); 188 | }); 189 | 190 | } 191 | 192 | else{ 193 | const response = responseGenerator.generate(true, "Passwords didn't match.", 500, null); 194 | res.send(response); 195 | } 196 | }); 197 | 198 | 199 | //passport google auth 200 | userRouter.get('/auth/google', passport.authenticate('google', { 201 | scope: ['profile', 'email'] 202 | })); 203 | //passport facebook auth 204 | userRouter.get('/auth/google/callback', passport.authenticate('google',{ 205 | failureRedirect : '/login' 206 | }), (req, res)=> { 207 | 208 | //console.log(token); 209 | res.redirect('/google/' +token); 210 | 211 | }); 212 | 213 | userRouter.get('/auth/facebook', 214 | passport.authenticate('facebook', { 215 | scope: ['email'] 216 | })); 217 | 218 | userRouter.get('/auth/facebook/callback', passport.authenticate('facebook', { 219 | failureRedirect : '/login' 220 | }), (req, res) =>{ 221 | 222 | //console.log(token); 223 | res.redirect('/facebook/' +token); 224 | }); 225 | app.use('/',userRouter); 226 | }; 227 | 228 | -------------------------------------------------------------------------------- /public/js/angular-timer.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-timer - v1.3.5 - 2017-07-23 2:18 PM 3 | * https://github.com/siddii/angular-timer 4 | * 5 | * Copyright (c) 2017 Siddique Hameed 6 | * Licensed MIT 7 | */ 8 | var timerModule=angular.module("timer",[]).directive("timer",["$compile",function(a){return{restrict:"EA",replace:!1,scope:{interval:"=interval",startTimeAttr:"=startTime",endTimeAttr:"=endTime",countdownattr:"=countdown",finishCallback:"&finishCallback",autoStart:"&autoStart",language:"@?",fallback:"@?",maxTimeUnit:"=",seconds:"=?",minutes:"=?",hours:"=?",days:"=?",months:"=?",years:"=?",secondsS:"=?",minutesS:"=?",hoursS:"=?",daysS:"=?",monthsS:"=?",yearsS:"=?"},controller:["$scope","$element","$attrs","$timeout","I18nService","$interpolate","progressBarService",function(b,c,d,e,f,g,h){function i(){b.timeoutId&&clearTimeout(b.timeoutId)}function j(){var a={};void 0!==d.startTime&&(b.millis=moment().diff(moment(b.startTimeAttr))),a=k.getTimeUnits(b.millis),b.maxTimeUnit&&"day"!==b.maxTimeUnit?"second"===b.maxTimeUnit?(b.seconds=Math.floor(b.millis/1e3),b.minutes=0,b.hours=0,b.days=0,b.months=0,b.years=0):"minute"===b.maxTimeUnit?(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4),b.hours=0,b.days=0,b.months=0,b.years=0):"hour"===b.maxTimeUnit?(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4%60),b.hours=Math.floor(b.millis/36e5),b.days=0,b.months=0,b.years=0):"month"===b.maxTimeUnit?(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4%60),b.hours=Math.floor(b.millis/36e5%24),b.days=Math.floor(b.millis/36e5/24%30),b.months=Math.floor(b.millis/36e5/24/30),b.years=0):"year"===b.maxTimeUnit&&(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4%60),b.hours=Math.floor(b.millis/36e5%24),b.days=Math.floor(b.millis/36e5/24%30),b.months=Math.floor(b.millis/36e5/24/30%12),b.years=Math.floor(b.millis/36e5/24/365)):(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4%60),b.hours=Math.floor(b.millis/36e5%24),b.days=Math.floor(b.millis/36e5/24),b.months=0,b.years=0),b.secondsS=1===b.seconds?"":"s",b.minutesS=1===b.minutes?"":"s",b.hoursS=1===b.hours?"":"s",b.daysS=1===b.days?"":"s",b.monthsS=1===b.months?"":"s",b.yearsS=1===b.years?"":"s",b.secondUnit=a.seconds,b.minuteUnit=a.minutes,b.hourUnit=a.hours,b.dayUnit=a.days,b.monthUnit=a.months,b.yearUnit=a.years,b.sseconds=b.seconds<10?"0"+b.seconds:b.seconds,b.mminutes=b.minutes<10?"0"+b.minutes:b.minutes,b.hhours=b.hours<10?"0"+b.hours:b.hours,b.ddays=b.days<10?"0"+b.days:b.days,b.mmonths=b.months<10?"0"+b.months:b.months,b.yyears=b.years<10?"0"+b.years:b.years}"function"!=typeof String.prototype.trim&&(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),b.autoStart=d.autoStart||d.autostart,b.language=b.language||"en",b.fallback=b.fallback||"en",b.$watch("language",function(a,c){void 0!==a&&k.init(a,b.fallback)});var k=new f;k.init(b.language,b.fallback),b.displayProgressBar=0,b.displayProgressActive="active",0===c.html().trim().length?c.append(a(""+g.startSymbol()+"millis"+g.endSymbol()+"")(b)):c.append(a(c.contents())(b)),b.startTime=null,b.endTime=null,b.timeoutId=null,b.countdown=angular.isNumber(b.countdownattr)&&parseInt(b.countdownattr,10)>=0?parseInt(b.countdownattr,10):void 0,b.isRunning=!1,b.$on("timer-start",function(){b.start()}),b.$on("timer-resume",function(){b.resume()}),b.$on("timer-stop",function(){b.stop()}),b.$on("timer-clear",function(){b.clear()}),b.$on("timer-reset",function(){b.reset()}),b.$on("timer-set-countdown",function(a,c){b.countdown=c}),b.$watch("startTimeAttr",function(a,c){a!==c&&b.isRunning&&b.start()}),b.$watch("endTimeAttr",function(a,c){a!==c&&b.isRunning&&b.start()}),b.start=function(){b.startTime=b.startTimeAttr?moment(b.startTimeAttr):moment(),b.endTime=b.endTimeAttr?moment(b.endTimeAttr):null,angular.isNumber(b.countdown)||(b.countdown=angular.isNumber(b.countdownattr)&&parseInt(b.countdownattr,10)>0?parseInt(b.countdownattr,10):void 0),i(),l(),b.isRunning=!0,b.$emit("timer-started",{timeoutId:b.timeoutId,millis:b.millis,seconds:b.seconds,minutes:b.minutes,hours:b.hours,days:b.days})},b.resume=function(){i(),b.countdownattr&&(b.countdown+=1),b.startTime=moment().diff(moment(b.stoppedTime).diff(moment(b.startTime))),l(),b.isRunning=!0,b.$emit("timer-started",{timeoutId:b.timeoutId,millis:b.millis,seconds:b.seconds,minutes:b.minutes,hours:b.hours,days:b.days})},b.stop=b.pause=function(){var a=b.timeoutId;b.clear(),b.$emit("timer-stopped",{timeoutId:a,millis:b.millis,seconds:b.seconds,minutes:b.minutes,hours:b.hours,days:b.days})},b.clear=function(){b.stoppedTime=moment(),i(),b.timeoutId=null,b.isRunning=!1},b.reset=function(){b.startTime=b.startTimeAttr?moment(b.startTimeAttr):moment(),b.endTime=b.endTimeAttr?moment(b.endTimeAttr):null,b.countdown=angular.isNumber(b.countdownattr)&&parseInt(b.countdownattr,10)>0?parseInt(b.countdownattr,10):void 0,i(),l(),b.isRunning=!1,b.clear(),b.$emit("timer-reseted",{timeoutId:b.timeoutId,millis:b.millis,seconds:b.seconds,minutes:b.minutes,hours:b.hours,days:b.days})},c.bind("$destroy",function(){i(),b.isRunning=!1}),b.countdownattr?(b.millis=1e3*b.countdownattr,b.addCDSeconds=function(a){b.countdown+=a,b.isRunning||b.start()},b.$on("timer-add-cd-seconds",function(a,c){b.addCDSeconds(c)}),b.$on("timer-set-countdown-seconds",function(a,c){b.isRunning||b.clear(),b.countdown=c,b.millis=1e3*c,j()})):b.millis=0,j();var l=function m(){var a=null;b.millis=moment().diff(b.startTime);var c=b.millis%1e3;return b.endTimeAttr&&(a=b.endTimeAttr,b.millis=moment(b.endTime).diff(moment()),c=b.interval-b.millis%1e3),b.countdownattr&&(a=b.countdownattr,b.millis=1e3*b.countdown),b.millis<0?(b.stop(),b.millis=0,j(),void(b.finishCallback&&b.$eval(b.finishCallback))):(j(),b.timeoutId=setTimeout(function(){m(),b.$apply()},b.interval-c),b.$emit("timer-tick",{timeoutId:b.timeoutId,millis:b.millis,seconds:b.seconds,minutes:b.minutes,hours:b.hours,days:b.days}),b.countdown>0?b.countdown--:b.countdown<=0&&(b.stop(),b.finishCallback&&b.$eval(b.finishCallback)),void(null!==a&&(b.progressBar=h.calculateProgressBar(b.startTime,b.millis,b.endTime,b.countdownattr),100===b.progressBar&&(b.displayProgressActive=""))))};(void 0===b.autoStart||b.autoStart===!0)&&b.start()}]}}]).directive("timerControls",function(){return{restrict:"EA",scope:!0,controller:["$scope",function(a){a.timerStatus="reset",a.$on("timer-started",function(){a.timerStatus="started"}),a.$on("timer-stopped",function(){a.timerStatus="stopped"}),a.$on("timer-reset",function(){a.timerStatus="reset"}),a.timerStart=function(){a.$broadcast("timer-start")},a.timerStop=function(){a.$broadcast("timer-stop")},a.timerResume=function(){a.$broadcast("timer-resume")},a.timerToggle=function(){switch(a.timerStatus){case"started":a.timerStop();break;case"stopped":a.timerResume();break;case"reset":a.timerStart()}},a.timerAddCDSeconds=function(b){a.$broadcast("timer-add-cd-seconds",b)}}]}});"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports=timerModule);var app=angular.module("timer");app.factory("I18nService",function(){var a=function(){};return a.prototype.language="en",a.prototype.fallback="en",a.prototype.timeHumanizer={},a.prototype.init=function(a,b){var c=humanizeDuration.getSupportedLanguages();this.fallback=void 0!==b?b:"en",-1===c.indexOf(b)&&(this.fallback="en"),this.language=a,-1===c.indexOf(a)&&(this.language=this.fallback),this.timeHumanizer=humanizeDuration.humanizer({language:this.language,halfUnit:!1})},a.prototype.getTimeUnits=function(a){var b=1e3*Math.round(a/1e3),c={};return"undefined"!=typeof this.timeHumanizer?c={millis:this.timeHumanizer(b,{units:["ms"]}),seconds:this.timeHumanizer(b,{units:["s"]}),minutes:this.timeHumanizer(b,{units:["m","s"]}),hours:this.timeHumanizer(b,{units:["h","m","s"]}),days:this.timeHumanizer(b,{units:["d","h","m","s"]}),months:this.timeHumanizer(b,{units:["mo","d","h","m","s"]}),years:this.timeHumanizer(b,{units:["y","mo","d","h","m","s"]})}:console.error('i18nService has not been initialized. You must call i18nService.init("en") for example'),c},a});var app=angular.module("timer");app.factory("progressBarService",function(){var a=function(){};return a.prototype.calculateProgressBar=function(a,b,c,d){var e,f,g=0;return b/=1e3,null!==c?(e=moment(c),f=e.diff(a,"seconds"),g=100*b/f):g=100*b/d,g=100-g,g=Math.round(10*g)/10,g>100&&(g=100),g},new a}); -------------------------------------------------------------------------------- /public/views/question-operations.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • 3 |
    4 |
    5 |
    6 | Test Id: {{test.testid}} 7 |
    8 |
    9 | {{test.title}} 10 |
    11 |
    12 | 13 | 14 |
    15 |
    16 |
    17 | 18 |
  • 19 |
20 | 21 | 22 | 75 | 76 | 77 | 124 | 125 | 126 | 193 | 194 | 203 | -------------------------------------------------------------------------------- /public/css/dashboard.css: -------------------------------------------------------------------------------- 1 | html { 2 | position: relative; 3 | min-height: 100%; 4 | } 5 | 6 | body { 7 | overflow-x: hidden; 8 | } 9 | 10 | 11 | #loading-bar .bar { 12 | background: #00c8e7 !important; 13 | height: 2px !important; 14 | } 15 | 16 | 17 | .content-wrapper { 18 | margin-top: 65px; 19 | min-height: calc(100vh - 65px); 20 | padding-top: 1rem; 21 | } 22 | 23 | 24 | #mainNav .navbar-collapse { 25 | overflow: auto; 26 | max-height: 75vh; 27 | } 28 | 29 | #mainNav .navbar-collapse .navbar-nav .nav-item .nav-link { 30 | cursor: pointer; 31 | } 32 | 33 | #mainNav .navbar-collapse .navbar-sidenav .nav-link-collapse:after { 34 | float: right; 35 | content: '\f107'; 36 | font-family: 'FontAwesome'; 37 | } 38 | 39 | #mainNav .navbar-collapse .navbar-sidenav .nav-link-collapse.collapsed:after { 40 | content: '\f105'; 41 | } 42 | 43 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level, 44 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level { 45 | padding-left: 0; 46 | } 47 | 48 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level > li > a, 49 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level > li > a { 50 | display: block; 51 | padding: 0.5em 0; 52 | } 53 | 54 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level > li > a:focus, #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level > li > a:hover, 55 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level > li > a:focus, 56 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level > li > a:hover { 57 | text-decoration: none; 58 | } 59 | 60 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level > li > a { 61 | padding-left: 1em; 62 | } 63 | 64 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level > li > a { 65 | padding-left: 2em; 66 | } 67 | 68 | #mainNav .navbar-collapse .sidenav-toggler { 69 | display: none; 70 | } 71 | 72 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link { 73 | position: relative; 74 | min-width: 45px; 75 | } 76 | 77 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after { 78 | float: right; 79 | width: auto; 80 | content: '\f105'; 81 | border: none; 82 | font-family: 'FontAwesome'; 83 | } 84 | 85 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link .indicator { 86 | position: absolute; 87 | top: 5px; 88 | left: 21px; 89 | font-size: 10px; 90 | } 91 | 92 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown.show > .nav-link:after { 93 | content: '\f107'; 94 | } 95 | 96 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown .dropdown-menu > .dropdown-item > .dropdown-message { 97 | overflow: hidden; 98 | max-width: none; 99 | text-overflow: ellipsis; 100 | } 101 | 102 | @media (min-width: 992px) { 103 | #mainNav .navbar-brand { 104 | width: 250px; 105 | } 106 | #mainNav .navbar-collapse { 107 | overflow: visible; 108 | max-height: none; 109 | } 110 | #mainNav .navbar-collapse .navbar-sidenav { 111 | position: absolute; 112 | top: 0; 113 | left: 0; 114 | -webkit-flex-direction: column; 115 | -ms-flex-direction: column; 116 | flex-direction: column; 117 | margin-top: 56px; 118 | } 119 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item { 120 | width: 250px; 121 | padding: 0; 122 | } 123 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item > .nav-link { 124 | padding: 1em; 125 | } 126 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level, 127 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level { 128 | padding-left: 0; 129 | list-style: none; 130 | } 131 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li, 132 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li { 133 | width: 250px; 134 | } 135 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a, 136 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a { 137 | padding: 1em; 138 | } 139 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a { 140 | padding-left: 2.75em; 141 | } 142 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a { 143 | padding-left: 3.75em; 144 | } 145 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link { 146 | min-width: 0; 147 | } 148 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after { 149 | width: 24px; 150 | text-align: center; 151 | } 152 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown .dropdown-menu > .dropdown-item > .dropdown-message { 153 | max-width: 300px; 154 | } 155 | } 156 | 157 | #mainNav.fixed-top .sidenav-toggler { 158 | display: none; 159 | } 160 | 161 | @media (min-width: 992px) { 162 | #mainNav.fixed-top .navbar-sidenav { 163 | height: calc(100vh - 56px); 164 | } 165 | #mainNav.fixed-top .sidenav-toggler { 166 | position: absolute; 167 | top: 0; 168 | left: 0; 169 | display: flex; 170 | -webkit-flex-direction: column; 171 | -ms-flex-direction: column; 172 | flex-direction: column; 173 | margin-top: calc(100vh - 56px); 174 | } 175 | #mainNav.fixed-top .sidenav-toggler > .nav-item { 176 | width: 250px; 177 | padding: 0; 178 | } 179 | #mainNav.fixed-top .sidenav-toggler > .nav-item > .nav-link { 180 | padding: 1em; 181 | } 182 | } 183 | 184 | #mainNav.fixed-top.navbar-dark .sidenav-toggler { 185 | background-color: #212529; 186 | } 187 | 188 | #mainNav.fixed-top.navbar-dark .sidenav-toggler a i { 189 | color: #adb5bd; 190 | } 191 | 192 | #mainNav.fixed-top.navbar-light .sidenav-toggler { 193 | background-color: #dee2e6; 194 | } 195 | 196 | #mainNav.fixed-top.navbar-light .sidenav-toggler a i { 197 | color: rgba(0, 0, 0, 0.5); 198 | } 199 | 200 | body.sidenav-toggled #mainNav.fixed-top .sidenav-toggler { 201 | overflow-x: hidden; 202 | width: 55px; 203 | } 204 | 205 | body.sidenav-toggled #mainNav.fixed-top .sidenav-toggler .nav-item, 206 | body.sidenav-toggled #mainNav.fixed-top .sidenav-toggler .nav-link { 207 | width: 55px !important; 208 | } 209 | 210 | body.sidenav-toggled #mainNav.fixed-top #sidenavToggler i { 211 | -webkit-transform: scaleX(-1); 212 | -moz-transform: scaleX(-1); 213 | -o-transform: scaleX(-1); 214 | transform: scaleX(-1); 215 | filter: FlipH; 216 | -ms-filter: 'FlipH'; 217 | } 218 | 219 | #mainNav.static-top .sidenav-toggler { 220 | display: none; 221 | } 222 | 223 | @media (min-width: 992px) { 224 | #mainNav.static-top .sidenav-toggler { 225 | display: flex; 226 | } 227 | } 228 | 229 | body.sidenav-toggled #mainNav.static-top #sidenavToggler i { 230 | -webkit-transform: scaleX(-1); 231 | -moz-transform: scaleX(-1); 232 | -o-transform: scaleX(-1); 233 | transform: scaleX(-1); 234 | filter: FlipH; 235 | -ms-filter: 'FlipH'; 236 | } 237 | 238 | .content-wrapper { 239 | overflow-x: hidden; 240 | background: white; 241 | } 242 | 243 | @media (min-width: 992px) { 244 | .content-wrapper { 245 | margin-left: 250px; 246 | } 247 | } 248 | 249 | #sidenavToggler i { 250 | font-weight: 800; 251 | } 252 | 253 | .navbar-sidenav-tooltip.show { 254 | display: none; 255 | } 256 | 257 | @media (min-width: 992px) { 258 | body.sidenav-toggled .content-wrapper { 259 | margin-left: 55px; 260 | } 261 | } 262 | 263 | body.sidenav-toggled .navbar-sidenav { 264 | width: 55px; 265 | } 266 | 267 | body.sidenav-toggled .navbar-sidenav .nav-link-text { 268 | display: none; 269 | } 270 | 271 | body.sidenav-toggled .navbar-sidenav .nav-item, 272 | body.sidenav-toggled .navbar-sidenav .nav-link { 273 | width: 55px !important; 274 | } 275 | 276 | body.sidenav-toggled .navbar-sidenav .nav-item:after, 277 | body.sidenav-toggled .navbar-sidenav .nav-link:after { 278 | display: none; 279 | } 280 | 281 | body.sidenav-toggled .navbar-sidenav .nav-item { 282 | white-space: nowrap; 283 | } 284 | 285 | body.sidenav-toggled .navbar-sidenav-tooltip.show { 286 | display: flex; 287 | } 288 | 289 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav .nav-link-collapse:after { 290 | color: #868e96; 291 | } 292 | 293 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item > .nav-link { 294 | color: #868e96; 295 | } 296 | 297 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item > .nav-link:hover { 298 | color: #adb5bd; 299 | } 300 | 301 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a, 302 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a { 303 | color: #868e96; 304 | } 305 | 306 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a:focus, #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a:hover, 307 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a:focus, 308 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a:hover { 309 | color: #adb5bd; 310 | } 311 | 312 | #mainNav.navbar-dark .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after { 313 | color: #adb5bd; 314 | } 315 | 316 | @media (min-width: 992px) { 317 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav { 318 | background: #343a40; 319 | } 320 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav li.active a { 321 | color: white !important; 322 | background-color: #495057; 323 | } 324 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav li.active a:focus, #mainNav.navbar-dark .navbar-collapse .navbar-sidenav li.active a:hover { 325 | color: white; 326 | } 327 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level, 328 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level { 329 | background: #343a40; 330 | } 331 | } 332 | 333 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav .nav-link-collapse:after { 334 | color: rgba(0, 0, 0, 0.5); 335 | } 336 | 337 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item > .nav-link { 338 | color: rgba(0, 0, 0, 0.5); 339 | } 340 | 341 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item > .nav-link:hover { 342 | color: rgba(0, 0, 0, 0.7); 343 | } 344 | 345 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a, 346 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a { 347 | color: rgba(0, 0, 0, 0.5); 348 | } 349 | 350 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a:focus, #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a:hover, 351 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a:focus, 352 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a:hover { 353 | color: rgba(0, 0, 0, 0.7); 354 | } 355 | 356 | #mainNav.navbar-light .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after { 357 | color: rgba(0, 0, 0, 0.5); 358 | } 359 | 360 | @media (min-width: 992px) { 361 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav { 362 | background: #f8f9fa; 363 | } 364 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav li.active a { 365 | color: #000 !important; 366 | background-color: #e9ecef; 367 | } 368 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav li.active a:focus, #mainNav.navbar-light .navbar-collapse .navbar-sidenav li.active a:hover { 369 | color: #000; 370 | } 371 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level, 372 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level { 373 | background: #f8f9fa; 374 | } 375 | } 376 | 377 | .card-body-icon { 378 | position: absolute; 379 | z-index: 0; 380 | top: -25px; 381 | right: -25px; 382 | font-size: 5rem; 383 | -webkit-transform: rotate(15deg); 384 | -ms-transform: rotate(15deg); 385 | transform: rotate(15deg); 386 | } 387 | 388 | @media (min-width: 576px) { 389 | .card-columns { 390 | column-count: 1; 391 | } 392 | } 393 | 394 | @media (min-width: 768px) { 395 | .card-columns { 396 | column-count: 2; 397 | } 398 | } 399 | 400 | @media (min-width: 1200px) { 401 | .card-columns { 402 | column-count: 2; 403 | } 404 | } 405 | 406 | .card-login { 407 | max-width: 25rem; 408 | } 409 | 410 | .card-register { 411 | max-width: 40rem; 412 | } 413 | 414 | footer.sticky-footer { 415 | position: absolute; 416 | right: 0; 417 | bottom: 0; 418 | width: 100%; 419 | height: 56px; 420 | background-color: #e9ecef; 421 | line-height: 55px; 422 | } 423 | 424 | @media (min-width: 992px) { 425 | footer.sticky-footer { 426 | width: calc(100% - 250px); 427 | } 428 | } 429 | 430 | tr:hover { 431 | background: #dfdfdf; 432 | } 433 | td a { 434 | display: block; 435 | padding: 16px; 436 | } 437 | 438 | .registered-users{ 439 | height: 300px; 440 | overflow-y: scroll; 441 | } 442 | @media (min-width: 992px) { 443 | body.sidenav-toggled footer.sticky-footer { 444 | width: calc(100% - 55px); 445 | } 446 | } -------------------------------------------------------------------------------- /app/controllers/test.controller.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const express = require('express'); 3 | const jwt = require('jsonwebtoken'); 4 | const bcrypt = require('bcrypt'); 5 | const events = require('events'); 6 | const nodemailer = require('nodemailer'); 7 | const passport = require('passport'); 8 | 9 | const userRouter = express.Router(); 10 | const testRouter = express.Router(); 11 | const userModel = mongoose.model('User'); 12 | const socialModel = mongoose.model('SocialUser'); 13 | const testModel = mongoose.model('Test'); 14 | const questionModel = mongoose.model('Question'); 15 | const performanceModel = mongoose.model('Performance'); 16 | 17 | //libraries and middlewares 18 | const config = require('./../../config/config.js'); 19 | const responseGenerator = require('./../../libs/responseGenerator'); 20 | const auth = require("./../../middlewares/auth"); 21 | const random = require("randomstring"); 22 | 23 | 24 | // *********** ALL API'S ********************// 25 | 26 | 27 | 28 | module.exports.controller = (app)=>{ 29 | //route to get the current user 30 | testRouter.get('/currentUser',(req,res)=>{ 31 | let user=req.user; 32 | res.send(user); 33 | }); 34 | 35 | //route to get the all users 36 | testRouter.get('/allusers',(req,res)=>{ 37 | userModel.find({},(err,users)=>{ 38 | if (err) { 39 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 40 | res.send(response); 41 | } else if (!users) { 42 | let response = responseGenerator.generate(false, "No Users registered in the system:(", 200, result); 43 | res.send(response); 44 | } else { 45 | let response = responseGenerator.generate(false, "Users Available", 200, users); 46 | res.send(response); 47 | } 48 | 49 | }); 50 | }); 51 | 52 | //route to get the all the social logged users 53 | testRouter.get('/allsocialusers',(req,res)=>{ 54 | socialModel.find({},(err,users)=>{ 55 | if (err) { 56 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 57 | res.send(response); 58 | } else if (!users) { 59 | let response = responseGenerator.generate(false, "No Social Users registered in the system:(", 200, result); 60 | res.send(response); 61 | } else { 62 | let response = responseGenerator.generate(false, "Users Available", 200, users); 63 | res.send(response); 64 | } 65 | 66 | }); 67 | }); 68 | 69 | //Api to create a new Test By Admin 70 | testRouter.post('/admin/createTest', (req, res) =>{ 71 | let newTest = new testModel({ 72 | testid: random.generate({ 73 | length: 10, 74 | charset: 'numeric' 75 | }), 76 | title: req.body.title, 77 | description: req.body.description, 78 | time: req.body.time, 79 | instructions: req.body.instructions 80 | }); 81 | 82 | newTest.save( (err, test) => { 83 | if (err) { 84 | let error = responseGenerator.generate(true, "Some Error Ocurred, error : " + err, 500, null); 85 | res.send(error); 86 | } else { 87 | let response = responseGenerator.generate(false, "Successfully Created A Test", 200, test); 88 | res.send(response); 89 | } 90 | }); 91 | 92 | });//end test creation 93 | 94 | // API to get all tests in DB 95 | testRouter.get('/allTests', (req, res) =>{ 96 | 97 | testModel.find({}, (err, result)=> { 98 | if (err) { 99 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 100 | res.send(response); 101 | } else if (!result) { 102 | let response = responseGenerator.generate(false, "No Tests Available", 200, result); 103 | res.send(response); 104 | } else { 105 | let response = responseGenerator.generate(false, "Tests Available", 200, result); 106 | res.send(response); 107 | } 108 | }); 109 | }); 110 | 111 | 112 | // API to get a complete details of test 113 | testRouter.get('/test/:tid', function (req, res) { 114 | //console.log(req); 115 | testModel.findOne({ 116 | 'testid': req.params.tid 117 | }, (err, result) =>{ 118 | if (err) { 119 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 120 | res.send(response); 121 | } else { 122 | let response = responseGenerator.generate(false, "Test Details", 200, result); 123 | res.send(response); 124 | } 125 | }); 126 | }); 127 | 128 | // API to delete test 129 | testRouter.post('/test/delete/:id', (req, res)=> { 130 | testModel.findOneAndRemove({ 131 | 'testid': req.params.id 132 | }, (err)=> { 133 | if (err) { 134 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 135 | res.send(response); 136 | } else { 137 | let response = responseGenerator.generate(false, "Test Deleted", 200, null); 138 | res.send(response); 139 | } 140 | }); 141 | }); 142 | 143 | 144 | // API to add questions to test created 145 | testRouter.post('/test/:tid/addQuestion', (req, res) =>{ 146 | 147 | testModel.findOneAndUpdate({ 148 | 'testid': req.params.tid 149 | }, { 150 | '$push': { 151 | questions: req.body 152 | } 153 | }, (err) =>{ 154 | if (err) { 155 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 156 | res.send(response); 157 | } else { 158 | //console.log(result); 159 | let response = responseGenerator.generate(false, "Question added Successfully", 200, null); 160 | res.send(response); 161 | } 162 | }); 163 | }); 164 | 165 | 166 | //API to delete a question in particular test 167 | testRouter.post('/deleteQuestion/:tid/:qid', (req, res) =>{ 168 | testModel.findOneAndUpdate({ 169 | 'testid': req.params.tid 170 | }, { 171 | "$pull": { 172 | "questions": { 173 | _id: req.params.qid 174 | } 175 | } 176 | }, (err, result) =>{ 177 | if (err) { 178 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 179 | res.send(result); 180 | } else { 181 | let response = responseGenerator.generate(false, "Question Deleted Successfully", 200, result); 182 | res.send(response); 183 | } 184 | }); 185 | }); 186 | 187 | 188 | // API to edit question 189 | testRouter.put('/test/editQuestion/:qid', (req, res) =>{ 190 | testModel.findOneAndUpdate({ 191 | "questions._id": req.params.qid 192 | }, { 193 | "$set": { 194 | "questions.$.question": req.body.question, 195 | "questions.$.optionA": req.body.optionA, 196 | "questions.$.optionB": req.body.optionB, 197 | "questions.$.optionC": req.body.optionC, 198 | "questions.$.optionD": req.body.optionD, 199 | "questions.$.answer": req.body.answer 200 | } 201 | }, (err) =>{ 202 | if (err) { 203 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 204 | res.send(response); 205 | } else { 206 | let response = responseGenerator.generate(false, "Question Edited Successfully", 200, null); 207 | res.send(response); 208 | } 209 | }); 210 | }); 211 | 212 | 213 | // API to edit test details 214 | testRouter.put('/test/edit/:tid', (req, res) =>{ 215 | 216 | testModel.findOneAndUpdate({ 217 | "testid": req.params.tid 218 | }, req.body, (err,test) =>{ 219 | if (err) { 220 | let response = responseGenerator.generate(true, "Some Internal Error", 500, null); 221 | res.send(response); 222 | } else { 223 | let response = responseGenerator.generate(false, "Test Edited Successfully", 200, null); 224 | res.send(response); 225 | } 226 | }); 227 | }); 228 | 229 | 230 | //to get all questions by User as well as Admin 231 | testRouter.get('/test/:tid/getQuestions', (req, res)=> { 232 | testModel.find({ 233 | testid: req.params.tid 234 | }, (err, test)=> { 235 | if (err) { 236 | let error = responseGenerator.generate(true, "Something is not working, error : " + err, 500, null); 237 | res.send(error); 238 | } else if (test === null || test === undefined || test === []) { 239 | let error = responseGenerator.generate(false, "No Question added in this test!", 204, null); 240 | res.send(error); 241 | } else { 242 | let response = responseGenerator.generate(false, "All Questions fetched successfully", 200, test[0].questions); 243 | res.send(response); 244 | } 245 | }) 246 | }); 247 | 248 | // api to store test attempted by users 249 | testRouter.post('/tests/:tid/attemptedby', (req, res) =>{ 250 | var data={ 251 | email:req.body.email, 252 | score:req.body.score 253 | } 254 | testModel.findOneAndUpdate({ 255 | 'testid': req.params.tid 256 | }, { 257 | '$push': { 258 | testAttemptedBy: data 259 | } 260 | }, (err)=> { 261 | if (err) { 262 | let response = responseGenerator.generate(true, "Some Error Ocurred, error : " + err, 500, null); 263 | res.send(response); 264 | } else { 265 | let response = responseGenerator.generate(false, "Successfully Updated The Test", 200, null); 266 | res.send(response); 267 | } 268 | }); 269 | }); 270 | 271 | 272 | //API to get tests attempted by a user 273 | testRouter.get('/usertests/:tid', (req, res) =>{ 274 | 275 | testModel.find({ 276 | testid: req.params.tid 277 | }, (err, result)=> { 278 | if (err) { 279 | response = responseGenerator.generate(true, "Some Internal Error", 500, null); 280 | res.send(response); 281 | } else if (result === null || result === undefined || result === []) { 282 | let error = responseGenerator.generate(false, "No users attempted the test!", 204, null); 283 | res.send(error); 284 | } 285 | else { 286 | response = responseGenerator.generate(false, "Tests Taken By User", 200, result.testAttemptedBy); 287 | res.send(response); 288 | } 289 | }); 290 | }); 291 | 292 | //API to get the performances of all users 293 | testRouter.get('/all/performances', (req, res) =>{ 294 | //api to get performance user specific 295 | performanceModel.find({}, (err, Performances)=> { 296 | if (err) { 297 | let error = responseGenerator.generate(true, "Something is not working, error : " + err, 500, null); 298 | res.send(error); 299 | 300 | } else { 301 | let response = responseGenerator.generate(false, "Performances fetched successfully!!!", 200, Performances); 302 | res.send(response); 303 | } 304 | }); 305 | }); 306 | 307 | //API to get the performance of all users in a particular test 308 | 309 | testRouter.get('/all/performance/:tid', (req, res) =>{ 310 | //api to get performance user specific 311 | performanceModel.find({ 312 | testId:req.params.tid 313 | }, (err, Performance)=> { 314 | if (err) { 315 | let error = responseGenerator.generate(true, "Something is not working, error : " + err, 500, null); 316 | res.send(error); 317 | 318 | } else { 319 | let response = responseGenerator.generate(false, "TotalPerformance of user in all Tests fetched successfully!!!", 200, Performance); 320 | res.send(response); 321 | } 322 | }); 323 | }); 324 | 325 | 326 | //API to get the performance of a single user in a particular test 327 | 328 | testRouter.get('/performance/:tid', (req, res) =>{ 329 | //api to get performance user specific 330 | performanceModel.findOne({ 331 | $and:[{userEmail: req.user.email} , {testId:req.params.tid}] 332 | }, (err, Performance) =>{ 333 | if (err) { 334 | let error = responseGenerator.generate(true, "Something is not working, error : " + err, 500, null); 335 | res.send(error); 336 | 337 | } else { 338 | let response = responseGenerator.generate(false, "TotalPerformance of user in all Tests fetched successfully!!!", 200, Performance); 339 | res.send(response); 340 | } 341 | }); 342 | }); 343 | 344 | 345 | //API to add the performance of the user 346 | testRouter.post('/addPerformance', (req, res) =>{ 347 | 348 | let performance = new performanceModel({ 349 | userEmail: req.body.email, 350 | testId: req.body.testid, 351 | score: req.body.score, 352 | questionCount: req.body.noOfQuestions, 353 | timeTaken:req.body.timetaken, 354 | totalCorrectAnswers:req.body.totalCorrectAnswers, 355 | totalSkipped:req.body.totalSkipped 356 | }); 357 | //console.log(taker); 358 | performance.save( (err, result) =>{ 359 | if (err) { 360 | response = responseGenerator.generate(true, "Some Internal Error", 500, null); 361 | res.send(response); 362 | } else { 363 | response = responseGenerator.generate(false, "Added Test Performance Successfully", 200, performance); 364 | res.send(response); 365 | } 366 | }); 367 | }); 368 | 369 | 370 | app.use('/user',auth.verifyToken,testRouter); 371 | 372 | 373 | } 374 | 375 | 376 | -------------------------------------------------------------------------------- /public/js/angular-animate.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.6.7 3 | (c) 2010-2017 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(S,q){'use strict';function Ea(a,b,c){if(!a)throw Pa("areq",b||"?",c||"required");return a}function Fa(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;V(a)&&(a=a.join(" "));V(b)&&(b=b.join(" "));return a+" "+b}function Qa(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function W(a,b,c){var d="";a=V(a)?a:a&&C(a)&&a.length?a.split(/\s+/):[];t(a,function(a,f){a&&0=a&&(a=g,g=0,b.push(e),e=[]);e.push(f.fn);f.children.forEach(function(a){g++;c.push(a)});a--}e.length&&b.push(e);return b}(c)}var s=[],y=X(a);return function(n,q,v){function E(a){a=a.hasAttribute("ng-animate-ref")? 29 | [a]:a.querySelectorAll("[ng-animate-ref]");var b=[];t(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function g(a){var b=[],c={};t(a,function(a,d){var k=J(a.element),g=0<=["enter","move"].indexOf(a.event),k=a.structural?E(k):[];if(k.length){var e=g?"to":"from";t(k,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][e]={animationID:d,element:A(a)}})}else b.push(a)});var d={},g={};t(c,function(c,e){var f=c.from,p=c.to;if(f&&p){var H=a[f.animationID], 30 | z=a[p.animationID],m=f.animationID.toString();if(!g[m]){var l=g[m]={structural:!0,beforeStart:function(){H.beforeStart();z.beforeStart()},close:function(){H.close();z.close()},classes:M(H.classes,z.classes),from:H,to:z,anchors:[]};l.classes.length?b.push(l):(b.push(H),b.push(z))}g[m].anchors.push({out:f.element,"in":p.element})}else f=f?f.animationID:p.animationID,p=f.toString(),d[p]||(d[p]=!0,b.push(a[f]))});return b}function M(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d=P&&b>=N&&(ba=!0,m()))}function ga(){function b(){if(!M){L(!1);t(x,function(a){k.style[a[0]]=a[1]});g(a,h); 38 | e.addClass(a,ca);if(r.recalculateTimingStyles){ma=k.getAttribute("class")+" "+fa;ja=q(k,ma);B=E(k,ma,ja);$=B.maxDelay;w=Math.max($,0);N=B.maxDuration;if(0===N){m();return}r.hasTransitions=0s.expectedEndTime)?n.cancel(s.timer):f.push(m)}F&&(l=n(c,l,!1),f[0]={timer:l,expectedEndTime:d},f.push(m),a.data("$$animateCss",f));if(ea.length)a.on(ea.join(" "),z);h.to&&(h.cleanupStyles&&Ma(p,k,Object.keys(h.to)),Ia(a,h))}}function c(){var b= 40 | a.data("$$animateCss");if(b){for(var d=1;d:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} 11 | .btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em} 12 | .btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em} 13 | .btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em} 14 | .btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} 15 | .btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em} 16 | .btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em} 17 | .btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em} 18 | .btn-social-icon>:first-child{border:none;text-align:center;width:100% !important} 19 | .btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0} 20 | .btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0} 21 | .btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0} 22 | .btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:focus,.btn-adn.focus{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)} 23 | .btn-adn:hover{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)} 24 | .btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:active:hover,.btn-adn.active:hover,.open>.dropdown-toggle.btn-adn:hover,.btn-adn:active:focus,.btn-adn.active:focus,.open>.dropdown-toggle.btn-adn:focus,.btn-adn:active.focus,.btn-adn.active.focus,.open>.dropdown-toggle.btn-adn.focus{color:#fff;background-color:#b94630;border-color:rgba(0,0,0,0.2)} 25 | .btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none} 26 | .btn-adn.disabled:hover,.btn-adn[disabled]:hover,fieldset[disabled] .btn-adn:hover,.btn-adn.disabled:focus,.btn-adn[disabled]:focus,fieldset[disabled] .btn-adn:focus,.btn-adn.disabled.focus,.btn-adn[disabled].focus,fieldset[disabled] .btn-adn.focus{background-color:#d87a68;border-color:rgba(0,0,0,0.2)} 27 | .btn-adn .badge{color:#d87a68;background-color:#fff} 28 | .btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:focus,.btn-bitbucket.focus{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)} 29 | .btn-bitbucket:hover{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)} 30 | .btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:active:hover,.btn-bitbucket.active:hover,.open>.dropdown-toggle.btn-bitbucket:hover,.btn-bitbucket:active:focus,.btn-bitbucket.active:focus,.open>.dropdown-toggle.btn-bitbucket:focus,.btn-bitbucket:active.focus,.btn-bitbucket.active.focus,.open>.dropdown-toggle.btn-bitbucket.focus{color:#fff;background-color:#0f253c;border-color:rgba(0,0,0,0.2)} 31 | .btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none} 32 | .btn-bitbucket.disabled:hover,.btn-bitbucket[disabled]:hover,fieldset[disabled] .btn-bitbucket:hover,.btn-bitbucket.disabled:focus,.btn-bitbucket[disabled]:focus,fieldset[disabled] .btn-bitbucket:focus,.btn-bitbucket.disabled.focus,.btn-bitbucket[disabled].focus,fieldset[disabled] .btn-bitbucket.focus{background-color:#205081;border-color:rgba(0,0,0,0.2)} 33 | .btn-bitbucket .badge{color:#205081;background-color:#fff} 34 | .btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:focus,.btn-dropbox.focus{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)} 35 | .btn-dropbox:hover{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)} 36 | .btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:active:hover,.btn-dropbox.active:hover,.open>.dropdown-toggle.btn-dropbox:hover,.btn-dropbox:active:focus,.btn-dropbox.active:focus,.open>.dropdown-toggle.btn-dropbox:focus,.btn-dropbox:active.focus,.btn-dropbox.active.focus,.open>.dropdown-toggle.btn-dropbox.focus{color:#fff;background-color:#0a568c;border-color:rgba(0,0,0,0.2)} 37 | .btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none} 38 | .btn-dropbox.disabled:hover,.btn-dropbox[disabled]:hover,fieldset[disabled] .btn-dropbox:hover,.btn-dropbox.disabled:focus,.btn-dropbox[disabled]:focus,fieldset[disabled] .btn-dropbox:focus,.btn-dropbox.disabled.focus,.btn-dropbox[disabled].focus,fieldset[disabled] .btn-dropbox.focus{background-color:#1087dd;border-color:rgba(0,0,0,0.2)} 39 | .btn-dropbox .badge{color:#1087dd;background-color:#fff} 40 | .btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:focus,.btn-facebook.focus{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)} 41 | .btn-facebook:hover{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)} 42 | .btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:active:hover,.btn-facebook.active:hover,.open>.dropdown-toggle.btn-facebook:hover,.btn-facebook:active:focus,.btn-facebook.active:focus,.open>.dropdown-toggle.btn-facebook:focus,.btn-facebook:active.focus,.btn-facebook.active.focus,.open>.dropdown-toggle.btn-facebook.focus{color:#fff;background-color:#23345a;border-color:rgba(0,0,0,0.2)} 43 | .btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none} 44 | .btn-facebook.disabled:hover,.btn-facebook[disabled]:hover,fieldset[disabled] .btn-facebook:hover,.btn-facebook.disabled:focus,.btn-facebook[disabled]:focus,fieldset[disabled] .btn-facebook:focus,.btn-facebook.disabled.focus,.btn-facebook[disabled].focus,fieldset[disabled] .btn-facebook.focus{background-color:#3b5998;border-color:rgba(0,0,0,0.2)} 45 | .btn-facebook .badge{color:#3b5998;background-color:#fff} 46 | .btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:focus,.btn-flickr.focus{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)} 47 | .btn-flickr:hover{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)} 48 | .btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:active:hover,.btn-flickr.active:hover,.open>.dropdown-toggle.btn-flickr:hover,.btn-flickr:active:focus,.btn-flickr.active:focus,.open>.dropdown-toggle.btn-flickr:focus,.btn-flickr:active.focus,.btn-flickr.active.focus,.open>.dropdown-toggle.btn-flickr.focus{color:#fff;background-color:#a80057;border-color:rgba(0,0,0,0.2)} 49 | .btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none} 50 | .btn-flickr.disabled:hover,.btn-flickr[disabled]:hover,fieldset[disabled] .btn-flickr:hover,.btn-flickr.disabled:focus,.btn-flickr[disabled]:focus,fieldset[disabled] .btn-flickr:focus,.btn-flickr.disabled.focus,.btn-flickr[disabled].focus,fieldset[disabled] .btn-flickr.focus{background-color:#ff0084;border-color:rgba(0,0,0,0.2)} 51 | .btn-flickr .badge{color:#ff0084;background-color:#fff} 52 | .btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,0.2)}.btn-foursquare:focus,.btn-foursquare.focus{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)} 53 | .btn-foursquare:hover{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)} 54 | .btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:active:hover,.btn-foursquare.active:hover,.open>.dropdown-toggle.btn-foursquare:hover,.btn-foursquare:active:focus,.btn-foursquare.active:focus,.open>.dropdown-toggle.btn-foursquare:focus,.btn-foursquare:active.focus,.btn-foursquare.active.focus,.open>.dropdown-toggle.btn-foursquare.focus{color:#fff;background-color:#e30742;border-color:rgba(0,0,0,0.2)} 55 | .btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none} 56 | .btn-foursquare.disabled:hover,.btn-foursquare[disabled]:hover,fieldset[disabled] .btn-foursquare:hover,.btn-foursquare.disabled:focus,.btn-foursquare[disabled]:focus,fieldset[disabled] .btn-foursquare:focus,.btn-foursquare.disabled.focus,.btn-foursquare[disabled].focus,fieldset[disabled] .btn-foursquare.focus{background-color:#f94877;border-color:rgba(0,0,0,0.2)} 57 | .btn-foursquare .badge{color:#f94877;background-color:#fff} 58 | .btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:focus,.btn-github.focus{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)} 59 | .btn-github:hover{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)} 60 | .btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:active:hover,.btn-github.active:hover,.open>.dropdown-toggle.btn-github:hover,.btn-github:active:focus,.btn-github.active:focus,.open>.dropdown-toggle.btn-github:focus,.btn-github:active.focus,.btn-github.active.focus,.open>.dropdown-toggle.btn-github.focus{color:#fff;background-color:#191919;border-color:rgba(0,0,0,0.2)} 61 | .btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none} 62 | .btn-github.disabled:hover,.btn-github[disabled]:hover,fieldset[disabled] .btn-github:hover,.btn-github.disabled:focus,.btn-github[disabled]:focus,fieldset[disabled] .btn-github:focus,.btn-github.disabled.focus,.btn-github[disabled].focus,fieldset[disabled] .btn-github.focus{background-color:#444;border-color:rgba(0,0,0,0.2)} 63 | .btn-github .badge{color:#444;background-color:#fff} 64 | .btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:focus,.btn-google.focus{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)} 65 | .btn-google:hover{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)} 66 | .btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active:hover,.btn-google.active:hover,.open>.dropdown-toggle.btn-google:hover,.btn-google:active:focus,.btn-google.active:focus,.open>.dropdown-toggle.btn-google:focus,.btn-google:active.focus,.btn-google.active.focus,.open>.dropdown-toggle.btn-google.focus{color:#fff;background-color:#a32b1c;border-color:rgba(0,0,0,0.2)} 67 | .btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none} 68 | .btn-google.disabled:hover,.btn-google[disabled]:hover,fieldset[disabled] .btn-google:hover,.btn-google.disabled:focus,.btn-google[disabled]:focus,fieldset[disabled] .btn-google:focus,.btn-google.disabled.focus,.btn-google[disabled].focus,fieldset[disabled] .btn-google.focus{background-color:#dd4b39;border-color:rgba(0,0,0,0.2)} 69 | .btn-google .badge{color:#dd4b39;background-color:#fff} 70 | .btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:focus,.btn-instagram.focus{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)} 71 | .btn-instagram:hover{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)} 72 | .btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:active:hover,.btn-instagram.active:hover,.open>.dropdown-toggle.btn-instagram:hover,.btn-instagram:active:focus,.btn-instagram.active:focus,.open>.dropdown-toggle.btn-instagram:focus,.btn-instagram:active.focus,.btn-instagram.active.focus,.open>.dropdown-toggle.btn-instagram.focus{color:#fff;background-color:#26455d;border-color:rgba(0,0,0,0.2)} 73 | .btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none} 74 | .btn-instagram.disabled:hover,.btn-instagram[disabled]:hover,fieldset[disabled] .btn-instagram:hover,.btn-instagram.disabled:focus,.btn-instagram[disabled]:focus,fieldset[disabled] .btn-instagram:focus,.btn-instagram.disabled.focus,.btn-instagram[disabled].focus,fieldset[disabled] .btn-instagram.focus{background-color:#3f729b;border-color:rgba(0,0,0,0.2)} 75 | .btn-instagram .badge{color:#3f729b;background-color:#fff} 76 | .btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:focus,.btn-linkedin.focus{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)} 77 | .btn-linkedin:hover{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)} 78 | .btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:active:hover,.btn-linkedin.active:hover,.open>.dropdown-toggle.btn-linkedin:hover,.btn-linkedin:active:focus,.btn-linkedin.active:focus,.open>.dropdown-toggle.btn-linkedin:focus,.btn-linkedin:active.focus,.btn-linkedin.active.focus,.open>.dropdown-toggle.btn-linkedin.focus{color:#fff;background-color:#00405f;border-color:rgba(0,0,0,0.2)} 79 | .btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none} 80 | .btn-linkedin.disabled:hover,.btn-linkedin[disabled]:hover,fieldset[disabled] .btn-linkedin:hover,.btn-linkedin.disabled:focus,.btn-linkedin[disabled]:focus,fieldset[disabled] .btn-linkedin:focus,.btn-linkedin.disabled.focus,.btn-linkedin[disabled].focus,fieldset[disabled] .btn-linkedin.focus{background-color:#007bb6;border-color:rgba(0,0,0,0.2)} 81 | .btn-linkedin .badge{color:#007bb6;background-color:#fff} 82 | .btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:focus,.btn-microsoft.focus{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)} 83 | .btn-microsoft:hover{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)} 84 | .btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:active:hover,.btn-microsoft.active:hover,.open>.dropdown-toggle.btn-microsoft:hover,.btn-microsoft:active:focus,.btn-microsoft.active:focus,.open>.dropdown-toggle.btn-microsoft:focus,.btn-microsoft:active.focus,.btn-microsoft.active.focus,.open>.dropdown-toggle.btn-microsoft.focus{color:#fff;background-color:#0f4bac;border-color:rgba(0,0,0,0.2)} 85 | .btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none} 86 | .btn-microsoft.disabled:hover,.btn-microsoft[disabled]:hover,fieldset[disabled] .btn-microsoft:hover,.btn-microsoft.disabled:focus,.btn-microsoft[disabled]:focus,fieldset[disabled] .btn-microsoft:focus,.btn-microsoft.disabled.focus,.btn-microsoft[disabled].focus,fieldset[disabled] .btn-microsoft.focus{background-color:#2672ec;border-color:rgba(0,0,0,0.2)} 87 | .btn-microsoft .badge{color:#2672ec;background-color:#fff} 88 | .btn-odnoklassniki{color:#fff;background-color:#f4731c;border-color:rgba(0,0,0,0.2)}.btn-odnoklassniki:focus,.btn-odnoklassniki.focus{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)} 89 | .btn-odnoklassniki:hover{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)} 90 | .btn-odnoklassniki:active,.btn-odnoklassniki.active,.open>.dropdown-toggle.btn-odnoklassniki{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)}.btn-odnoklassniki:active:hover,.btn-odnoklassniki.active:hover,.open>.dropdown-toggle.btn-odnoklassniki:hover,.btn-odnoklassniki:active:focus,.btn-odnoklassniki.active:focus,.open>.dropdown-toggle.btn-odnoklassniki:focus,.btn-odnoklassniki:active.focus,.btn-odnoklassniki.active.focus,.open>.dropdown-toggle.btn-odnoklassniki.focus{color:#fff;background-color:#b14c09;border-color:rgba(0,0,0,0.2)} 91 | .btn-odnoklassniki:active,.btn-odnoklassniki.active,.open>.dropdown-toggle.btn-odnoklassniki{background-image:none} 92 | .btn-odnoklassniki.disabled:hover,.btn-odnoklassniki[disabled]:hover,fieldset[disabled] .btn-odnoklassniki:hover,.btn-odnoklassniki.disabled:focus,.btn-odnoklassniki[disabled]:focus,fieldset[disabled] .btn-odnoklassniki:focus,.btn-odnoklassniki.disabled.focus,.btn-odnoklassniki[disabled].focus,fieldset[disabled] .btn-odnoklassniki.focus{background-color:#f4731c;border-color:rgba(0,0,0,0.2)} 93 | .btn-odnoklassniki .badge{color:#f4731c;background-color:#fff} 94 | .btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:focus,.btn-openid.focus{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)} 95 | .btn-openid:hover{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)} 96 | .btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active:hover,.btn-openid.active:hover,.open>.dropdown-toggle.btn-openid:hover,.btn-openid:active:focus,.btn-openid.active:focus,.open>.dropdown-toggle.btn-openid:focus,.btn-openid:active.focus,.btn-openid.active.focus,.open>.dropdown-toggle.btn-openid.focus{color:#fff;background-color:#b86607;border-color:rgba(0,0,0,0.2)} 97 | .btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none} 98 | .btn-openid.disabled:hover,.btn-openid[disabled]:hover,fieldset[disabled] .btn-openid:hover,.btn-openid.disabled:focus,.btn-openid[disabled]:focus,fieldset[disabled] .btn-openid:focus,.btn-openid.disabled.focus,.btn-openid[disabled].focus,fieldset[disabled] .btn-openid.focus{background-color:#f7931e;border-color:rgba(0,0,0,0.2)} 99 | .btn-openid .badge{color:#f7931e;background-color:#fff} 100 | .btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,0.2)}.btn-pinterest:focus,.btn-pinterest.focus{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)} 101 | .btn-pinterest:hover{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)} 102 | .btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:active:hover,.btn-pinterest.active:hover,.open>.dropdown-toggle.btn-pinterest:hover,.btn-pinterest:active:focus,.btn-pinterest.active:focus,.open>.dropdown-toggle.btn-pinterest:focus,.btn-pinterest:active.focus,.btn-pinterest.active.focus,.open>.dropdown-toggle.btn-pinterest.focus{color:#fff;background-color:#801419;border-color:rgba(0,0,0,0.2)} 103 | .btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none} 104 | .btn-pinterest.disabled:hover,.btn-pinterest[disabled]:hover,fieldset[disabled] .btn-pinterest:hover,.btn-pinterest.disabled:focus,.btn-pinterest[disabled]:focus,fieldset[disabled] .btn-pinterest:focus,.btn-pinterest.disabled.focus,.btn-pinterest[disabled].focus,fieldset[disabled] .btn-pinterest.focus{background-color:#cb2027;border-color:rgba(0,0,0,0.2)} 105 | .btn-pinterest .badge{color:#cb2027;background-color:#fff} 106 | .btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:focus,.btn-reddit.focus{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)} 107 | .btn-reddit:hover{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)} 108 | .btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:active:hover,.btn-reddit.active:hover,.open>.dropdown-toggle.btn-reddit:hover,.btn-reddit:active:focus,.btn-reddit.active:focus,.open>.dropdown-toggle.btn-reddit:focus,.btn-reddit:active.focus,.btn-reddit.active.focus,.open>.dropdown-toggle.btn-reddit.focus{color:#000;background-color:#98ccff;border-color:rgba(0,0,0,0.2)} 109 | .btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none} 110 | .btn-reddit.disabled:hover,.btn-reddit[disabled]:hover,fieldset[disabled] .btn-reddit:hover,.btn-reddit.disabled:focus,.btn-reddit[disabled]:focus,fieldset[disabled] .btn-reddit:focus,.btn-reddit.disabled.focus,.btn-reddit[disabled].focus,fieldset[disabled] .btn-reddit.focus{background-color:#eff7ff;border-color:rgba(0,0,0,0.2)} 111 | .btn-reddit .badge{color:#eff7ff;background-color:#000} 112 | .btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:focus,.btn-soundcloud.focus{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)} 113 | .btn-soundcloud:hover{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)} 114 | .btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:active:hover,.btn-soundcloud.active:hover,.open>.dropdown-toggle.btn-soundcloud:hover,.btn-soundcloud:active:focus,.btn-soundcloud.active:focus,.open>.dropdown-toggle.btn-soundcloud:focus,.btn-soundcloud:active.focus,.btn-soundcloud.active.focus,.open>.dropdown-toggle.btn-soundcloud.focus{color:#fff;background-color:#a83800;border-color:rgba(0,0,0,0.2)} 115 | .btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none} 116 | .btn-soundcloud.disabled:hover,.btn-soundcloud[disabled]:hover,fieldset[disabled] .btn-soundcloud:hover,.btn-soundcloud.disabled:focus,.btn-soundcloud[disabled]:focus,fieldset[disabled] .btn-soundcloud:focus,.btn-soundcloud.disabled.focus,.btn-soundcloud[disabled].focus,fieldset[disabled] .btn-soundcloud.focus{background-color:#f50;border-color:rgba(0,0,0,0.2)} 117 | .btn-soundcloud .badge{color:#f50;background-color:#fff} 118 | .btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:focus,.btn-tumblr.focus{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)} 119 | .btn-tumblr:hover{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)} 120 | .btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:active:hover,.btn-tumblr.active:hover,.open>.dropdown-toggle.btn-tumblr:hover,.btn-tumblr:active:focus,.btn-tumblr.active:focus,.open>.dropdown-toggle.btn-tumblr:focus,.btn-tumblr:active.focus,.btn-tumblr.active.focus,.open>.dropdown-toggle.btn-tumblr.focus{color:#fff;background-color:#111c26;border-color:rgba(0,0,0,0.2)} 121 | .btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none} 122 | .btn-tumblr.disabled:hover,.btn-tumblr[disabled]:hover,fieldset[disabled] .btn-tumblr:hover,.btn-tumblr.disabled:focus,.btn-tumblr[disabled]:focus,fieldset[disabled] .btn-tumblr:focus,.btn-tumblr.disabled.focus,.btn-tumblr[disabled].focus,fieldset[disabled] .btn-tumblr.focus{background-color:#2c4762;border-color:rgba(0,0,0,0.2)} 123 | .btn-tumblr .badge{color:#2c4762;background-color:#fff} 124 | .btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:focus,.btn-twitter.focus{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)} 125 | .btn-twitter:hover{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)} 126 | .btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:active:hover,.btn-twitter.active:hover,.open>.dropdown-toggle.btn-twitter:hover,.btn-twitter:active:focus,.btn-twitter.active:focus,.open>.dropdown-toggle.btn-twitter:focus,.btn-twitter:active.focus,.btn-twitter.active.focus,.open>.dropdown-toggle.btn-twitter.focus{color:#fff;background-color:#1583d7;border-color:rgba(0,0,0,0.2)} 127 | .btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none} 128 | .btn-twitter.disabled:hover,.btn-twitter[disabled]:hover,fieldset[disabled] .btn-twitter:hover,.btn-twitter.disabled:focus,.btn-twitter[disabled]:focus,fieldset[disabled] .btn-twitter:focus,.btn-twitter.disabled.focus,.btn-twitter[disabled].focus,fieldset[disabled] .btn-twitter.focus{background-color:#55acee;border-color:rgba(0,0,0,0.2)} 129 | .btn-twitter .badge{color:#55acee;background-color:#fff} 130 | .btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:focus,.btn-vimeo.focus{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)} 131 | .btn-vimeo:hover{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)} 132 | .btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:active:hover,.btn-vimeo.active:hover,.open>.dropdown-toggle.btn-vimeo:hover,.btn-vimeo:active:focus,.btn-vimeo.active:focus,.open>.dropdown-toggle.btn-vimeo:focus,.btn-vimeo:active.focus,.btn-vimeo.active.focus,.open>.dropdown-toggle.btn-vimeo.focus{color:#fff;background-color:#0f7b9f;border-color:rgba(0,0,0,0.2)} 133 | .btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none} 134 | .btn-vimeo.disabled:hover,.btn-vimeo[disabled]:hover,fieldset[disabled] .btn-vimeo:hover,.btn-vimeo.disabled:focus,.btn-vimeo[disabled]:focus,fieldset[disabled] .btn-vimeo:focus,.btn-vimeo.disabled.focus,.btn-vimeo[disabled].focus,fieldset[disabled] .btn-vimeo.focus{background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)} 135 | .btn-vimeo .badge{color:#1ab7ea;background-color:#fff} 136 | .btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:focus,.btn-vk.focus{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)} 137 | .btn-vk:hover{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)} 138 | .btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:active:hover,.btn-vk.active:hover,.open>.dropdown-toggle.btn-vk:hover,.btn-vk:active:focus,.btn-vk.active:focus,.open>.dropdown-toggle.btn-vk:focus,.btn-vk:active.focus,.btn-vk.active.focus,.open>.dropdown-toggle.btn-vk.focus{color:#fff;background-color:#3a526b;border-color:rgba(0,0,0,0.2)} 139 | .btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none} 140 | .btn-vk.disabled:hover,.btn-vk[disabled]:hover,fieldset[disabled] .btn-vk:hover,.btn-vk.disabled:focus,.btn-vk[disabled]:focus,fieldset[disabled] .btn-vk:focus,.btn-vk.disabled.focus,.btn-vk[disabled].focus,fieldset[disabled] .btn-vk.focus{background-color:#587ea3;border-color:rgba(0,0,0,0.2)} 141 | .btn-vk .badge{color:#587ea3;background-color:#fff} 142 | .btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:focus,.btn-yahoo.focus{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)} 143 | .btn-yahoo:hover{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)} 144 | .btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:active:hover,.btn-yahoo.active:hover,.open>.dropdown-toggle.btn-yahoo:hover,.btn-yahoo:active:focus,.btn-yahoo.active:focus,.open>.dropdown-toggle.btn-yahoo:focus,.btn-yahoo:active.focus,.btn-yahoo.active.focus,.open>.dropdown-toggle.btn-yahoo.focus{color:#fff;background-color:#39074e;border-color:rgba(0,0,0,0.2)} 145 | .btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none} 146 | .btn-yahoo.disabled:hover,.btn-yahoo[disabled]:hover,fieldset[disabled] .btn-yahoo:hover,.btn-yahoo.disabled:focus,.btn-yahoo[disabled]:focus,fieldset[disabled] .btn-yahoo:focus,.btn-yahoo.disabled.focus,.btn-yahoo[disabled].focus,fieldset[disabled] .btn-yahoo.focus{background-color:#720e9e;border-color:rgba(0,0,0,0.2)} 147 | .btn-yahoo .badge{color:#720e9e;background-color:#fff} 148 | --------------------------------------------------------------------------------