Some (experimental) fixes
[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.score = (value != "*" ? value : "?");
63 break;
64 case "Cadence":
65 game.cadence = value;
66 break;
67 case "Options":
68 game.options = value;
69 break;
70 }
71 idx++;
72 }
73 // Always generate random ID for imported games, because they could be
74 // downloaded at different states (prefix 'i' for 'Import').
75 game.id = 'i' + getRandString()
76 if (!game.cadence)
77 // Provide a random cadence, just to be sure nothing breaks:
78 game.cadence = "1d";
79 game.chats = []; //not stored in PGN :)
80 // Skip "human moves" section:
81 while (lines[++idx].length > 0) {}
82 // Read moves
83 game.moves = [];
84 await import("@/variants/" + game.vname + ".js")
85 .then((vModule) => {
86 window.V = vModule[game.vname + "Rules"];
87 while (++idx < lines.length && lines[idx].length > 0) {
88 const spaceIdx = lines[idx].indexOf(' ');
89 const skipMoveNum = lines[idx].substr(spaceIdx + 1);
90 const lineParts = skipMoveNum.split(",");
91 let move = [];
92 lineParts.forEach(lpart => {
93 const smParts = lpart.split(' ');
94 const startEnd = smParts[0].split('.');
95 let sm = {};
96 sm.start =
97 startEnd[0] != "-"
98 ? V.SquareToCoords(startEnd[0])
99 : { x: -1, y: -1 };
100 sm.end =
101 startEnd[1] != "-"
102 ? V.SquareToCoords(startEnd[1])
103 : { x: -1, y: -1 };
104 const appearVanish = smParts[1].split('/').map(av => {
105 if (av == "-") return [];
106 return av.split('.').map(psq => {
107 const xy = V.SquareToCoords(psq.substr(2));
108 return {
109 x: xy.x,
110 y: xy.y,
111 c: psq[0],
112 p: psq[1]
113 };
114 });
115 });
116 sm.appear = appearVanish[0];
117 sm.vanish = appearVanish[1];
118 move.push(sm);
119 });
120 if (move.length == 1) move = move[0];
121 game.moves.push(move);
122 }
123 this.$emit("game-uploaded", game);
124 });
125 }
126 }
127 };
128 </script>
129
130 <style lang="sass" scoped>
131 input#upload
132 display: none
133
134 button#uploadBtn
135 display: block
136 margin: 0 auto
137
138 img.inline
139 height: 22px
140 @media screen and (max-width: 767px)
141 height: 18px
142 </style>