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, $interval) { $rootScope.current_theme = 0; $rootScope.current_exercice = 0; $rootScope.current_tag = undefined; $rootScope.time = {}; $rootScope.recvTime = function(response) { sessionStorage.userService = angular.toJson({ "cu": Math.floor(response.headers("x-fic-time") * 1000), "he": (new Date()).getTime(), }); } function updTime() { if (sessionStorage.userService && $rootScope.settings) { var time = angular.fromJson(sessionStorage.userService); var settings = $rootScope.settings; var srv_cur = new Date(Date.now() + (time.cu - time.he)); if (Math.floor(settings.start / 1000) == Math.floor(srv_cur / 1000)) { $rootScope.refresh(true); } var remain = 0; if (settings.start == 0) { $rootScope.time = {}; return } else if (settings.start > srv_cur) { $rootScope.startIn = Math.floor((settings.start - srv_cur) / 1000); remain = settings.end - settings.start; } else if (settings.end > srv_cur) { $rootScope.startIn = 0; remain = settings.end - srv_cur; } remain = remain / 1000; if (remain < 0) { remain = 0; $rootScope.time.end = true; $rootScope.time.expired = true; } else if (remain < 60) { $rootScope.time.end = false; $rootScope.time.expired = true; } else { $rootScope.time.end = false; $rootScope.time.expired = false; } $rootScope.time.remaining = remain; $rootScope.time.hours = Math.floor(remain / 3600); $rootScope.time.minutes = Math.floor((remain % 3600) / 60); $rootScope.time.seconds = Math.floor(remain % 60); } } updTime(); $interval(updTime, 1000); }) .controller("DataController", function($sce, $scope, $http, $rootScope, $timeout, $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; } } $rootScope.refresh = function(justMy) { if (!justMy) { $timeout.cancel($scope.cbr); $scope.cbr = $timeout($rootScope.refresh, 42000); $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; }); $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[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(); }); $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); }); } $http.get("/my.json").then(function(response) { $scope.my = response.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(); if ($scope.my.team_id == 0) { angular.forEach($scope.my.exercices, function(exercice, eid) { angular.forEach(exercice.hints, function(hint, hid) { $scope.my.exercices[eid].hints[hid].hidden = true; }); }); } }, function(response) { if (!$scope.my && response.status == 404) { $location.url("/register"); } }); console.log("refresh!"); } $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.exercice]; }) } else { $rootScope.current_exercice = 0; } $scope.hsubmit = function(hint) { hint.submitted = true; $http({ url: "/openhint/" + $rootScope.current_exercice, method: "POST", data: { id: hint.id } }).then(function(response, status, header, config) { 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.refresh(); } else { $timeout.cancel($scope.cbh); $scope.cbh = $timeout(checkDiffHint, 750); } } }); }); }; checkDiffHint(); }, function(response, status, header, config) { hint.submitted = false; console.error(response.data.errmsg); }); }; }) .controller("SubmissionController", function($scope, $http, $rootScope, $timeout) { $rootScope.sberr = ""; $scope.$watch("my", function(my) { if (!my || !my.exercices || !my.exercices[$rootScope.current_exercice]) { $timeout.cancel($scope.cbs); $scope.cbs = $timeout(waitMy, 420); } else { angular.forEach(my.exercices[$rootScope.current_exercice].mcqs, function(mcq,qid) { angular.forEach(mcq["choices"], function(choice,cid) { this[cid] = { label: choice, value: mcq["checks_solved"] !== undefined && mcq["checks_solved"][cid] !== undefined && mcq["checks_solved"][cid] > 0 }; if (mcq["checks_solved"] !== undefined) { this[cid].disabled = mcq["checks_solved"][cid] === undefined || mcq["checks_solved"][cid] >= 0; this[cid].solved = mcq["checks_solved"][cid] !== undefined && mcq["checks_solved"][cid] >= 2; } }, mcq["choices"]); }, $scope.mcqs); } }); $scope.ssubmit = function() { var resp = {"flags":{}} var check = undefined if ($scope.my.exercices[$rootScope.current_exercice].flags && Object.keys($scope.my.exercices[$rootScope.current_exercice].flags).length) { angular.forEach($scope.my.exercices[$rootScope.current_exercice].flags, function(flag,kid) { 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) { var nid = 0; 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 (choice.justify) resp["flags"][mcq.justify[nid++]] = choice.justify; else if (!choice.solved) nid++; } } } }); 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, status, header, config) { $rootScope.messageClass = {"text-success": true}; $rootScope.message = response.data.errmsg; $rootScope.sberr = ""; 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.refresh(); $scope.my = my; } else { $timeout.cancel($scope.cbd); $scope.cbd = $timeout(checkDiff, 750); } }); }; checkDiff(); }, function(response, status, header, config) { if (status >= 500) { $scope.my.exercices[$rootScope.current_exercice].submitted = false; } $rootScope.messageClass = {"text-danger": true}; $rootScope.message = response.data.errmsg; if (status != 402) { $rootScope.sberr = "Une erreur est survenue lors de l'envoi. Veuillez réessayer dans quelques instants."; } }); $scope.my.exercices[$rootScope.current_exercice].submitted = true; }; }) .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 = ""; $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, status, header, config) { $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.refresh(); } else { $timeout.cancel($scope.cbt); $scope.cbt = $timeout(checkDiff, 750); } }); }; checkDiff(); }, function(response, status, header, config) { $rootScope.messageClass = {"text-danger": true}; $rootScope.message = response.data.errmsg; if (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) { $rootScope.current_theme = 0; $rootScope.current_exercice = 0; $rootScope.current_tag = undefined; $rootScope.title = "Bienvenue au challenge forensic !"; $rootScope.authors = null; $scope.members = [{}]; $scope.AddMember = function() { $scope.members.push({}); } $scope.RemoveMember = function(k) { $scope.members.splice(k, 1); } $scope.Validate = function() { if ($scope.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.members = $scope.members.filter(function(m) { return ((m.lastname != undefined && m.lastname != "") || (m.firstname != undefined && m.firstname != "") || (m.nickname != undefined && m.nickname != "")); }); if ($scope.members.length == 0) { $scope.messageClass = {"text-danger": true}; $scope.message = "Veuillez ajouter au moins un membre dans votre équipe !"; $scope.members.push({}); return; } $http({ url: "/registration", method: "POST", data: { teamName: $scope.teamName, members: $scope.members, } }).then(function(response, status, header, config) { $scope.messageClass = {"text-success": true}; $scope.message = response.data.errmsg; $rootScope.refresh(); if ($scope.my) $location.url("/"); }, function(response) { $scope.messageClass = {"text-danger": true}; console.log(response); 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."; }); } if ($scope.my) { $location.url("/"); } }) .controller("TagController", function($scope, $rootScope, $routeParams) { $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}); }) }) }) }) .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) { $rootScope.current_theme = 0; $rootScope.current_exercice = 0; $rootScope.current_tag = undefined; $rootScope.title = "Bienvenue au challenge forensic de l'ÉPITA !"; $rootScope.authors = ""; }); function sready() { if ($("#solution").val().length) { $("#sbmt").removeClass("disabled"); } else { $("#sbmt").addClass("disabled"); } };