angular.module("FICApp", ["ngRoute", "ngResource", "ngSanitize"]) .config(function($routeProvider, $locationProvider) { $routeProvider .when("/themes", { controller: "ThemesListController", templateUrl: "views/theme-list.html" }) .when("/themes/:themeId", { controller: "ThemeController", templateUrl: "views/theme.html" }) .when("/themes/:themeId/exercices/:exerciceId", { controller: "ExerciceController", templateUrl: "views/exercice.html" }) .when("/settings", { controller: "SettingsController", templateUrl: "views/settings.html" }) .when("/exercices", { controller: "AllExercicesListController", templateUrl: "views/exercice-list.html" }) .when("/exercices/:exerciceId", { controller: "ExerciceController", templateUrl: "views/exercice.html" }) .when("/teams", { controller: "TeamsListController", templateUrl: "views/team-list.html" }) .when("/teams/print", { controller: "TeamsListController", templateUrl: "views/team-print.html" }) .when("/teams/:teamId", { controller: "TeamController", templateUrl: "views/team-edit.html" }) .when("/teams/:teamId/stats", { controller: "TeamController", templateUrl: "views/team-stats.html" }) .when("/public/:screenId", { controller: "PublicController", templateUrl: "views/public.html" }) .when("/events", { controller: "EventsListController", templateUrl: "views/event-list.html" }) .when("/events/:eventId", { controller: "EventController", templateUrl: "views/event.html" }) .when("/", { templateUrl: "views/home.html" }); $locationProvider.html5Mode(true); }); angular.module("FICApp") .factory("Version", function($resource) { return $resource("/api/version") }) .factory("Event", function($resource) { return $resource("/api/events/:eventId", { eventId: '@id' }, { 'update': {method: 'PUT'}, }) }) .factory("ROSettings", function($resource) { return $resource("/api/settings-ro.json") }) .factory("Settings", function($resource) { return $resource("/api/settings.json", null, { 'update': {method: 'PUT'}, }) }) .factory("Scene", function($resource) { return $resource("/api/public/:screenId", { screenId: '@id' }, { 'update': {method: 'PUT', isArray: true}, }) }) .factory("Team", function($resource) { return $resource("/api/teams/:teamId", { teamId: '@id' }, { 'update': {method: 'PUT'}, }) }) .factory("TeamMember", function($resource) { return $resource("/api/teams/:teamId/members", { teamId: '@id' }, { 'save': {method: 'PUT'}, }) }) .factory("TeamMy", function($resource) { return $resource("/api/teams/:teamId/my.json", { teamId: '@id' }) }) .factory("Teams", function($resource) { return $resource("/api/teams.json") }) .factory("TeamHistory", function($resource) { return $resource("/api/teams/:teamId/history.json", { teamId: '@id' }) }) .factory("TeamStats", function($resource) { return $resource("/api/teams/:teamId/stats.json", { teamId: '@id' }) }) .factory("TeamPresence", function($resource) { return $resource("/api/teams/:teamId/tries", { teamId: '@id' }) }) .factory("Theme", function($resource) { return $resource("/api/themes/:themeId", { themeId: '@id' }, { update: {method: 'PUT'} }); }) .factory("Themes", function($resource) { return $resource("/api/themes.json", null, { 'get': {method: 'GET'}, }) }) .factory("ThemedExercice", function($resource) { return $resource("/api/themes/:themeId/exercices/:exerciceId", { exerciceId: '@id' }, { update: {method: 'PUT'} }) }) .factory("Exercice", function($resource) { return $resource("/api/exercices/:exerciceId", { exerciceId: '@id' }, { update: {method: 'PUT'} }) }) .factory("ExerciceFile", function($resource) { return $resource("/api/exercices/:exerciceId/files/:fileId", { exerciceId: '@idExercice', fileId: '@id' }, { update: {method: 'PUT'} }) }) .factory("ExerciceHint", function($resource) { return $resource("/api/exercices/:exerciceId/hints/:hintId", { exerciceId: '@idExercice', hintId: '@id' }, { update: {method: 'PUT'} }) }) .factory("ExerciceKey", function($resource) { return $resource("/api/exercices/:exerciceId/keys/:keyId", { exerciceId: '@idExercice', keyId: '@id' }, { update: {method: 'PUT'} }) }); String.prototype.capitalize = function() { return this .toLowerCase() .replace( /(^|\s)([a-z])/g, function(m,p1,p2) { return p1+p2.toUpperCase(); } ); } angular.module("FICApp") .filter("capitalize", function() { return function(input) { if (input != undefined) return input.capitalize(); } }) .filter("toColor", function() { return function(input) { num >>>= 0; var b = num & 0xFF, g = (num & 0xFF00) >>> 8, r = (num & 0xFF0000) >>> 16, a = ( (num & 0xFF000000) >>> 24 ) / 255 ; return "#" + r.toString(16) + g.toString(16) + b.toString(16); } }) .filter("size", function() { var units = [ "o", "kio", "Mio", "Gio", "Tio", "Pio", "Eio", "Zio", "Yio", ] return function(input) { var res = input; var unit = 0; while (res > 1024) { unit += 1; res = res / 1024; } return (Math.round(res * 100) / 100) + " " + units[unit]; } }) .filter("cksum", function() { return function(input) { if (input == undefined) return input; var raw = atob(input).toString(16); var hex = ''; for (var i = 0; i < raw.length; i++ ) { var _hex = raw.charCodeAt(i).toString(16) hex += (_hex.length == 2 ? _hex : '0' + _hex); } return hex } }) .filter("time", function() { return function(input) { if (input == undefined) { return "--"; } else if (input >= 10) { return input; } else { return "0" + input; } } }) .directive('color', function() { return { require: 'ngModel', link: function(scope, ele, attr, ctrl){ ctrl.$formatters.unshift(function(num){ num >>>= 0; var b = num & 0xFF, g = (num & 0xFF00) >>> 8, r = (num & 0xFF0000) >>> 16, a = ( (num & 0xFF000000) >>> 24 ) / 255 ; return "#" + r.toString(16) + g.toString(16) + b.toString(16); }); ctrl.$parsers.unshift(function(viewValue){ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(viewValue); return result ? ( parseInt(result[1], 16) * 256 * 256 + parseInt(result[2], 16) * 256 + parseInt(result[3], 16) ) : 0; }); } }; }) .directive('integer', function() { return { require: 'ngModel', link: function(scope, ele, attr, ctrl){ ctrl.$parsers.unshift(function(viewValue){ return parseInt(viewValue, 10); }); } }; }) .directive('float', function() { return { require: 'ngModel', link: function(scope, ele, attr, ctrl){ ctrl.$parsers.unshift(function(viewValue){ return parseFloat(viewValue, 10); }); } }; }) .controller("VersionController", function($scope, Version) { $scope.v = Version.get(); }) .controller("DIWEBoxController", function($scope, $rootScope, $interval, $timeout) { function updBox() { while ($rootScope._newBoxes.length > 0) { var b = $rootScope._newBoxes.shift(); $scope.boxes.unshift(b); var id = $scope.boxes.length - 1; b.cancel = function() { $scope.boxes.pop($scope.boxes.indexOf(b)); } if (b.timeout >= 0) $timeout(function() { b.cancel(); }, b.timeout); } } $rootScope._newBoxes = new Array(); $rootScope.newBox = function(kind, title, msg, tmout) { if (kind === undefined) { kind = 'default'; } if (msg === undefined) { msg = ''; } if (tmout === undefined) { tmout = 5000; } var mybox = { 'kind': kind, 'title': title, 'timeout': tmout, }; if (Array.isArray(msg)) mybox['list'] = msg; else mybox['msg'] = msg; $rootScope._newBoxes.push(mybox); }; $rootScope.newYesNoBox = function(kind, title, msg, yesFunc, noFunc, tmout) { if (kind === undefined) { kind = 'default'; } if (msg === undefined) { msg = ''; } if (tmout === undefined) { tmout = 5000; } var mybox; var yesFn = function() { mybox.cancel(); if (yesFunc !== undefined) { yesFunc(); } } var noFn = function() { mybox.cancel(); if (noFunc !== undefined) { noFunc(); } } mybox = { 'kind': kind, 'title': title, 'msg': msg, 'yes': yesFn, 'no': noFn, 'timeout': tmout, }; $rootScope._newBoxes.push(mybox); }; $scope.boxes = new Array(); updBox(); $interval(updBox, 750); }) .controller("SettingsController", function($scope, $rootScope, Settings, ROSettings, $location, $http) { $scope.config = Settings.get(); $scope.configro = ROSettings.get(); $scope.duration = 240; $scope.saveSettings = function(msg) { if (msg === undefined) { msg = 'New settings saved!'; } this.config.$update(function() { $rootScope.newBox('success', msg); }, function(response) { $rootScope.newBox('danger', 'An error occurs when saving settings:', response.data); }); } $scope.regenerate = function() { this.config.generation = (new Date()).toISOString(); $scope.saveSettings("Regeneration in progress..."); } $scope.launchChallenge = function() { var ts = Date.now() - Date.now() % 60000; var d = new Date(ts + 120000); this.config.start = d.toISOString(); var f = new Date(ts + 120000 + this.duration * 60000); this.config.end = f.toISOString(); $rootScope.newYesNoBox('info', 'Challenge ready to start,', 'propagate the changes?', function() { $scope.saveSettings(); }); } $scope.reset = function(type) { if (confirm("Êtes-vous sûr ?")) { $http.post("/api/reset", {"type": type}).then(function(time) { $rootScope.newBox('success', type + 'reseted'); $location.url("/"); }, function(response) { $rootScope.newBox('danger', 'An error occurs when reseting ' + type + ':', response.data); }); } }; }) .controller("PublicController", function($scope, $rootScope, $routeParams, $location, Scene, Theme, Teams, Exercice) { $scope.screens = [0,1,2,3,4,5,6,7,8,9]; $scope.screenid = $routeParams.screenId; $scope.scenes = Scene.query({ screenId: $routeParams.screenId }); $scope.themes = Theme.query(); $scope.teams = Teams.get(); $scope.chScreen = function(sid) { if ($scope.screenid) $location.url("/public/" + $scope.screenid); else $location.url("/public/"); } $scope.types = { "welcome": "Messages de bienvenue", "countdown": "Compte à rebours", "message": "Message", "panel": "Boîte", "exercice": "Exercice", "table": "Tableau", "rank": "Classement", }; $scope.welcome_types = { "teams": "Accueil des équipes", "public": "Accueil du public", }; $scope.colors = { "primary": "Primaire", "secondary": "Secondaire", "info": "Info", "success": "Success", "warning": "Warning", "danger": "Danger", "light": "Clair", "dark": "Foncé", }; $scope.rank_types = { "general": "Classement général", }; $scope.table_types = { "levels": "Niveaux d'exercices", "teams": "Équipes", }; $scope.exercices = Exercice.query(); $scope.clearScene = function() { $scope.someUpdt = true; $scope.scenes = []; }; $scope.presetScene = function(scene) { $scope.someUpdt = true; if (scene == "registration") $scope.scenes = [ { type: "welcome", params: { kind: "teams" }, }, { type: "welcome", params: { kind: "public", notitle: true }, }, ]; else if (scene == "welcome") $scope.scenes = [ { type: "welcome", params: { kind: "public" }, }, ]; else if (scene == "start") $scope.scenes = [ { type: "welcome", params: { kind: "public" }, }, { type: "countdown", params: { color: "success", end: null, lead: "Go, go, go !", title: "Le challenge forensic va bientôt commencer !" }, }, ]; else if (scene == "summary") { $scope.scenes = [ { type: "table", params: { kind: "levels", themes: $scope.themes.map(function(z, i) { return z.id; }), total: true }, }, { type: "rank", params: { limit: 5, which: "general" }, }, ]; } }; $scope.saveScenes = function() { $scope.someUpdt = false; var prms = Scene.update({ screenId: $scope.screenid }, $scope.scenes); prms.$promise.then(function() { $rootScope.newBox('success', 'Scene successfully published!'); }, function(response) { $rootScope.newBox('danger', 'An error occurs when saving scene:', response.data); }); }; $scope.addScene = function() { $scope.someUpdt = true; $scope.scenes.push({params: {}}); }; $scope.delScene = function(s) { $scope.someUpdt = true; angular.forEach($scope.scenes, function(scene, k) { if (scene == s) $scope.scenes.splice(k, 1); }); }; $scope.upScene = function(s) { $scope.someUpdt = true; angular.forEach($scope.scenes, function(scene, k) { if (scene == s && k > 0) { $scope.scenes.splice(k, 1); $scope.scenes.splice(k - 1, 0, scene); } }); }; $scope.downScene = function(s) { $scope.someUpdt = true; var move = true; angular.forEach($scope.scenes, function(scene, k) { if (move && scene == s) { $scope.scenes.splice(k, 1); $scope.scenes.splice(k + 1, 0, scene); move = false; } }); }; }) .controller("EventsListController", function($scope, Event, $location) { $scope.events = Event.query(); $scope.fields = ["id", "kind", "txt", "time"]; $scope.clearEvents = function(id) { Event.delete(function() { $scope.events = []; }); }; $scope.show = function(id) { $location.url("/events/" + id); }; }) .controller("EventController", function($scope, Event, $routeParams, $location) { $scope.event = Event.get({ eventId: $routeParams.eventId }); $scope.fields = ["kind", "txt", "time"]; $scope.kinds = { "alert-secondary": "Par défaut", "alert-primary": "Mise en valeur", "alert-info": "Info", "alert-warning": "Warning", "alert-success": "Success", "alert-danger": "Danger", "alert-light": "Clair", "alert-dark": "Foncé", }; $scope.saveEvent = function() { if (this.event.id) { this.event.$update(); } else { this.event.$save(function() { $location.url("/events/" + $scope.event.id); }); } } $scope.deleteEvent = function() { this.event.$remove(function() { $location.url("/events/");}); } }) .controller("ThemesListController", function($scope, Theme, $location, $rootScope, $http) { $scope.themes = Theme.query(); $scope.fields = ["id", "name"]; $scope.show = function(id) { $location.url("/themes/" + id); }; $scope.inSync = false; $scope.sync = function() { $scope.inSync = true; $http({ url: "/api/sync/themes", method: "GET" }).then(function(response) { $scope.inSync = false; $scope.themes = Theme.query(); if (response.data) $rootScope.newBox('danger', response.data); else $rootScope.newBox('success', 'Synchronisation de la liste des thèmes terminée avec succès.'); }, function(response) { $scope.inSync = false; $rootScope.newBox('danger', 'An error occurs when synchronizing theme list:', response.data); }); }; }) .controller("ThemeController", function($scope, Theme, $routeParams, $location, $rootScope, $http) { $scope.theme = Theme.get({ themeId: $routeParams.themeId }); $scope.fields = ["name", "authors"]; $scope.saveTheme = function() { if (this.theme.id) { this.theme.$update(); } else { this.theme.$save(function() { $location.url("/themes/" + $scope.theme.id); }); } } $scope.deleteTheme = function() { this.theme.$remove(function() { $location.url("/themes/");}); } $scope.inSync = false; $scope.syncExo = function() { $scope.inSync = true; $http({ url: "/api/sync/themes/" + $scope.theme.id + "/exercices", method: "GET" }).then(function(response) { $scope.inSync = false; $scope.theme = Theme.get({ themeId: $routeParams.themeId }); if (response.data) $rootScope.newBox('warning', null, response.data, -1); else $rootScope.newBox('success', 'Synchronisation de la liste des exercices terminée avec succès.'); }, function(response) { $scope.inSync = false; $rootScope.newBox('danger', 'An error occurs when synchrinizing exercices:', response.data); }); }; }) .controller("AllExercicesListController", function($scope, Exercice, $routeParams, $location, $rootScope, $http) { $scope.exercices = Exercice.query(); $scope.fields = ["title", "statement", "overview", "videoURI"]; $scope.show = function(id) { $location.url("/exercices/" + id); }; $scope.inSync = false; $scope.syncFull = function() { $scope.inSync = true; $scope.done = -1; $scope.total = 0; var work = []; var go = function() { if (!work.length) { $rootScope.newBox('info', "Synchronisation des exercices terminée."); $scope.inSync = false; return; } var u = work.pop(); $http({ url: u, method: "GET" }).then(function(response) { $scope.done += 1; go(); }, function(response) { $scope.done += 1; go(); }); }; angular.forEach($scope.exercices, function(ex) { if ($scope.syncFiles) work.push("/api/sync/exercices/" + ex.id + "/files"); if ($scope.syncHints) work.push("/api/sync/exercices/" + ex.id + "/hints"); if ($scope.syncKeys) work.push("/api/sync/exercices/" + ex.id + "/keys"); }); $scope.total = work.length; go(); }; $scope.syncFiles = true; $scope.syncHints = true; $scope.syncKeys = true; }) .controller("ExercicesListController", function($scope, ThemedExercice, $routeParams, $location) { $scope.exercices = ThemedExercice.query({ themeId: $routeParams.themeId }); $scope.fields = ["title", "statement", "overview", "videoURI"]; $scope.show = function(id) { $location.url("/themes/" + $routeParams.themeId + "/exercices/" + id); }; }) .controller("ExerciceController", function($scope, Exercice, ThemedExercice, $routeParams, $location) { if ($routeParams.themeId && $routeParams.exerciceId == "new") { $scope.exercice = new ThemedExercice(); } else { $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); } $scope.exercices = Exercice.query(); $scope.fields = ["title", "statement", "overview", "depend", "gain", "coefficient", "videoURI"]; $scope.saveExercice = function() { if (this.exercice.id) { this.exercice.$update(); } else if ($routeParams.themeId) { this.exercice.$save({ themeId: $routeParams.themeId }, function() { $location.url("/themes/" + $scope.exercice.idTheme + "/exercices/" + $scope.exercice.id); }); } } }) .controller("ExerciceFilesController", function($scope, ExerciceFile, $routeParams, $rootScope, $http) { $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); $scope.deleteFile = function() { this.file.$delete(function() { $scope.files.splice($scope.files.indexOf(this.file), 1); }); return false; } $scope.saveFile = function() { this.file.$update(); } $scope.inSync = false; $scope.syncFiles = function() { $scope.inSync = true; $http({ url: "/api/sync/exercices/" + $routeParams.exerciceId + "/files", method: "GET" }).then(function(response) { $scope.inSync = false; $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); if (response.data) $rootScope.newBox('danger', response.data); else $rootScope.newBox('success', "Synchronisation de la liste de drapeaux terminée avec succès."); }, function(response) { $scope.inSync = false; $rootScope.newBox('danger', 'An error occurs when synchronizing flags list:', response.data); }); }; }) .controller("ExerciceHintsController", function($scope, ExerciceHint, $routeParams, $rootScope, $http) { $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); $scope.addHint = function() { $scope.hints.push(new ExerciceHint()); } $scope.deleteHint = function() { this.hint.$delete(function() { $scope.hints.splice($scope.hints.indexOf(this.hint), 1); }); } $scope.saveHint = function() { if (this.hint.id) { this.hint.$update(); } else { this.hint.$save({ exerciceId: $routeParams.exerciceId }); } } $scope.inSync = false; $scope.syncHints = function() { $scope.inSync = true; $http({ url: "/api/sync/exercices/" + $routeParams.exerciceId + "/hints", method: "GET" }).then(function(response) { $scope.inSync = false; $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); if (response.data) $rootScope.newBox('danger', response.data); else $rootScope.newBox('success', "Synchronisation de la liste d'indices terminée avec succès."); }, function(response) { $scope.inSync = false; $rootScope.newBox('danger', 'An error occurs when synchronizing hints list:', response.data); }); }; }) .controller("ExerciceKeysController", function($scope, ExerciceKey, $routeParams, $rootScope, $http) { $scope.keys = ExerciceKey.query({ exerciceId: $routeParams.exerciceId }); $scope.addKey = function() { $scope.keys.push(new ExerciceKey()); } $scope.deleteKey = function() { this.key.$delete(function() { $scope.keys.splice($scope.keys.indexOf(this.key), 1); }); } $scope.saveKey = function() { if (this.key.id) { this.key.$update(); } else { this.key.$save({ exerciceId: $routeParams.exerciceId }); } } $scope.inSync = false; $scope.syncKeys = function() { $scope.inSync = true; $http({ url: "/api/sync/exercices/" + $routeParams.exerciceId + "/keys", method: "GET" }).then(function(response) { $scope.inSync = false; $scope.keys = ExerciceKey.query({ exerciceId: $routeParams.exerciceId }); if (response.data) $rootScope.newBox('danger', response.data); else $rootScope.newBox('success', "Synchronisation de la liste de drapeaux terminée avec succès."); }, function(response) { $scope.inSync = false; $rootScope.newBox('danger', 'An error occurs when synchronizing flags list:', response.data); }); }; }) .controller("TeamsListController", function($scope, Team, $location) { $scope.teams = Team.query(); $scope.fields = ["id", "name", "initialName"]; $scope.show = function(id) { $location.url("/teams/" + id); }; }) .controller("TeamMembersController", function($scope, TeamMember) { $scope.fields = ["firstname", "lastname", "nickname", "company"]; if ($scope.team != null) { $scope.members = TeamMember.query({ teamId: $scope.team.id }); $scope.newMember = function() { $scope.members.push(new TeamMember()); } $scope.saveTeamMembers = function() { if (this.team.id) { TeamMember.save({ teamId: this.team.id }, $scope.members); } } $scope.removeMember = function(member) { angular.forEach($scope.members, function(m, k) { if (member == m) $scope.members.splice(k, 1); }); } } }) .controller("TeamController", function($scope, $rootScope, $location, Team, TeamMember, $routeParams, $http) { $scope.team = Team.get({ teamId: $routeParams.teamId }); $scope.fields = ["name", "color"]; $scope.hasCertificate = false; $http({ url: "/api/teams/" + Math.floor($routeParams.teamId) + "/certificate.p12", method: "HEAD", transformResponse: null }).then(function(response) { $scope.hasCertificate = true; }, function(response) { $scope.hasCertificate = false; }); $scope.generateCertificate = function() { $http({ url: "/api/teams/" + Math.floor($routeParams.teamId) + "/certificate/generate", method: "GET", transformResponse: null }).then(function(response) { $scope.hasCertificate = true; $rootScope.newBox('success', 'Team certificate successfully generated!'); }, function(response) { $rootScope.newBox('danger', 'An error occurs when generating certiticate:', response.data); }); } $scope.revokeCertificate = function() { if (!confirm("Are you sure you want to revoke this certificate?")) return false; $http({ url: "/api/teams/" + Math.floor($routeParams.teamId) + "/certificate.p12", method: "DELETE", transformResponse: null }).then(function(response) { $scope.hasCertificate = false; }, function(response) { $rootScope.newBox('danger', 'An error occurs when revoking the certiticate:', response.data); }); } $scope.saveTeam = function() { if (this.team.id) { this.team.$update(); } else { this.team.$save(function() { $location.url("/teams/" + $scope.team.id); }); } } $scope.deleteTeam = function() { backName = this.team.name; this.team.$remove(function() { $rootScope.newBox('success', 'Team ' + backName + ' successfully removed.'); $location.url("/teams/"); }, function(response) { console.log($rootScope.newBox); $rootScope.newBox('danger', 'An error occurs during suppression of the team:', response.data); }); } $scope.showStats = function() { $location.url("/teams/" + $scope.team.id + "/stats"); } }) .controller("TeamHistoryController", function($scope, TeamHistory, $routeParams) { $scope.history = TeamHistory.query({ teamId: $routeParams.teamId }); }) .controller("TeamStatsController", function($scope, TeamStats, $routeParams) { $scope.teamstats = TeamStats.get({ teamId: $routeParams.teamId }); $scope.teamstats.$promise.then(function(res) { solvedByLevelPie("#pieLevels", res.levels); var themes = []; angular.forEach(res.themes, function(theme, tid) { themes.push(theme); }) solvedByThemesPie("#pieThemes", themes); }); }) .controller("TeamExercicesController", function($scope, Teams, Themes, TeamMy, Exercice, $routeParams) { $scope.teams = Teams.get(); $scope.themes = Themes.get(); $scope.exercices = Exercice.query(); $scope.my = TeamMy.get({ teamId: $routeParams.teamId }); $scope.teams.$promise.then(function(res){ $scope.nb_teams = 0; $scope.nb_reg_teams = Object.keys(res).length; angular.forEach(res, function(team, tid) { if (team.rank) $scope.nb_teams += 1; }, 0); }); $scope.my.$promise.then(function(res){ $scope.solved_exercices = 0; angular.forEach(res.exercices, function(exercice, eid) { if (exercice.solved_rank) { $scope.solved_exercices += 1; } }, 0); }); }) .controller("PresenceController", function($scope, TeamPresence, $routeParams) { $scope.presence = TeamPresence.query({ teamId: $routeParams.teamId }); $scope.presence.$promise.then(function(res) { presenceCal($scope, "#presenceCal", res); }); }) .controller("CountdownController", function($scope, $rootScope, $http, $timeout) { $scope.time = {}; function updTime() { $timeout.cancel($scope.cbm); $scope.cbm = $timeout(updTime, 1000); if (sessionStorage.userService) { var time = angular.fromJson(sessionStorage.userService); var srv_cur = (Date.now() + (time.cu * 1000 - time.he)) / 1000; var remain = time.du; if (time.st == Math.floor(srv_cur)) { $scope.refresh(true); } if (time.st > 0 && time.st <= srv_cur) { $scope.startIn = 0; remain = time.st + time.du - srv_cur; } else if (time.st > 0) { $scope.startIn = Math.floor(time.st - srv_cur); } if (remain < 0) { remain = 0; $scope.time.end = true; $scope.time.expired = true; } else if (remain < 60) { $scope.time.end = false; $scope.time.expired = true; } else { $scope.time.end = false; $scope.time.expired = false; } $scope.time.start = time.st * 1000; $scope.time.duration = time.du * 1000; $scope.time.remaining = remain; $scope.time.hours = Math.floor(remain / 3600); $scope.time.minutes = Math.floor((remain % 3600) / 60); $scope.time.seconds = Math.floor(remain % 60); $rootScope.time = $scope.time; } } $http.get("/time.json").then(function(time) { time.he = (new Date()).getTime(); sessionStorage.userService = angular.toJson(time); updTime(); }); }); function solvedByLevelPie(location, data) { var width = d3.select(location).node().getBoundingClientRect().width - parseInt(d3.select(location).style("padding-right")) - parseInt(d3.select(location).style("padding-left")), height = d3.select(location).node().getBoundingClientRect().width - 10, radius = Math.min(width, height) / 2, innerRadius = 0.1 * radius; var color = d3.scale.ordinal() .range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]); var pie = d3.layout.pie() .sort(null) .value(function(d) { return d.width; }); var arc = d3.svg.arc() .innerRadius(innerRadius) .outerRadius(function (d) { return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius; }); var outlineArc = d3.svg.arc() .innerRadius(innerRadius) .outerRadius(radius); var svg = d3.select(location).append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); data.forEach(function(d) { d.score = d.solved * 100 / d.total; d.width = d.tries + 1; }); var path = svg.selectAll(".solidArc") .data(pie(data)) .enter().append("path") .attr("fill", function(d) { return color(d.data.tip); }) .attr("class", "solidArc") .attr("stroke", "gray") .attr("d", arc); var outerPath = svg.selectAll(".outlineArc") .data(pie(data)) .enter().append("path") .attr("fill", "none") .attr("stroke", "gray") .attr("class", "outlineArc") .attr("d", outlineArc); var labelArc = d3.svg.arc() .outerRadius(0.8 * radius) .innerRadius(0.8 * radius); svg.selectAll(".labelArc") .data(pie(data)) .enter().append("text") .attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; }) .attr("dy", ".35em") .attr("text-anchor", "middle") .text(function(d) { return d.data.tip + ": " + d.data.solved + "/" + d.data.total; }); svg.selectAll(".label2Arc") .data(pie(data)) .enter().append("text") .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; }) .attr("dy", ".35em") .attr("text-anchor", "middle") .text(function(d) { return d.data.tries; }); } function solvedByThemesPie(location, data) { var width = d3.select(location).node().getBoundingClientRect().width - parseInt(d3.select(location).style("padding-right")) - parseInt(d3.select(location).style("padding-left")), height = d3.select(location).node().getBoundingClientRect().width, radius = Math.min(width, height) / 2, innerRadius = 0.1 * radius; var color = d3.scale.ordinal() .range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]); var pie = d3.layout.pie() .sort(null) .value(function(d) { return d.width; }); var arc = d3.svg.arc() .innerRadius(innerRadius) .outerRadius(function (d) { return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius; }); var outlineArc = d3.svg.arc() .innerRadius(innerRadius) .outerRadius(radius); var svg = d3.select(location).append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); data.forEach(function(d) { d.score = d.solved * 100 / d.total; d.width = d.tries + 0.5; }); var path = svg.selectAll(".solidArc") .data(pie(data)) .enter().append("path") .attr("fill", function(d) { return color(d.data.tip); }) .attr("class", "solidArc") .attr("stroke", "gray") .attr("d", arc); var outerPath = svg.selectAll(".outlineArc") .data(pie(data)) .enter().append("path") .attr("fill", "none") .attr("stroke", "gray") .attr("class", "outlineArc") .attr("d", outlineArc); svg.selectAll(".label2Arc") .data(pie(data)) .enter().append("text") .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; }) .attr("dy", ".35em") .attr("text-anchor", "middle") .text(function(d) { return d.data.solved; }); var labelArc = d3.svg.arc() .outerRadius(0.8 * radius) .innerRadius(0.8 * radius); svg.selectAll(".labelArc") .data(pie(data)) .enter().append("text") .attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; }) .attr("dy", ".35em") .attr("text-anchor", "middle") .text(function(d) { return d.data.tip + ": " + d.data.tries; }); } function presenceCal(scope, location, data) { var width = d3.select(location).node().getBoundingClientRect().width, height = 80, cellSize = 17; // cell size var percent = d3.format(".1%"), format = d3.time.format("%H:%M"); var color = d3.scale.quantize() .domain([0, 16]) .range(d3.range(8).map(function(d) { return "q" + d + "-8"; })); var svg = d3.select(location).selectAll("svg") .data(d3.range(scope.time.start, scope.time.start + (scope.time.start % 86400000 + scope.time.duration), 86400000).map(function(t) { return new Date(t); })) .enter().append("svg") .attr("width", width) .attr("height", height) .attr("class", "RdYlGn") .append("g") .attr("transform", "translate(" + ((width - cellSize * 24) / 2) + "," + (height - cellSize * 4 - 1) + ")"); svg.append("text") .attr("transform", "translate(-6," + cellSize * 2.6 + ")rotate(-90)") .style("text-anchor", "middle") .text(function(d) { return d.getDate() + "-" + (d.getMonth() + 1); }); var rect = svg.selectAll(".quarter") .data(function(d) { return d3.time.minutes(new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0), new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23), 15); }) .enter().append("rect") .attr("width", cellSize) .attr("height", cellSize) .attr("transform", function(d) { return "translate(" + (d.getHours() * cellSize) + "," + (d.getMinutes() / 15 * cellSize) + ")"; }) .attr("class", function(d) { if (d >= scope.time.start && d < scope.time.start + scope.time.duration) return color(data.reduce(function(prev, cur){ cur = new Date(cur).getTime(); dv = d.getTime(); return prev + ((dv <= cur && cur < dv+15*60000)?1:0); }, 0)); }); }