Commit | Line | Data |
---|---|---|
5f918a27 BA |
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> | |
1ef65040 | 12 | import { getRandString } from "@/utils/alea"; |
5f918a27 BA |
13 | export default { |
14 | name: "my-upload-game", | |
15 | methods: { | |
16 | uploadTrigger: function() { | |
801e2870 BA |
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 | }, | |
1ef65040 BA |
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 "Url": | |
65 | // Prefix "I_" to say "this is an import" | |
66 | game.id = "i" + value.match(/\/game\/([a-zA-Z0-9]+)$/)[1]; | |
67 | break; | |
68 | case "Cadence": | |
69 | game.cadence = value; | |
70 | break; | |
71 | } | |
72 | idx++; | |
73 | } | |
74 | if (!game.id) { | |
75 | game.id = "i" + getRandString(); | |
76 | // Provide a random cadence, just to be sure nothing breaks: | |
77 | game.cadence = "1d"; | |
78 | } | |
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 lineParts = lines[idx].split(" "); | |
89 | const startEnd = lineParts[1].split('.'); | |
90 | let move = {}; | |
91 | if (startEnd[0] != "-") move.start = V.SquareToCoords(startEnd[0]); | |
92 | if (startEnd[1] != "-") move.end = V.SquareToCoords(startEnd[1]); | |
93 | const appearVanish = lineParts[2].split('/').map(lpart => { | |
94 | if (lpart == "-") return []; | |
95 | return lpart.split('.').map(psq => { | |
96 | const xy = V.SquareToCoords(psq.substr(2)); | |
97 | return { | |
98 | x: xy.x, | |
99 | y: xy.y, | |
100 | c: psq[0], | |
101 | p: psq[1] | |
102 | }; | |
103 | }); | |
104 | }); | |
105 | move.appear = appearVanish[0]; | |
106 | move.vanish = appearVanish[1]; | |
107 | game.moves.push(move); | |
108 | } | |
109 | this.$emit("game-uploaded", game); | |
110 | }); | |
5f918a27 BA |
111 | } |
112 | } | |
113 | }; | |
114 | </script> | |
115 | ||
116 | <style lang="sass" scoped> | |
117 | input#upload | |
118 | display: none | |
119 | ||
73f8753f BA |
120 | button#uploadBtn |
121 | display: block | |
122 | margin: 0 auto | |
123 | ||
5f918a27 BA |
124 | img.inline |
125 | height: 22px | |
126 | @media screen and (max-width: 767px) | |
127 | height: 18px | |
128 | </style> |