--------------------------------------------------------------------------------
/public/views/signup.html:
--------------------------------------------------------------------------------
1 |
2 |
Go Back To HomePage
3 |
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 |
40 |
72 |
--------------------------------------------------------------------------------
/public/views/userperformances.html:
--------------------------------------------------------------------------------
1 |
Click To Get the OverAll Performance
2 |
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 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
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 |
32 |
33 |
34 |
40 |
41 |
42 | - 1) You will be alloted {{testCtrl.testtime}} Minutes to Give Test, After which window will close Automatically.
43 | - 2) You can't go back after submitting the Answer for each Question.
44 | - 3) You can submit the Test before the finishing Time if you want but can't after the time finishes, It will Automatically be submitted.
45 | - 4) Please don't do cheating in test. Be fair to yourself for your true Assessment of skills.
46 | - 5) If you aggree with all the terms stated above you may proceed by clicking Start Button Below.
47 |
48 |
Best Of Luck!
49 |
50 |
54 |
55 |
56 |
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 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Performance Of Users
30 |
31 |
37 |
38 |
39 |
40 |
73 |
74 |
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 |
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 |
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 |
61 |
62 |
63 |
64 |
Do you want to Submit?
65 | Times Up!! Press Submit button to submit the test.
66 |
67 |
71 |
72 |
73 |
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;i
0&&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 |
23 |
24 |
25 |
31 |
32 |
37 |
42 |
47 |
52 |
57 |
62 |
67 |
68 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
86 |
87 |
88 | - {{$index + 1}}) {{question.question}}
89 |
90 |
91 |
- 1) {{question.optionA}}
92 |
93 |
94 |
- 2) {{question.optionB}}
95 |
96 |
97 |
98 |
99 |
- 3) {{question.optionC}}
100 |
101 |
102 |
- 4) {{question.optionD}}
103 |
104 |
105 | -
106 |
107 |
108 | Answer: {{question.answer}}
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
135 |
136 |
143 |
150 |
157 |
164 |
171 |
178 |
185 |
186 |
190 |
191 |
192 |
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 |
--------------------------------------------------------------------------------