ea85ea752d3d461c115952340c66e51d3780d20d
[vchess.git] / client / src / components / UploadGame.vue
1 <template lang="pug">
2 div
3 input#upload(type="file" @change="upload")
4 button#uploadBtn(
5 @click="uploadTrigger()"
6 aria-label="store.state.tr['Upload a game']"
7 )
8 img.inline(src="/images/icons/upload.svg")
9 </template>
10
11 <script>
12 import { getRandString } from "@/utils/alea";
13 export default {
14 name: "my-upload-game",
15 methods: {
16 uploadTrigger: function() {
17 document.getElementById("upload").click();
18 },
19 upload: function(e) {
20 const file = (e.target.files || e.dataTransfer.files)[0];
21 var reader = new FileReader();
22 reader.onloadend = ev => {
23 this.parseAndEmit(ev.currentTarget.result);
24 };
25 reader.readAsText(file);
26 },
27 parseAndEmit: async function(pgn) {
28 let game = {
29 // Players potential ID and socket IDs are not searched
30 players: [
31 { id: 0, sid: "" },
32 { id: 0, sid: "" }
33 ]
34 };
35 const lines = pgn.split('\n');
36 let idx = 0;
37 // Read header
38 while (lines[idx].length > 0) {
39 // NOTE: not using "split(' ')" because the FEN has spaces
40 const spaceIdx = lines[idx].indexOf(' ');
41 const prop = lines[idx].substr(0, spaceIdx).match(/^\[(.*)$/)[1];
42 const value = lines[idx].substr(spaceIdx + 1).match(/^"(.*)"\]$/)[1];
43 switch (prop) {
44 case "Variant":
45 game.vname = value;
46 break;
47 case "Date":
48 game.created = new Date(value).getTime();
49 break;
50 case "White":
51 game.players[0].name = value;
52 break;
53 case "Black":
54 game.players[1].name = value;
55 break;
56 case "Fen":
57 game.fenStart = value;
58 break;
59 case "Result":
60 // Allow importing unfinished games, but mark them as
61 // "unknown result" to avoid running the clocks...
62 game.result = (value != "*" ? value : "?");
63 break;
64 case "Cadence":
65 game.cadence = value;
66 break;
67 }
68 idx++;
69 }
70 // Always generate random ID for imported games, because they could be
71 // downloaded at different states (prefix 'i' for 'Import').
72 game.id = 'i' + getRandString()
73 if (!game.cadence)
74 // Provide a random cadence, just to be sure nothing breaks:
75 game.cadence = "1d";
76 game.chats = []; //not stored in PGN :)
77 // Skip "human moves" section:
78 while (lines[++idx].length > 0) {}
79 // Read moves
80 game.moves = [];
81 await import("@/variants/" + game.vname + ".js")
82 .then((vModule) => {
83 window.V = vModule[game.vname + "Rules"];
84 while (++idx < lines.length && lines[idx].length > 0) {
85 const spaceIdx = lines[idx].indexOf(' ');
86 const skipMoveNum = lines[idx].substr(spaceIdx + 1);
87 const lineParts = skipMoveNum.split(",");
88 let move = [];
89 lineParts.forEach(lpart => {
90 const smParts = lpart.split(' ');
91 const startEnd = smParts[0].split('.');
92 let sm = {};
93 sm.start =
94 startEnd[0] != "-"
95 ? V.SquareToCoords(startEnd[0])
96 : { x: -1, y: -1 };
97 sm.end =
98 startEnd[1] != "-"
99 ? V.SquareToCoords(startEnd[1])
100 : { x: -1, y: -1 };
101 const appearVanish = smParts[1].split('/').map(av => {
102 if (av == "-") return [];
103 return av.split('.').map(psq => {
104 const xy = V.SquareToCoords(psq.substr(2));
105 return {
106 x: xy.x,
107 y: xy.y,
108 c: psq[0],
109 p: psq[1]
110 };
111 });
112 });
113 sm.appear = appearVanish[0];
114 sm.vanish = appearVanish[1];
115 move.push(sm);
116 });
117 if (move.length == 1) move = move[0];
118 game.moves.push(move);
119 }
120 this.$emit("game-uploaded", game);
121 });
122 }
123 }
124 };
125 </script>
126
127 <style lang="sass" scoped>
128 input#upload
129 display: none
130
131 button#uploadBtn
132 display: block
133 margin: 0 auto
134
135 img.inline
136 height: 22px
137 @media screen and (max-width: 767px)
138 height: 18px
139 </style>