X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=js%2Findex.js;h=d9f45762b2fb2ac758ed3a1b33bcc036850b8516;hb=3480360cddb841d799891d8f120a0d5356c6e935;hp=944a51b2a548b796465e981ffbc168f96a4863b9;hpb=7a00c4090c05f7c5f9d9b1b49bcf749b90f95c06;p=westcastle.git diff --git a/js/index.js b/js/index.js index 944a51b..d9f4576 100644 --- a/js/index.js +++ b/js/index.js @@ -6,10 +6,10 @@ new Vue({ }, components: { 'my-players': { - props: ['players'], + props: ['players','initPlayers'], template: `
-
+

Présents

@@ -18,15 +18,21 @@ new Vue({
-
+

Absents

- +
{{ p.prenom }} {{ p.nom }}
+
+ + +
`, computed: { @@ -41,80 +47,49 @@ new Vue({ methods: { toggleAvailability: function(i) { this.players[i].available = 1 - this.players[i].available; - this.$forceUpdate(); //TODO (Vue.set... ?!) }, - }, - }, - 'my-ranking': { - props: ['players'], - data: function() { - return { - sortMethod: "score", - }; - }, - template: ` -
- - - - - - - - - - - - - -
RangJoueurScorePdT
{{ i+1 }}{{ p.prenom }} {{ p.nom }}{{ p.score }}{{ p.pdt }}
-
- `, - computed: { - sortedPlayers: function() { - let sortFunc = this.sortMethod == "score" - ? this.sortByScore - : this.sortByPdt; - return this.players - .map( p => { return p; }) //to not alter original array - .sort(sortFunc); + uploadTrigger: function() { + document.getElementById("upload").click(); }, - }, - methods: { - sortByScore: function(a,b) { - return b.score - a.score; - }, - sortByPdt: function(a,b) { - return b.pdt - a.pdt; + upload: function(e) { + let file = (e.target.files || e.dataTransfer.files)[0]; + var reader = new FileReader(); + reader.onloadend = ev => { + this.initPlayers(ev.currentTarget.result); + }; + reader.readAsText(file); }, }, }, 'my-pairings': { - props: ['players'], + props: ['players','commitScores'], data: function() { return { unpaired: [], tables: [], //array of arrays of players indices - scores: [], //scores for each table (3 or 4 players) - pdts: [], //"points de table" for each table (3 or 4 players) + sessions: [], //"mini-points" for each table currentIndex: -1, //table index for scoring + scored: [], //boolean for each table index }; }, template: `
- -
+ + +
+

Table {{ index+1 }}

- - - - - - + +
{{ players[i].prenom }} {{ players[i].nom }}{{ pdts[index][j] }}
  {{ players[i].prenom }} {{ players[i].nom }}{{ sessions[index][j] }}
@@ -128,23 +103,32 @@ new Vue({
- - + +
{{ players[tables[currentIndex][i]].prenom }} {{ players[tables[currentIndex][i]].nom }} + {{ players[tables[currentIndex][i]].prenom }} {{ players[tables[currentIndex][i]].nom }} +
-
- - +
+ + +
`, methods: { + // TODO: clic sur "Valider" télécharge la ronde courante + // TODO: mémoriser les appariements passés pour éviter que les mêmes joueurs se rencontrent plusieurs fois doPairings: function() { // Simple case first: 4 by 4 let tables = []; let currentTable = []; - let ordering = _.shuffle(_.range(this.players.length)); //TODO: take scores into account? + let ordering = _.shuffle(_.range(this.players.length)); for (let i=0; i { + if (t.length < 4) + t.push(0); //index of "Toto", ghost player + }); this.tables = tables; - this.scores = tables.map( t => { return []; }); //empty scores - this.pdts = tables.map( t => { return []; }); //empty pdts - }, - shuffle: function() { - this.doPairings(); + this.sessions = tables.map( t => { return []; }); //empty sessions + this.scored = tables.map( t => { return false; }); //nothing scored yet + this.currentIndex = -1; //required if reset while scoring }, showScoreForm: function(table,index) { - if (this.scores[index].length > 0) - return; //already scored - this.scores[index] = _.times(table.length, _.constant(0)); - this.pdts[index] = _.times(table.length, _.constant(0)); + if (this.sessions[index].length == 0) + this.sessions[index] = _.times(table.length, _.constant(0)); this.currentIndex = index; }, + closeScoreForm: function() { + if (!this.scored[this.currentIndex]) + this.sessions[this.currentIndex] = []; + this.currentIndex = -1; + }, + getPdts: function() { + let sortedSessions = this.sessions[this.currentIndex] + .map( (s,i) => { return {value:parseInt(s), index:i}; }) + .sort( (a,b) => { return b.value - a.value; }); + const ref_pdts = [4, 2, 1, 0]; + // NOTE: take care of ex-aequos (spread points subtotal) + let curSum = 0, curCount = 0, start = 0; + let sortedPdts = []; + for (let i=0; i<4; i++) + { + curSum += ref_pdts[i]; + curCount++; + if (i==3 || sortedSessions[i].value > sortedSessions[i+1].value) + { + let pdt = curSum / curCount; + for (let j=start; j<=i; j++) + sortedPdts.push(pdt); + curSum = 0; + curCount = 0; + start = i+1; + } + } + // Re-order pdts to match table order + let pdts = [0, 0, 0, 0]; + for (let i=0; i<4; i++) + pdts[sortedSessions[i].index] = sortedPdts[i]; + return pdts; + }, setScore: function() { - let sortedPdts = this.pdts[this.currentIndex] - .map( (s,i) => { return {value:s, index:i}; }) - .sort( (a,b) => { return parseInt(b.value) - parseInt(a.value); }); - let scores = [4, 2, 1, 0]; //TODO: biased for 3-players tables. TODO: ex-aequos ?! - for (let i=0; i +
+ {{ formattedTime }} +
+ +
+ `, + computed: { + formattedTime: function() { + let seconds = this.time % 60; + let minutes = Math.floor(this.time / 60); + return this.padToZero(minutes) + ":" + this.padToZero(seconds); + }, + divHeight: function() { + return screen.height; }, - writeScoreToDb: function() - { - let xhr = new XMLHttpRequest(); - xhr.open("POST", "scripts/rw_players.php"); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - let orderedPlayers = this.players - .map( p => { return Object.assign({}, p); }) //deep (enough) copy - .sort( (a,b) => { return b.score - a.score; }); - xhr.send("players="+encodeURIComponent(JSON.stringify(orderedPlayers))); + divWidth: function() { + return screen.width; + }, + }, + methods: { + padToZero: function(a) { + if (a < 10) + return "0" + a; + return a; + }, + pauseResume: function() { + this.running = !this.running; + if (this.running) + this.start(); + }, + reset: function(e) { + this.running = false; + this.time = this.initialTime; + }, + start: function() { + if (!this.running) + return; + if (this.time == 0) + { + new Audio("sounds/gong.mp3").play(); + this.running = false; + return; + } + if (this.time == this.initialTime) + new Audio("sounds/gong.mp3").play(); //gong at the beginning + setTimeout(() => { + if (this.running) + this.time--; + this.start(); + }, 1000); + }, + }, + created: function() { + this.reset(); + }, + }, + 'my-ranking': { + props: ['players','sortByScore','commitScores'], + template: ` +
+ + + + + + + + + + + + + +
RangJoueurPointsMini-pts
{{ p.rank }}{{ p.prenom }} {{ p.nom }}{{ p.pdt }}{{ p.session }}
+
+ + + +
+
+ `, + computed: { + sortedPlayers: function() { + let res = this.rankPeople(); + // Add rank information (taking care of ex-aequos) + let rank = 1; + for (let i=0; i { + p.pdt = 0; + p.session = 0; + p.available = 1; + }); + this.commitScores(); + document.getElementById("doPairings").click(); + } + }, + download: function() { + // Prepare file content + let content = "prénom,nom,pdt,session\n"; + this.players + .slice(1) //discard Toto + .sort(this.sortByScore) + .forEach( p => { + content += p.prenom + "," + p.nom + "," + p.pdt + "," + p.session + "\n"; + }); + // Prepare and trigger download link + let downloadAnchor = document.getElementById("download"); + downloadAnchor.setAttribute("download", "classement.csv"); + downloadAnchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content); + downloadAnchor.click(); }, }, }, }, created: function() { - let xhr = new XMLHttpRequest(); - let self = this; - xhr.onreadystatechange = function() { - if (this.readyState == 4 && this.status == 200) - { - let players = JSON.parse(xhr.responseText); - players.forEach( p => { - p.score = !!p.score ? parseInt(p.score) : 0; - p.pdt = !!p.pdt ? parseInt(p.pdt) : 0; - p.available = !!p.available ? p.available : 1; //use integer for fputcsv PHP func + let players = JSON.parse(localStorage.getItem("players")); + if (players !== null) + { + this.addToto(players); + this.players = players; + } + }, + methods: { + addToto: function(array) { + array.unshift({ //add ghost 4th player for 3-players tables + prenom: "Toto", + nom: "", + pdt: 0, + session: 0, + available: 0, + }); + }, + // Used both in ranking and pairings: + sortByScore: function(a,b) { + return b.pdt - a.pdt + (Math.atan(b.session - a.session) / (Math.PI/2)) / 2; + }, + commitScores: function() { + localStorage.setItem( + "players", + JSON.stringify(this.players.slice(1)) //discard Toto + ); + }, + // Used in players, reinit players array + initPlayers: function(csv) { + const allLines = csv + .split(/\r\n|\n|\r/) //line breaks + .splice(1); //discard header + let players = allLines + .filter( line => { return line.length > 0; }) //remove empty lines + .map( line => { + let parts = line.split(","); + let p = { prenom: parts[0], nom: parts[1] }; + p.pdt = parts.length > 2 ? parseFloat(parts[2]) : 0; + p.session = parts.length > 3 ? parseInt(parts[3]) : 0; + p.available = parts.length > 4 ? parts[4] : 1; + return p; }); - self.players = players; - } - }; - xhr.open("GET", "scripts/rw_players.php", true); - xhr.send(null); + this.addToto(players); + this.players = players; + this.commitScores(); //save players in memory + }, }, });