angular.module("FICApp", ["ngRoute", "ngResource"]) .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/:exerciceId", { controller: "ExerciceController", templateUrl: "views/exercice.html" }) .when("/teams", { controller: "TeamsListController", templateUrl: "views/team-list.html" }) .when("/teams/:teamId", { controller: "TeamController", templateUrl: "views/team.html" }) .when("/teams/new", { controller: "TeamNewController", templateUrl: "views/team-new.html" }) .when("/", { templateUrl: "views/home.html" }); $locationProvider.html5Mode(true); }); angular.module("FICApp") .factory("Version", function($resource) { return $resource("/api/version") }) .factory("Team", function($resource) { return $resource("/api/teams/:teamId", { teamId: '@id' }, { 'save': {method: 'PATCH'}, }) }) .factory("TeamMember", function($resource) { return $resource("/api/teams/:teamId/members", { teamId: '@id' }) }) .factory("TeamMy", function($resource) { return $resource("/api/teams/:teamId/my.json", { teamId: '@id' }) }) .factory("Teams", function($resource) { return $resource("/api/teams/teams.json") }) .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", null, { 'save': {method: 'PATCH'}, }) }) .factory("Themes", function($resource) { return $resource("/api/themes/themes.json", null, { 'get': {method: 'GET'}, }) }) .factory("Exercice", function($resource) { return $resource("/api/exercices/:exerciceId") }); 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) { return input.capitalize(); } }) .filter("time", function() { return function(input) { if (input == undefined) { return "--"; } else if (input >= 10) { return input; } else { return "0" + input; } } }) .controller("VersionController", function($scope, Version) { $scope.v = Version.get(); }) .controller("ThemesListController", function($scope, Theme, $location) { $scope.themes = Theme.query(); $scope.fields = ["id", "name"]; $scope.show = function(id) { $location.url("/themes/" + id); }; }) .controller("ThemeController", function($scope, Theme, $routeParams) { $scope.theme = Theme.get({ themeId: $routeParams.themeId }); $scope.fields = ["name"]; $scope.saveTheme = function() { this.theme.$save({themeId: this.theme.themeId}); } }) .controller("ExercicesListController", function($scope, Exercice, $routeParams, $location) { $scope.exercices = Exercice.query({ themeId: $routeParams.themeId }); $scope.fields = ["id", "title", "statement", "videoURI"]; $scope.show = function(id) { $location.url("/themes/" + $routeParams.themeId + "/" + id); }; }) .controller("ExerciceController", function($scope, Theme, $routeParams) { $scope.exercice = Exercice.get({ themeId: $routeParams.themeId }); $scope.fields = ["name", "statement", "hint", "videoURI"]; $scope.saveTheme = function() { this.exercice.$save({ themeId: this.exercice.themeId, exerciceId: this.exercice.exerciceId}); } }) .controller("TeamsListController", function($scope, Team, $location) { $scope.teams = Team.query(); $scope.fields = ["id", "name", "initialName"]; $scope.show = function(id) { $location.url("/teams/" + id); }; }) .controller("TeamController", function($scope, Team, TeamMember, $routeParams) { $scope.team = Team.get({ teamId: $routeParams.teamId }); $scope.members = TeamMember.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); solvedByThemesPie("#pieThemes", res.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) { $scope.solved_exercices += 1; } }, 0); }); }) .controller("TeamNewController", function($scope, Team, $location) { $scope.contact = new Team({ }) }) .controller("PresenceController", function($scope, TeamPresence, $routeParams) { $scope.presence = TeamPresence.query({ teamId: $routeParams.teamId }); $scope.presence.$promise.then(function(res) { presenceCal("#presenceCal", res); }); }) .controller("CountdownController", function($scope, $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) { remain = time.st + time.du - 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; $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); } } $http.get("/time.json").success(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 - 10, 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, 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(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(26, 29)) .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 + "-02"; }); var rect = svg.selectAll(".quarter") .data(function(d) { return d3.time.minutes(new Date(2016, 1, d, 0), new Date(2016, 1, d, 24), 15); }) .enter().append("rect") .attr("width", cellSize) .attr("height", cellSize) .attr("class", function(d) { 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)); }