server/frontend/static/js/challenge.js

631 lines
19 KiB
JavaScript

angular.module("FICApp", ["ngRoute", "ngSanitize"])
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when("/rules", {
controller: "HomeController",
templateUrl: "views/rules.html"
})
.when("/edit", {
controller: "MyTeamController",
templateUrl: "views/team-edit.html"
})
.when("/rank", {
controller: "RankController",
templateUrl: "views/rank.html"
})
.when("/register", {
controller: "RegisterController",
templateUrl: "views/register.html"
})
.when("/videos", {
controller: "VideosController",
templateUrl: "views/videos.html"
})
.when("/tags/:tag", {
controller: "TagController",
templateUrl: "views/tag.html"
})
.when("/:theme", {
controller: "ExerciceController",
templateUrl: "views/theme.html"
})
.when("/:theme/:exercice", {
controller: "ExerciceController",
templateUrl: "views/defi.html"
})
.when("/", {
controller: "HomeController",
templateUrl: "views/home.html"
})
.otherwise({
redirectTo: "/"
});
$locationProvider.html5Mode(true);
})
.run(function($rootScope) {
$rootScope.current_theme = 0;
$rootScope.current_exercice = 0;
$rootScope.current_tag = undefined;
})
.controller("DataController", function($sce, $scope, $http, $rootScope, $interval, $location) {
var actMenu = function() {
if ($scope.my && $scope.themes) {
var tags = {};
angular.forEach($scope.themes, function(theme, key) {
$scope.themes[key].exercice_solved = 0;
var last_exercice = null;
angular.forEach(theme.exercices, function(exercice, k) {
if (last_exercice != null)
last_exercice.next = k;
last_exercice = exercice;
if ($scope.my.exercices && $scope.my.exercices[k] && $scope.my.exercices[k].solved) {
$scope.themes[key].exercice_solved++;
}
angular.forEach(exercice.tags, function(tag) {
if (!tags[tag])
tags[tag] = {count: 1, solved: 0};
else
tags[tag].count += 1;
if ($scope.my.exercices && $scope.my.exercices[k] && $scope.my.exercices[k].solved)
tags[tag].solved += 1;
});
});
});
$scope.tags = tags;
$scope.tagsl = Object.keys(tags).sort();
}
}
var refreshSettingsInterval
var refreshSettings = function() {
if (refreshSettingsInterval)
$interval.cancel(refreshSettingsInterval);
refreshSettingsInterval = $interval(refreshSettings, Math.floor(Math.random() * 24000) + 32000);
$http.get("/settings.json").then(function(response) {
$rootScope.recvTime(response);
response.data.start = new Date(response.data.start);
response.data.end = new Date(response.data.end);
response.data.generation = new Date(response.data.generation);
$rootScope.settings = response.data;
});
}
var refreshThemesInterval
var refreshThemes = function() {
if (refreshThemesInterval)
$interval.cancel(refreshThemesInterval);
refreshThemesInterval = $interval(refreshThemes, Math.floor(Math.random() * 24000) + 32000);
$http.get("/themes.json").then(function(response) {
$scope.themes = response.data;
$scope.max_gain = 0;
$scope.max_solved = 0;
$scope.themesUrl = {};
$scope.exercicesUrl = {};
angular.forEach(response.data, function(theme, key) {
$scope.themesUrl[theme.urlid] = key;
this[key].exercice_count = Object.keys(theme.exercices).length;
this[key].exercice_coeff_max = 0;
this[key].gain = 0;
this[key].solved = 0;
angular.forEach(theme.exercices, function(ex, k) {
$scope.exercicesUrl[theme.urlid + "/" + ex.urlid] = k;
this.gain += ex.gain;
this.solved += ex.solved;
this.exercice_coeff_max = Math.max(this.exercice_coeff_max, ex.curcoeff);
}, theme);
$scope.max_gain += theme.gain;
$scope.max_solved = Math.max($scope.max_solved, theme.solved);
}, response.data);
actMenu();
});
}
var refreshTeamsInterval;
$rootScope.refreshTeams = function() {
if (refreshTeamsInterval)
$interval.cancel(refreshTeamsInterval);
refreshTeamsInterval = $interval($rootScope.refreshTeams, Math.floor(Math.random() * 24000) + 32000);
$http.get("/teams.json").then(function(response) {
var teams = response.data;
$scope.teams_count = Object.keys(teams).length
$scope.teams = teams;
$scope.rank = [];
angular.forEach($scope.teams, function(team, tid) {
team.id = tid;
this.push(team);
}, $scope.rank);
});
}
var refreshMyInterval;
var refreshMy = function() {
if (refreshMyInterval)
$interval.cancel(refreshMyInterval);
refreshMyInterval = $interval(refreshMy, Math.floor(Math.random() * 24000) + 24000);
$http.get("/my.json").then(function(response) {
$rootScope.recvMy(response.data);
}, function(response) {
if (!$scope.my && response.status == 404) {
$location.url("/register");
}
});
}
$rootScope.recvMy = function(data) {
if (data.team_id == 0) {
angular.forEach(data.exercices, function(exercice, eid) {
angular.forEach(exercice.hints, function(hint, hid) {
if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].hints[hid] && $scope.my.exercices[eid].hints[hid].hidden !== undefined)
data.exercices[eid].hints[hid].hidden = $scope.my.exercices[eid].hints[hid].hidden;
else
data.exercices[eid].hints[hid].hidden = true;
});
});
}
angular.forEach(data.exercices, function(exercice, eid) {
angular.forEach(exercice.flags, function(flag, fid) {
if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].value !== undefined)
data.exercices[eid].flags[fid].value = $scope.my.exercices[eid].flags[fid].value;
if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].values !== undefined)
data.exercices[eid].flags[fid].values = $scope.my.exercices[eid].flags[fid].values;
else
data.exercices[eid].flags[fid].values = [""];
});
});
angular.forEach(data.exercices, function(exercice, eid) {
angular.forEach(exercice.mcqs, function(mcq, mid) {
angular.forEach(mcq.choices, function(choice, cid) {
if (!(choice instanceof Object))
this[cid] = {
label: choice,
};
this[cid].disabled = mcq.solved || mcq.part_solved || this[cid].solved;
if (!this[cid].help)
this[cid].help = "Flag correspondant";
if (!this[cid].disabled)
this[cid].value = $scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid] && $scope.my.exercices[eid].mcqs[mid].choices[cid].value
if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid]) {
if ($scope.my.exercices[eid].mcqs[mid].choices[cid].justify !== undefined)
this[cid].justify = $scope.my.exercices[eid].mcqs[mid].choices[cid].justify;
}
}, mcq.choices);
});
});
$scope.my = data;
angular.forEach($scope.my.exercices, function(exercice, eid) {
exercice.solved = exercice.solved_rank > 0;
if (exercice.video_uri) {
exercice.video_uri = $sce.trustAsResourceUrl(exercice.video_uri);
}
});
actMenu();
}
$rootScope.refresh = function(justMy) {
if (!justMy) {
refreshSettings();
refreshThemes();
$rootScope.refreshTeams();
}
refreshMy();
}
$rootScope.refresh();
})
.controller("ExerciceController", function($scope, $routeParams, $http, $rootScope, $timeout) {
if (window.scrollY > 112)
window.scrollTo(window.scrollX, 112);
$rootScope.current_tag = undefined;
$scope.$watch("themesUrl", function(themesUrl) {
if (themesUrl != undefined)
$rootScope.current_theme = themesUrl[$routeParams.theme];
})
if ($routeParams.exercice) {
$scope.$watch("exercicesUrl", function(exercicesUrl) {
if (exercicesUrl != undefined)
$rootScope.current_exercice = exercicesUrl[$routeParams.theme + "/" + $routeParams.exercice];
})
} else {
$rootScope.current_exercice = 0;
}
var cbh;
$scope.hsubmit = function(hint) {
hint.submitted = true;
$scope.hinterror = null;
$http({ url: "/openhint/" + $rootScope.current_exercice, method: "POST", data: { id: hint.id } }).then(function(response) {
var checkDiffHint = function() {
$http.get("/my.json").then(function(response) {
var my = response.data;
angular.forEach(my.exercices[$rootScope.current_exercice].hints, function(h,hid){
if (hint.id == h.id) {
if (hint.content != h.content) {
$rootScope.recvMy(my);
} else {
if (cbh)
$timeout.cancel(cbh);
cbh = $timeout(checkDiffHint, 750);
}
}
});
});
};
checkDiffHint();
}, function(response) {
$scope.hinterror = response.data.errmsg;
hint.submitted = false;
});
};
})
.controller("SubmissionController", function($scope, $http, $rootScope, $timeout) {
$scope.sberr = null;
var cbs;
var cbd;
$scope.additem = function(key) {
key.values.push("");
};
$scope.ssubmit = function() {
var resp = {}
var check = undefined
$scope.sberr = null;
if ($scope.my.exercices[$rootScope.current_exercice].flags && Object.keys($scope.my.exercices[$rootScope.current_exercice].flags).length)
{
resp["flags"] = {};
angular.forEach($scope.my.exercices[$rootScope.current_exercice].flags, function(flag,kid) {
if (flag.values !== undefined) {
if (flag.separator) {
for (var i = flag.values.length - 1; i >= 0; i--) {
if (!flag.values[i].length)
flag.values.splice(i, 1);
}
if (flag.ignoreorder)
flag.value = flag.values.slice().sort().join(flag.separator) + flag.separator;
else
flag.value = flag.values.join(flag.separator) + flag.separator;
if (flag.values.length == 0)
flag.values = [""];
}
else
flag.value = flag.values[0];
}
if (flag.found == null) {
if (flag.soluce !== undefined) {
if (check === undefined) check = true;
if (flag.value && flag.soluce == b2sum(flag.value))
flag.found = new Date();
check &= flag.found;
} else {
resp["flags"][kid] = flag.value;
}
}
});
}
if ($scope.my.exercices[$rootScope.current_exercice].mcqs && Object.keys($scope.my.exercices[$rootScope.current_exercice].mcqs).length)
{
var soluce = "";
resp["mcqs"] = {};
angular.forEach($scope.my.exercices[$rootScope.current_exercice].mcqs, function(mcq) {
if (mcq.solved == null) {
angular.forEach(mcq.choices, function(choice, cid) {
if (mcq.soluce !== undefined) {
if (check === undefined) check = true;
soluce += choice.value ? "t" : "f";
} else {
if (choice.value) {
resp["mcqs"][cid] = choice.value;
if (mcq.justify !== undefined) {
if (resp["justifications"] == undefined)
resp["justifications"] = {};
if (choice.justify)
resp["justifications"][cid] = choice.justify;
}
}
}
});
if (mcq.soluce !== undefined) {
if (mcq.soluce == b2sum(soluce))
mcq.solved = new Date();
check &= mcq.solved;
}
}
});
}
if (check !== undefined)
{
$scope.my.exercices[$rootScope.current_exercice].tries += 1;
$scope.my.exercices[$rootScope.current_exercice].solved_time = new Date();
if (check) {
$scope.my.exercices[$rootScope.current_exercice].solved = true;
}
return;
}
$http({ url: "/submit/" + $rootScope.current_exercice, method: "POST", data: resp }).then(function(response) {
$scope.messageClass = {"text-success": true};
$scope.message = response.data.errmsg;
$scope.sberr = null;
angular.forEach($scope.flags, function(flag,kid) {
flag.value = "";
});
var checkDiff = function() {
$http.get("/my.json").then(function(response) {
var my = response.data;
if ($scope.my.exercices[$rootScope.current_exercice].tries != my.exercices[$rootScope.current_exercice].tries || $scope.my.exercices[$rootScope.current_exercice].solved_time != my.exercices[$rootScope.current_exercice].solved_time) {
$rootScope.recvMy(my);
$rootScope.refreshTeams();
} else {
if (cbd)
$timeout.cancel(cbd);
cbd = $timeout(checkDiff, 750);
}
});
};
checkDiff();
}, function(response) {
if (response.status >= 500) {
$scope.my.exercices[$rootScope.current_exercice].submitted = false;
}
$scope.messageClass = {"text-danger": true};
$scope.sberr = "Oups !";
$scope.message = response.data.errmsg;
});
$scope.my.exercices[$rootScope.current_exercice].submitted = true;
};
$scope.wantchoices = function(kid) {
$scope.my.exercices[$rootScope.current_exercice].flags[kid].wcsubmitted = true;
$http({ url: "/wantchoices/" + $rootScope.current_exercice, method: "POST", data: { id: Math.floor(kid) } }).then(function(response) {
var checkDiffWC = function() {
$http.get("/my.json").then(function(response) {
var my = response.data;
if (my.exercices[$rootScope.current_exercice].flags[kid].choices)
$rootScope.recvMy(my);
else {
if (cbd)
$timeout.cancel(cbd);
cbd = $timeout(checkDiffWC, 750);
}
});
};
checkDiffWC();
}, function(response) {
$scope.messageClass = {"text-danger": true};
$scope.sberr = "Oups !";
$scope.message = response.data.errmsg;
$scope.my.exercices[$rootScope.current_exercice].flags[kid].wcsubmitted = false;
});
}
})
.controller("MyTeamController", function($scope, $http, $rootScope, $timeout) {
$rootScope.current_theme = 0;
$rootScope.current_exercice = 0;
$rootScope.current_tag = undefined;
if ($scope.my) {
$rootScope.title = $scope.my.name;
$rootScope.authors = $scope.my.members.map(function (cur) {
return cur.firstname.capitalize() + " " + cur.lastname.capitalize();
}).join(", ");
}
$scope.newName = "";
$rootScope.message = "";
$rootScope.sberr = "";
var cbt;
$scope.tsubmit = function() {
$rootScope.sberr = "";
if ($scope.newName.length < 1) {
$rootScope.messageClass = {"text-danger": true};
$rootScope.sberr = "Nom d'équipe invalide: pas d'entrée.";
return false;
}
else if ($scope.newName.length > 32) {
$rootScope.messageClass = {"text-danger": true};
$rootScope.sberr = "Nom d'équipe invalide: pas plus de 32 caractères.";
return false;
}
else if (!$scope.newName.match(/^[A-Za-z0-9 àéèêëîïôùûü_-]+$/)) {
$rootScope.messageClass = {"text-danger": true};
$rootScope.sberr = "Nom d'équipe invalide: seuls les caractères alpha-numériques sont autorisés.";
return false;
}
$http({
url: "/submit/name",
method: "POST",
data: {newName: $scope.newName}
}).then(function(response) {
$rootScope.messageClass = {"text-success": true};
$rootScope.message = response.data.errmsg;
var checkDiff = function() {
$http.get("/my.json").then(function(response) {
if ($scope.my.name != response.data.name) {
$scope.newName = "";
$rootScope.message = "";
$rootScope.recvMy(response.data);
$rootScope.refreshTeams();
} else {
if (cbt)
$timeout.cancel(cbt);
cbt = $timeout(checkDiff, 750);
}
});
};
checkDiff();
}, function(response) {
$rootScope.messageClass = {"text-danger": true};
$rootScope.message = response.data.errmsg;
if (response.status != 402) {
$rootScope.sberr = "Une erreur est survenue lors de l'envoi. Veuillez réessayer dans quelques instants.";
}
});
};
})
.controller("RegisterController", function($scope, $rootScope, $location, $http, $interval) {
$rootScope.current_theme = 0;
$rootScope.current_exercice = 0;
$rootScope.current_tag = undefined;
$rootScope.title = "Bienvenue au challenge forensic !";
$rootScope.authors = null;
$scope.form = {"teamName": "", "members": [{}]};
$scope.AddMember = function() {
$scope.form.members.push({});
}
$scope.RemoveMember = function(k) {
$scope.form.members.splice(k, 1);
}
$scope.Validate = function() {
if ($scope.form.teamName.length <= 3) {
$('#teamName').addClass("is-invalid")
return;
} else {
$('#vldBtn').removeClass("input-group-btn");
$('#vldBtn').css("display", "none");
$scope.part2 = true;
}
}
$scope.rsubmit = function() {
if (!$scope.part2)
return $scope.Validate();
// Remove empty members
$scope.form.members = $scope.form.members.filter(function(m) {
return ((m.lastname != undefined && m.lastname != "") || (m.firstname != undefined && m.firstname != "") || (m.nickname != undefined && m.nickname != ""));
});
if ($scope.form.members.length == 0) {
$scope.messageClass = {"text-danger": true};
$scope.message = "Veuillez ajouter au moins un membre dans votre équipe !";
$scope.form.members.push({});
return;
}
$http({
url: "/registration",
method: "POST",
data: $scope.form
}).then(function(response) {
$scope.messageClass = {"text-success": true};
$scope.message = response.data.errmsg;
$interval(function(){
$http.get("/my.json").then(function(response) {
$rootScope.refresh();
});
}, 1500);
}, function(response) {
$scope.messageClass = {"text-danger": true};
if (response.data && response.data.errmsg)
$scope.message = response.data.errmsg;
else
$scope.message = "Une erreur est survenue lors de l'inscription de l'équipe. Veuillez réessayer dans quelques instants.";
});
}
$scope.$watch("my", function(my){
if (my)
$location.url("/");
});
})
.controller("TagController", function($scope, $rootScope, $routeParams, $location) {
$rootScope.current_theme = 0;
$rootScope.current_exercice = 0;
$rootScope.current_tag = $routeParams.tag;
$rootScope.title = "Challenges " + $routeParams.tag;
$rootScope.authors = "";
$scope.tag = $routeParams.tag;
$scope.exercices = [];
$scope.$watch("themes", function(themes) {
$scope.exercices = [];
angular.forEach(themes, function(theme, tid) {
angular.forEach(theme.exercices, function(exercice, eid) {
if (exercice.tags.indexOf($scope.tag) >= 0)
$scope.exercices.push({"exercice": exercice, "theme": theme, "eid": eid, "tid": tid});
})
})
})
$scope.goDefi = function() {
$location.url("/" + this.ex.theme.urlid + "/" + this.ex.exercice.urlid);
}
})
.controller("RankController", function($scope, $rootScope) {
$rootScope.current_theme = 0;
$rootScope.current_exercice = 0;
$rootScope.current_tag = undefined;
$rootScope.title = "Classement général";
$rootScope.authors = "";
$scope.fields = ["rank", "name", "score"];
$scope.rankOrder = "rank";
$scope.reverse = false;
$scope.order = function(fld) {
if ($scope.rankOrder == fld) {
$scope.reverse = !$scope.reverse;
} else {
$scope.rankOrder = fld;
$scope.reverse = (fld == "score");
}
};
})
.controller("VideosController", function($scope, $rootScope) {
$rootScope.current_theme = 0;
$rootScope.current_exercice = 0;
$rootScope.current_tag = undefined;
$rootScope.title = "Vidéos de résolution";
$rootScope.authors = "";
})
.controller("HomeController", function($scope, $rootScope, $location) {
$rootScope.current_theme = 0;
$rootScope.current_exercice = 0;
$rootScope.current_tag = undefined;
$rootScope.title = "Bienvenue au challenge forensic de l'ÉPITA !";
$rootScope.authors = "";
$scope.goTheme = function() {
$location.url("/" + this.theme.urlid);
}
});
function sready() {
if ($("#solution").val().length) {
$("#sbmt").removeClass("disabled");
} else {
$("#sbmt").addClass("disabled");
}
};