Commit | Line | Data |
---|---|---|
7a00c409 BA |
1 | new Vue({ |
2 | el: "#mahjong", | |
3 | data: { | |
4 | players: [], //array of objects, filled later | |
5 | display: "players", | |
6 | }, | |
7 | components: { | |
8 | 'my-players': { | |
48bee368 | 9 | props: ['players','initPlayers'], |
7a00c409 BA |
10 | template: ` |
11 | <div id="players"> | |
1369a09d | 12 | <div class="left"> |
7a00c409 BA |
13 | <p>Présents</p> |
14 | <table class="list"> | |
15 | <tr v-for="p in sortedPlayers" v-if="p.available" @click="toggleAvailability(p.index)"> | |
16 | <td>{{ p.prenom }}</td> | |
17 | <td>{{ p.nom }}</td> | |
18 | </tr> | |
19 | </table> | |
20 | </div> | |
1369a09d | 21 | <div id="inactive" class="right"> |
7a00c409 BA |
22 | <p>Absents</p> |
23 | <table class="list"> | |
fd4a69e4 | 24 | <tr v-for="p in sortedPlayers" v-if="!p.available && p.nom!=''" @click="toggleAvailability(p.index)"> |
7a00c409 BA |
25 | <td>{{ p.prenom }}</td> |
26 | <td>{{ p.nom }}</td> | |
27 | </tr> | |
28 | </table> | |
29 | </div> | |
48bee368 BA |
30 | <div class="clear"> |
31 | <input class="hide" id="upload" type="file" @change="upload"/> | |
32 | <button class="btn block cancel" @click="uploadTrigger()" title="Charge la liste des joueurs, en principe en début de tournoi"> | |
33 | (Ré)initialiser | |
34 | </button> | |
35 | </div> | |
7a00c409 BA |
36 | </div> |
37 | `, | |
38 | computed: { | |
39 | sortedPlayers: function() { | |
40 | return this.players | |
41 | .map( (p,i) => { return Object.assign({}, p, {index: i}); }) | |
42 | .sort( (a,b) => { | |
43 | return a.nom.localeCompare(b.nom); | |
44 | }); | |
45 | }, | |
46 | }, | |
47 | methods: { | |
48 | toggleAvailability: function(i) { | |
49 | this.players[i].available = 1 - this.players[i].available; | |
48bee368 BA |
50 | }, |
51 | uploadTrigger: function() { | |
52 | document.getElementById("upload").click(); | |
53 | }, | |
54 | upload: function(e) { | |
55 | let file = (e.target.files || e.dataTransfer.files)[0]; | |
56 | var reader = new FileReader(); | |
57 | reader.onloadend = ev => { | |
58 | this.initPlayers(ev.currentTarget.result); | |
59 | }; | |
60 | reader.readAsText(file); | |
7a00c409 BA |
61 | }, |
62 | }, | |
63 | }, | |
7a00c409 | 64 | 'my-pairings': { |
48bee368 | 65 | props: ['players','commitScores'], |
7a00c409 BA |
66 | data: function() { |
67 | return { | |
68 | unpaired: [], | |
69 | tables: [], //array of arrays of players indices | |
1d2d7593 | 70 | sessions: [], //"mini-points" for each table |
7a00c409 | 71 | currentIndex: -1, //table index for scoring |
1369a09d | 72 | scored: [], //boolean for each table index |
7a00c409 BA |
73 | }; |
74 | }, | |
75 | template: ` | |
76 | <div id="pairings"> | |
77 | <div v-show="currentIndex < 0"> | |
48bee368 | 78 | <div class="button-container-horizontal"> |
496ab82e | 79 | <button class="btn cancel" :class="{hide: tables.length==0}" @click="cancelRound()" title="Annule la ronde courante : tous les scores en cours seront perdus, et un nouveau tirage effectué. ATTENTION : action irréversible"> |
aa6ce1d0 | 80 | Annuler |
48bee368 | 81 | </button> |
aa6ce1d0 | 82 | <button id="doPairings" class="btn" :disabled="scored.some( s => { return !s; })" @click="doPairings()" title="Répartit les joueurs actifs aléatoirement sur les tables"> |
48bee368 BA |
83 | Nouvelle ronde |
84 | </button> | |
85 | </div> | |
1369a09d | 86 | <div class="pairing" v-for="(table,index) in tables" :class="{scored: scored[index]}" |
7a00c409 BA |
87 | @click="showScoreForm(table,index)"> |
88 | <p>Table {{ index+1 }}</p> | |
89 | <table> | |
90 | <tr v-for="(i,j) in table"> | |
fd4a69e4 | 91 | <td :class="{toto: players[i].prenom=='Toto'}">{{ players[i].prenom }} {{ players[i].nom }}</td> |
1d2d7593 | 92 | <td class="score"><span v-show="sessions[index].length > 0">{{ sessions[index][j] }}</span></td> |
7a00c409 | 93 | </tr> |
7a00c409 BA |
94 | </table> |
95 | </div> | |
96 | <div v-if="unpaired.length>0" class="pairing unpaired"> | |
97 | <p>Exempts</p> | |
98 | <div v-for="i in unpaired"> | |
99 | {{ players[i].prenom }} {{ players[i].nom }} | |
100 | </div> | |
101 | </div> | |
102 | </div> | |
103 | <div id="scoreInput" v-if="currentIndex >= 0"> | |
104 | <table> | |
105 | <tr v-for="(index,i) in tables[currentIndex]"> | |
fd4a69e4 BA |
106 | <td :class="{toto: players[tables[currentIndex][i]].prenom=='Toto'}"> |
107 | {{ players[tables[currentIndex][i]].prenom }} {{ players[tables[currentIndex][i]].nom }} | |
108 | </td> | |
48bee368 | 109 | <td><input type="text" v-model="sessions[currentIndex][i]" :disabled="scored[currentIndex]"/></td> |
7a00c409 BA |
110 | </tr> |
111 | </table> | |
1369a09d | 112 | <div class="button-container-horizontal"> |
496ab82e | 113 | <button :class="{hide:scored[currentIndex]}" class="btn validate" @click="setScore()" title="Enregistre le score dans la base"> |
48bee368 BA |
114 | Enregistrer |
115 | </button> | |
aa6ce1d0 | 116 | <button :class="{hide:!scored[currentIndex]}" class="btn cancel" @click="cancelScore()" title="Annule le score précédemment enregistré"> |
48bee368 BA |
117 | Annuler |
118 | </button> | |
119 | <button class="btn" @click="closeScoreForm()">Fermer</button> | |
7a00c409 BA |
120 | </div> |
121 | </div> | |
122 | </div> | |
123 | `, | |
124 | methods: { | |
aa6ce1d0 BA |
125 | // TODO: télécharger la ronde courante (faudrait aussi mémoriser les points...) |
126 | // --> je devrais séparer les components en plusieurs fichiers maintenant | |
496ab82e | 127 | cancelRound: function() { |
942ca610 BA |
128 | this.scored.forEach( (s,i) => { |
129 | if (s) | |
130 | { | |
131 | // Cancel this table | |
aa6ce1d0 BA |
132 | this.currentIndex = i; //TODO: clumsy. functions should take "index" as argument |
133 | this.cancelScore(); | |
942ca610 BA |
134 | } |
135 | }); | |
136 | this.currentIndex = -1; | |
137 | this.doPairings(); | |
496ab82e | 138 | }, |
7a00c409 | 139 | doPairings: function() { |
aa6ce1d0 | 140 | let rounds = JSON.parse(localStorage.getItem("rounds")) || []; |
942ca610 BA |
141 | if (this.scored.some( s => { return s; })) |
142 | { | |
143 | this.commitScores(); //TODO: temporary: shouldn't be here... (incremental commit) | |
942ca610 | 144 | rounds.push(this.tables); |
aa6ce1d0 | 145 | localStorage.setItem("rounds", JSON.stringify(rounds)); |
942ca610 | 146 | } |
aa6ce1d0 BA |
147 | this.currentIndex = -1; //required if reset while scoring |
148 | let tables = []; | |
149 | // 1) Pre-compute tables repartition (in numbers): depends on active players count % 4 | |
942ca610 | 150 | let activePlayers = this.players |
aa6ce1d0 | 151 | .map( (p,i) => { return Object.assign({}, p, {index:i}); }) |
942ca610 BA |
152 | .filter( p => { return p.available; }); |
153 | let repartition = _.times(Math.floor(activePlayers.length/4), _.constant(4)); | |
aa6ce1d0 BA |
154 | let remainder = activePlayers.length % 4; |
155 | if (remainder > 0) | |
156 | repartition.push(remainder); | |
157 | switch (remainder) | |
942ca610 BA |
158 | { |
159 | case 1: | |
160 | // Need 2 more | |
161 | if (repartition.length-1 >= 2) | |
162 | { | |
aa6ce1d0 BA |
163 | repartition[repartition.length-3] -- ; |
164 | repartition[repartition.length-2] -- ; | |
942ca610 BA |
165 | repartition[repartition.length-1] += 2; |
166 | } | |
167 | break; | |
168 | case 2: | |
169 | // Need 1 more | |
170 | if (repartition.length-1 >= 1) | |
171 | { | |
aa6ce1d0 BA |
172 | repartition[repartition.length-2] -- ; |
173 | repartition[repartition.length-1] ++ ; | |
942ca610 BA |
174 | } |
175 | break; | |
176 | } | |
aa6ce1d0 BA |
177 | // 2) Shortcut for round 1: just spread at random |
178 | if (rounds.length == 0) | |
7a00c409 | 179 | { |
aa6ce1d0 BA |
180 | let currentTable = []; |
181 | let ordering = _.shuffle(_.range(activePlayers.length)); | |
182 | let tableIndex = 0; | |
183 | ordering.forEach( i => { | |
184 | currentTable.push(activePlayers[i].index); | |
185 | if (currentTable.length == repartition[tableIndex]) | |
186 | { | |
187 | if (currentTable.length == 3) | |
188 | currentTable.push(0); //add Toto | |
189 | // flush | |
190 | tables.push(currentTable); | |
191 | currentTable = []; | |
192 | tableIndex++; | |
193 | } | |
194 | }); | |
7a00c409 | 195 | } |
aa6ce1d0 | 196 | else |
7a00c409 | 197 | { |
aa6ce1d0 BA |
198 | // General case after round 1: |
199 | // NOTE: alternative method, deterministic: player 1 never move, player 2 moves by 1, ...and so on | |
200 | // --> but this leads to inferior pairings (e.g. 2 tables 8 players) | |
201 | // ----- | |
202 | // 2bis) Compute the "meeting" matrix: who played who and how many times | |
203 | let meetMat = _.range(this.players.length).map( i => { | |
204 | return _.times(this.players.length, _.constant(0)); | |
205 | }); | |
206 | rounds.forEach( r => { //for each round | |
207 | r.forEach( t => { //for each table within round | |
208 | for (let i=0; i<4; i++) //TODO: these loops are ugly | |
209 | { | |
210 | for (let j=i+1; j<4; j++) | |
211 | meetMat[t[i]][t[j]]++; | |
212 | } | |
213 | }); | |
214 | }); | |
215 | // 3) Fill tables by minimizing row sums of meetMat | |
216 | const playersCount = activePlayers.length; | |
217 | repartition.forEach( r => { | |
218 | // Pick first player at random among active players, unless there is one unpaired guy | |
219 | let firstPlayer = this.unpaired[0]; //can be undefined | |
220 | if (!firstPlayer || activePlayers.length < playersCount) | |
7a00c409 | 221 | { |
aa6ce1d0 BA |
222 | let randIndex = _.sample( _.range(activePlayers.length) ); |
223 | firstPlayer = activePlayers[randIndex].index; | |
224 | activePlayers.splice(randIndex, 1); | |
225 | } | |
226 | else | |
227 | activePlayers.splice( activePlayers.findIndex( item => { return item.index == firstPlayer; }), 1 ); | |
228 | let table = [ firstPlayer ]; | |
229 | for (let i=1; i<r; i++) | |
230 | { | |
231 | // Minimize row sums of meetMat for remaining players | |
232 | let counts = []; | |
233 | activePlayers.forEach( u => { | |
234 | let count = 0; | |
235 | let candidate = u.index; | |
236 | table.forEach( p => { | |
237 | count += meetMat[p][candidate]; | |
238 | count += meetMat[candidate][p]; | |
239 | }); | |
240 | counts.push( {index:u.index, count:count } ); | |
7a00c409 | 241 | }); |
aa6ce1d0 BA |
242 | counts.sort( (a,b) => { return a.count - b.count; }); |
243 | table.push(counts[0].index); | |
244 | activePlayers.splice( activePlayers.findIndex( item => { return item.index == counts[0].index; }), 1 ); | |
7a00c409 | 245 | } |
aa6ce1d0 BA |
246 | if (table.length == 3) |
247 | table.push(0); //add Todo | |
248 | tables.push(table); | |
249 | }); | |
7a00c409 | 250 | } |
aa6ce1d0 BA |
251 | if (tables.length >= 1 && tables[tables.length-1].length < 3) |
252 | this.unpaired = tables.pop(); | |
253 | else | |
254 | this.unpaired = []; | |
7a00c409 | 255 | this.tables = tables; |
aa6ce1d0 BA |
256 | this.resetScores(); |
257 | }, | |
258 | resetScores: function() { | |
259 | this.sessions = this.tables.map( t => { return []; }); //empty sessions | |
260 | this.scored = this.tables.map( t => { return false; }); //nothing scored yet | |
7a00c409 BA |
261 | }, |
262 | showScoreForm: function(table,index) { | |
1369a09d BA |
263 | if (this.sessions[index].length == 0) |
264 | this.sessions[index] = _.times(table.length, _.constant(0)); | |
7a00c409 BA |
265 | this.currentIndex = index; |
266 | }, | |
48bee368 BA |
267 | closeScoreForm: function() { |
268 | if (!this.scored[this.currentIndex]) | |
269 | this.sessions[this.currentIndex] = []; | |
270 | this.currentIndex = -1; | |
271 | }, | |
272 | getPdts: function() { | |
1d2d7593 | 273 | let sortedSessions = this.sessions[this.currentIndex] |
ce0473b4 BA |
274 | .map( (s,i) => { return {value:parseInt(s), index:i}; }) |
275 | .sort( (a,b) => { return b.value - a.value; }); | |
48bee368 | 276 | const ref_pdts = [4, 2, 1, 0]; |
1369a09d BA |
277 | // NOTE: take care of ex-aequos (spread points subtotal) |
278 | let curSum = 0, curCount = 0, start = 0; | |
48bee368 | 279 | let sortedPdts = []; |
1369a09d | 280 | for (let i=0; i<4; i++) |
7a00c409 | 281 | { |
48bee368 | 282 | curSum += ref_pdts[i]; |
1369a09d BA |
283 | curCount++; |
284 | if (i==3 || sortedSessions[i].value > sortedSessions[i+1].value) | |
285 | { | |
286 | let pdt = curSum / curCount; | |
287 | for (let j=start; j<=i; j++) | |
48bee368 | 288 | sortedPdts.push(pdt); |
1369a09d BA |
289 | curSum = 0; |
290 | curCount = 0; | |
291 | start = i+1; | |
292 | } | |
48bee368 BA |
293 | } |
294 | // Re-order pdts to match table order | |
295 | let pdts = [0, 0, 0, 0]; | |
296 | for (let i=0; i<4; i++) | |
297 | pdts[sortedSessions[i].index] = sortedPdts[i]; | |
298 | return pdts; | |
299 | }, | |
300 | setScore: function() { | |
301 | let pdts = this.getPdts(); | |
302 | for (let i=0; i<4; i++) | |
303 | { | |
304 | this.players[this.tables[this.currentIndex][i]].pdt += pdts[i]; | |
1d2d7593 | 305 | this.players[this.tables[this.currentIndex][i]].session += parseInt(this.sessions[this.currentIndex][i]); |
7a00c409 | 306 | } |
48bee368 | 307 | Vue.set(this.scored, this.currentIndex, true); |
7a00c409 | 308 | this.currentIndex = -1; |
7a00c409 | 309 | }, |
aa6ce1d0 | 310 | cancelScore: function() { |
48bee368 BA |
311 | let pdts = this.getPdts(); |
312 | for (let i=0; i<4; i++) | |
313 | { | |
314 | this.players[this.tables[this.currentIndex][i]].pdt -= pdts[i]; | |
315 | this.players[this.tables[this.currentIndex][i]].session -= parseInt(this.sessions[this.currentIndex][i]); | |
316 | } | |
317 | Vue.set(this.scored, this.currentIndex, false); | |
6d7bb9ad | 318 | }, |
7a00c409 BA |
319 | }, |
320 | }, | |
2caa3889 BA |
321 | 'my-timer': { |
322 | data: function() { | |
323 | return { | |
324 | time: 0, //remaining time, in seconds | |
325 | running: false, | |
496ab82e BA |
326 | initialTime: 90, //1h30, in minutes |
327 | setter: false, | |
328 | setterTime: 0, //to input new initial time | |
2caa3889 BA |
329 | }; |
330 | }, | |
331 | template: ` | |
8d4d2300 | 332 | <div id="timer" :style="{lineHeight: divHeight + 'px', fontSize: 0.66*divHeight + 'px', width: divWidth + 'px', height: divHeight + 'px'}"> |
496ab82e | 333 | <div v-show="!setter" @click.left="pauseResume()" @click.right.prevent="reset()" :class="{timeout:time==0}"> |
2caa3889 BA |
334 | {{ formattedTime }} |
335 | </div> | |
496ab82e | 336 | <input type="text" autofocus id="setter" @keyup.enter="setTime()" @keyup.esc="setter=false" v-show="setter" v-model="setterTime"></input> |
2caa3889 BA |
337 | <img class="close-cross" src="img/cross.svg" @click="$emit('clockover')"/> |
338 | </div> | |
339 | `, | |
340 | computed: { | |
341 | formattedTime: function() { | |
342 | let seconds = this.time % 60; | |
343 | let minutes = Math.floor(this.time / 60); | |
344 | return this.padToZero(minutes) + ":" + this.padToZero(seconds); | |
345 | }, | |
8d4d2300 | 346 | divHeight: function() { |
6d7bb9ad BA |
347 | return screen.height; |
348 | }, | |
8d4d2300 BA |
349 | divWidth: function() { |
350 | return screen.width; | |
351 | }, | |
2caa3889 BA |
352 | }, |
353 | methods: { | |
496ab82e BA |
354 | setTime: function() { |
355 | this.initialTime = this.setterTime; | |
356 | this.setter = false; | |
357 | this.reset(); | |
358 | }, | |
2caa3889 BA |
359 | padToZero: function(a) { |
360 | if (a < 10) | |
361 | return "0" + a; | |
362 | return a; | |
363 | }, | |
364 | pauseResume: function() { | |
365 | this.running = !this.running; | |
366 | if (this.running) | |
367 | this.start(); | |
368 | }, | |
369 | reset: function(e) { | |
370 | this.running = false; | |
496ab82e | 371 | this.time = this.initialTime * 60; |
2caa3889 BA |
372 | }, |
373 | start: function() { | |
374 | if (!this.running) | |
375 | return; | |
376 | if (this.time == 0) | |
377 | { | |
378 | new Audio("sounds/gong.mp3").play(); | |
379 | this.running = false; | |
380 | return; | |
381 | } | |
aa6ce1d0 | 382 | if (this.time == this.initialTime * 60) |
3480360c | 383 | new Audio("sounds/gong.mp3").play(); //gong at the beginning |
2caa3889 BA |
384 | setTimeout(() => { |
385 | if (this.running) | |
386 | this.time--; | |
387 | this.start(); | |
388 | }, 1000); | |
389 | }, | |
390 | }, | |
391 | created: function() { | |
496ab82e | 392 | this.setterTime = this.initialTime; |
2caa3889 BA |
393 | this.reset(); |
394 | }, | |
496ab82e BA |
395 | mounted: function() { |
396 | let timer = document.getElementById("timer"); | |
397 | let keyDict = { | |
398 | 32: () => { this.setter = true; }, //Space | |
399 | 27: () => { this.setter = false; }, //Esc | |
400 | }; | |
401 | document.addEventListener("keyup", e => { | |
402 | if (timer.style.display !== "none") | |
403 | { | |
404 | let func = keyDict[e.keyCode]; | |
405 | if (!!func) | |
406 | { | |
407 | e.preventDefault(); | |
408 | func(); | |
409 | } | |
410 | } | |
411 | }); | |
412 | }, | |
2caa3889 | 413 | }, |
48b3a536 | 414 | 'my-ranking': { |
48bee368 | 415 | props: ['players','sortByScore','commitScores'], |
48b3a536 BA |
416 | template: ` |
417 | <div id="ranking"> | |
418 | <table class="ranking"> | |
419 | <tr class="title"> | |
420 | <th>Rang</th> | |
421 | <th>Joueur</th> | |
422 | <th>Points</th> | |
423 | <th>Mini-pts</th> | |
424 | </tr> | |
425 | <tr v-for="p in sortedPlayers"> | |
426 | <td>{{ p.rank }}</td> | |
427 | <td>{{ p.prenom }} {{ p.nom }}</td> | |
428 | <td>{{ p.pdt }}</td> | |
429 | <td>{{ p.session }}</td> | |
430 | </tr> | |
431 | </table> | |
432 | <div class="button-container-vertical" style="width:200px"> | |
48bee368 BA |
433 | <a id="download" href="#"></a> |
434 | <button class="btn" @click="download()" title="Télécharge le classement courant au format CSV">Télécharger</button> | |
435 | <button class="btn cancel" @click="resetPlayers()" title="Réinitialise les scores à zéro. ATTENTION: action irréversible"> | |
436 | Réinitialiser | |
437 | </button> | |
48b3a536 BA |
438 | </div> |
439 | </div> | |
440 | `, | |
441 | computed: { | |
442 | sortedPlayers: function() { | |
443 | let res = this.rankPeople(); | |
444 | // Add rank information (taking care of ex-aequos) | |
445 | let rank = 1; | |
446 | for (let i=0; i<res.length; i++) | |
447 | { | |
448 | if (i==0 || this.sortByScore(res[i],res[i-1]) == 0) | |
449 | res[i].rank = rank; | |
450 | else //strictly lower scoring | |
451 | res[i].rank = ++rank; | |
452 | } | |
453 | return res; | |
454 | }, | |
455 | }, | |
456 | methods: { | |
457 | rankPeople: function() { | |
458 | return this.players | |
459 | .slice(1) //discard Toto | |
48b3a536 BA |
460 | .sort(this.sortByScore); |
461 | }, | |
462 | resetPlayers: function() { | |
48bee368 BA |
463 | if (confirm('Êtes-vous sûr ?')) |
464 | { | |
465 | this.players | |
466 | .slice(1) //discard Toto | |
467 | .forEach( p => { | |
468 | p.pdt = 0; | |
469 | p.session = 0; | |
470 | p.available = 1; | |
471 | }); | |
472 | this.commitScores(); | |
473 | document.getElementById("doPairings").click(); | |
474 | } | |
475 | }, | |
476 | download: function() { | |
477 | // Prepare file content | |
478 | let content = "prénom,nom,pdt,session\n"; | |
48b3a536 BA |
479 | this.players |
480 | .slice(1) //discard Toto | |
48bee368 | 481 | .sort(this.sortByScore) |
48b3a536 | 482 | .forEach( p => { |
48bee368 | 483 | content += p.prenom + "," + p.nom + "," + p.pdt + "," + p.session + "\n"; |
48b3a536 | 484 | }); |
48bee368 BA |
485 | // Prepare and trigger download link |
486 | let downloadAnchor = document.getElementById("download"); | |
487 | downloadAnchor.setAttribute("download", "classement.csv"); | |
488 | downloadAnchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content); | |
489 | downloadAnchor.click(); | |
48b3a536 BA |
490 | }, |
491 | }, | |
492 | }, | |
7a00c409 BA |
493 | }, |
494 | created: function() { | |
48bee368 BA |
495 | let players = JSON.parse(localStorage.getItem("players")); |
496 | if (players !== null) | |
497 | { | |
498 | this.addToto(players); | |
499 | this.players = players; | |
500 | } | |
7a00c409 | 501 | }, |
ade10194 | 502 | methods: { |
48bee368 BA |
503 | addToto: function(array) { |
504 | array.unshift({ //add ghost 4th player for 3-players tables | |
505 | prenom: "Toto", | |
506 | nom: "", | |
507 | pdt: 0, | |
508 | session: 0, | |
509 | available: 0, | |
510 | }); | |
511 | }, | |
1369a09d | 512 | // Used both in ranking and pairings: |
ade10194 BA |
513 | sortByScore: function(a,b) { |
514 | return b.pdt - a.pdt + (Math.atan(b.session - a.session) / (Math.PI/2)) / 2; | |
515 | }, | |
48bee368 BA |
516 | commitScores: function() { |
517 | localStorage.setItem( | |
518 | "players", | |
519 | JSON.stringify(this.players.slice(1)) //discard Toto | |
520 | ); | |
521 | }, | |
522 | // Used in players, reinit players array | |
523 | initPlayers: function(csv) { | |
524 | const allLines = csv | |
525 | .split(/\r\n|\n|\r/) //line breaks | |
526 | .splice(1); //discard header | |
527 | let players = allLines | |
528 | .filter( line => { return line.length > 0; }) //remove empty lines | |
529 | .map( line => { | |
530 | let parts = line.split(","); | |
d940eb68 BA |
531 | let p = { prenom: parts[0], nom: parts[1] }; |
532 | p.pdt = parts.length > 2 ? parseFloat(parts[2]) : 0; | |
533 | p.session = parts.length > 3 ? parseInt(parts[3]) : 0; | |
534 | p.available = parts.length > 4 ? parts[4] : 1; | |
535 | return p; | |
48bee368 BA |
536 | }); |
537 | this.addToto(players); | |
538 | this.players = players; | |
55043689 | 539 | this.commitScores(); //save players in memory |
1369a09d | 540 | }, |
ade10194 | 541 | }, |
7a00c409 | 542 | }); |