├── .dockerignore
├── .gitattributes
├── .gitignore
├── Dockerfile
├── README.md
├── VARS.js
├── package-lock.json
├── package.json
├── server.js
├── tampermonkey script
├── content
│ ├── UserGui.js
│ ├── chessboard.css
│ ├── chessboard.js
│ ├── chesspieces
│ │ ├── bB.svg
│ │ ├── bK.svg
│ │ ├── bN.svg
│ │ ├── bP.svg
│ │ ├── bQ.svg
│ │ ├── bR.svg
│ │ ├── wB.svg
│ │ ├── wK.svg
│ │ ├── wN.svg
│ │ ├── wP.svg
│ │ ├── wQ.svg
│ │ └── wR.svg
│ ├── jquery.js
│ ├── lozza.js
│ ├── stockfish-2018.js
│ ├── stockfish-5.js
│ └── stockfish-multi-thread
│ │ ├── stockfish.js
│ │ ├── stockfish.wasm
│ │ └── stockfish.worker.js
└── smart-chess.js
└── utils
├── engine.js
├── engine
└── engine_path
└── process.js
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | tampermonkey script
3 | utils/engine/*
4 | */stockfish*
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.exe
3 | *.bin
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 | WORKDIR /cas
3 | COPY package*.json ./
4 | RUN npm install
5 | COPY . .
6 | EXPOSE 5000
7 | CMD [ "npm start" ]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Smart Chess Bot: The Ultimate Chess Analysis System
2 |
3 | ## Overview
4 |
5 | Smart Chess Bot is an advanced chess analysis system designed to elevate your gameplay to new heights. Leveraging state-of-the-art algorithms and cutting-edge technology, our system provides unparalleled insights into chess positions, empowering players to make informed and strategic moves.
6 |
7 | ## Features
8 |
9 | - **Intelligent Analysis**: Our system employs sophisticated algorithms to analyze chess positions and recommend optimal moves.
10 | - **Multiple Chess Engines**: Choose from a selection of JavaScript UCI chess engines, including Lozza Engine, Stockfish 5 Engine, and Stockfish 2018 Engine, each offering varying speeds and maximum Elo ratings.
11 | - **Browser Compatibility**: Compatible with popular browsers such as Chrome, Firefox, Edge, and Opera, ensuring accessibility for players across platforms.
12 | - **User-friendly Interface**: Enjoy a seamless and intuitive user experience, making it easy to integrate our system into your chess strategy.
13 |
14 | ## Getting Started
15 |
16 | To start using Smart Chess Bot, follow these simple steps:
17 |
18 | 1. **Installation**: Install the TamperMonkey extension for your browser from [here](https://www.tampermonkey.net/).
19 | 2. **TamperMonkey Script**: Add the TamperMonkey script available [here](https://greasyfork.org/en/scripts/460147-c-a-s-chess-assistance-system) to enable Smart Chess Bot functionality.
20 | 3. **Allow Pop-ups**: Ensure that pop-ups are allowed in your browser settings to enable the full functionality of the system.
21 | 4. **Optional: Node Server Setup**: To run the chess engine locally on your system, follow these steps:
22 |
23 | - **Install Node Modules**: Run `npm install` in your terminal to install the necessary dependencies.
24 |
25 | - **Download Stockfish Engine**: [Download](https://stockfishchess.org/download/)
26 |
27 | - **Stockfish Engine Path**: Place the Stockfish engine in the following path: `utils/engine`.
28 |
29 | - **TamperMonkey Script**: Set engine name in the script settings.
30 |
31 | - **Run Server Command**: npm start server.js
32 |
33 |
34 | ## Screenshots
35 |
36 | 
37 | 
38 | 
39 |
--------------------------------------------------------------------------------
/VARS.js:
--------------------------------------------------------------------------------
1 | const VARS={
2 | PORT : 5000,
3 | LICHESS_API : "https://lichess.org/api/cloud-eval",
4 | DEPTH_MODE:0,
5 | MOVETIME_MODE:1
6 | }
7 |
8 |
9 | module.exports={VARS}
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "server",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "cors": "^2.8.5",
13 | "express": "^4.17.3",
14 | "nodemon": "^3.1.9",
15 | "request": "^2.88.2"
16 | }
17 | },
18 | "node_modules/accepts": {
19 | "version": "1.3.8",
20 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
21 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
22 | "dependencies": {
23 | "mime-types": "~2.1.34",
24 | "negotiator": "0.6.3"
25 | },
26 | "engines": {
27 | "node": ">= 0.6"
28 | }
29 | },
30 | "node_modules/ajv": {
31 | "version": "6.12.6",
32 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
33 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
34 | "dependencies": {
35 | "fast-deep-equal": "^3.1.1",
36 | "fast-json-stable-stringify": "^2.0.0",
37 | "json-schema-traverse": "^0.4.1",
38 | "uri-js": "^4.2.2"
39 | },
40 | "funding": {
41 | "type": "github",
42 | "url": "https://github.com/sponsors/epoberezkin"
43 | }
44 | },
45 | "node_modules/anymatch": {
46 | "version": "3.1.3",
47 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
48 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
49 | "license": "ISC",
50 | "dependencies": {
51 | "normalize-path": "^3.0.0",
52 | "picomatch": "^2.0.4"
53 | },
54 | "engines": {
55 | "node": ">= 8"
56 | }
57 | },
58 | "node_modules/array-flatten": {
59 | "version": "1.1.1",
60 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
61 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
62 | },
63 | "node_modules/asn1": {
64 | "version": "0.2.6",
65 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
66 | "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
67 | "dependencies": {
68 | "safer-buffer": "~2.1.0"
69 | }
70 | },
71 | "node_modules/assert-plus": {
72 | "version": "1.0.0",
73 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
74 | "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
75 | "engines": {
76 | "node": ">=0.8"
77 | }
78 | },
79 | "node_modules/asynckit": {
80 | "version": "0.4.0",
81 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
82 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
83 | },
84 | "node_modules/aws-sign2": {
85 | "version": "0.7.0",
86 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
87 | "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
88 | "engines": {
89 | "node": "*"
90 | }
91 | },
92 | "node_modules/aws4": {
93 | "version": "1.12.0",
94 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz",
95 | "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
96 | },
97 | "node_modules/balanced-match": {
98 | "version": "1.0.2",
99 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
100 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
101 | "license": "MIT"
102 | },
103 | "node_modules/bcrypt-pbkdf": {
104 | "version": "1.0.2",
105 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
106 | "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
107 | "dependencies": {
108 | "tweetnacl": "^0.14.3"
109 | }
110 | },
111 | "node_modules/binary-extensions": {
112 | "version": "2.3.0",
113 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
114 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
115 | "license": "MIT",
116 | "engines": {
117 | "node": ">=8"
118 | },
119 | "funding": {
120 | "url": "https://github.com/sponsors/sindresorhus"
121 | }
122 | },
123 | "node_modules/body-parser": {
124 | "version": "1.20.1",
125 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
126 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
127 | "dependencies": {
128 | "bytes": "3.1.2",
129 | "content-type": "~1.0.4",
130 | "debug": "2.6.9",
131 | "depd": "2.0.0",
132 | "destroy": "1.2.0",
133 | "http-errors": "2.0.0",
134 | "iconv-lite": "0.4.24",
135 | "on-finished": "2.4.1",
136 | "qs": "6.11.0",
137 | "raw-body": "2.5.1",
138 | "type-is": "~1.6.18",
139 | "unpipe": "1.0.0"
140 | },
141 | "engines": {
142 | "node": ">= 0.8",
143 | "npm": "1.2.8000 || >= 1.4.16"
144 | }
145 | },
146 | "node_modules/brace-expansion": {
147 | "version": "1.1.11",
148 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
149 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
150 | "license": "MIT",
151 | "dependencies": {
152 | "balanced-match": "^1.0.0",
153 | "concat-map": "0.0.1"
154 | }
155 | },
156 | "node_modules/braces": {
157 | "version": "3.0.3",
158 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
159 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
160 | "license": "MIT",
161 | "dependencies": {
162 | "fill-range": "^7.1.1"
163 | },
164 | "engines": {
165 | "node": ">=8"
166 | }
167 | },
168 | "node_modules/bytes": {
169 | "version": "3.1.2",
170 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
171 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
172 | "engines": {
173 | "node": ">= 0.8"
174 | }
175 | },
176 | "node_modules/call-bind": {
177 | "version": "1.0.2",
178 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
179 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
180 | "dependencies": {
181 | "function-bind": "^1.1.1",
182 | "get-intrinsic": "^1.0.2"
183 | },
184 | "funding": {
185 | "url": "https://github.com/sponsors/ljharb"
186 | }
187 | },
188 | "node_modules/caseless": {
189 | "version": "0.12.0",
190 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
191 | "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
192 | },
193 | "node_modules/chokidar": {
194 | "version": "3.6.0",
195 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
196 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
197 | "license": "MIT",
198 | "dependencies": {
199 | "anymatch": "~3.1.2",
200 | "braces": "~3.0.2",
201 | "glob-parent": "~5.1.2",
202 | "is-binary-path": "~2.1.0",
203 | "is-glob": "~4.0.1",
204 | "normalize-path": "~3.0.0",
205 | "readdirp": "~3.6.0"
206 | },
207 | "engines": {
208 | "node": ">= 8.10.0"
209 | },
210 | "funding": {
211 | "url": "https://paulmillr.com/funding/"
212 | },
213 | "optionalDependencies": {
214 | "fsevents": "~2.3.2"
215 | }
216 | },
217 | "node_modules/combined-stream": {
218 | "version": "1.0.8",
219 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
220 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
221 | "dependencies": {
222 | "delayed-stream": "~1.0.0"
223 | },
224 | "engines": {
225 | "node": ">= 0.8"
226 | }
227 | },
228 | "node_modules/concat-map": {
229 | "version": "0.0.1",
230 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
231 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
232 | "license": "MIT"
233 | },
234 | "node_modules/content-disposition": {
235 | "version": "0.5.4",
236 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
237 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
238 | "dependencies": {
239 | "safe-buffer": "5.2.1"
240 | },
241 | "engines": {
242 | "node": ">= 0.6"
243 | }
244 | },
245 | "node_modules/content-type": {
246 | "version": "1.0.5",
247 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
248 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
249 | "engines": {
250 | "node": ">= 0.6"
251 | }
252 | },
253 | "node_modules/cookie-signature": {
254 | "version": "1.0.6",
255 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
256 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
257 | },
258 | "node_modules/cors": {
259 | "version": "2.8.5",
260 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
261 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
262 | "license": "MIT",
263 | "dependencies": {
264 | "object-assign": "^4",
265 | "vary": "^1"
266 | },
267 | "engines": {
268 | "node": ">= 0.10"
269 | }
270 | },
271 | "node_modules/dashdash": {
272 | "version": "1.14.1",
273 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
274 | "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
275 | "dependencies": {
276 | "assert-plus": "^1.0.0"
277 | },
278 | "engines": {
279 | "node": ">=0.10"
280 | }
281 | },
282 | "node_modules/debug": {
283 | "version": "2.6.9",
284 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
285 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
286 | "dependencies": {
287 | "ms": "2.0.0"
288 | }
289 | },
290 | "node_modules/delayed-stream": {
291 | "version": "1.0.0",
292 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
293 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
294 | "engines": {
295 | "node": ">=0.4.0"
296 | }
297 | },
298 | "node_modules/depd": {
299 | "version": "2.0.0",
300 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
301 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
302 | "engines": {
303 | "node": ">= 0.8"
304 | }
305 | },
306 | "node_modules/destroy": {
307 | "version": "1.2.0",
308 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
309 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
310 | "engines": {
311 | "node": ">= 0.8",
312 | "npm": "1.2.8000 || >= 1.4.16"
313 | }
314 | },
315 | "node_modules/ecc-jsbn": {
316 | "version": "0.1.2",
317 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
318 | "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
319 | "dependencies": {
320 | "jsbn": "~0.1.0",
321 | "safer-buffer": "^2.1.0"
322 | }
323 | },
324 | "node_modules/ee-first": {
325 | "version": "1.1.1",
326 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
327 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
328 | },
329 | "node_modules/encodeurl": {
330 | "version": "1.0.2",
331 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
332 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
333 | "engines": {
334 | "node": ">= 0.8"
335 | }
336 | },
337 | "node_modules/escape-html": {
338 | "version": "1.0.3",
339 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
340 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
341 | },
342 | "node_modules/etag": {
343 | "version": "1.8.1",
344 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
345 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
346 | "engines": {
347 | "node": ">= 0.6"
348 | }
349 | },
350 | "node_modules/express": {
351 | "version": "4.18.2",
352 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
353 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
354 | "dependencies": {
355 | "accepts": "~1.3.8",
356 | "array-flatten": "1.1.1",
357 | "body-parser": "1.20.1",
358 | "content-disposition": "0.5.4",
359 | "content-type": "~1.0.4",
360 | "cookie": "0.5.0",
361 | "cookie-signature": "1.0.6",
362 | "debug": "2.6.9",
363 | "depd": "2.0.0",
364 | "encodeurl": "~1.0.2",
365 | "escape-html": "~1.0.3",
366 | "etag": "~1.8.1",
367 | "finalhandler": "1.2.0",
368 | "fresh": "0.5.2",
369 | "http-errors": "2.0.0",
370 | "merge-descriptors": "1.0.1",
371 | "methods": "~1.1.2",
372 | "on-finished": "2.4.1",
373 | "parseurl": "~1.3.3",
374 | "path-to-regexp": "0.1.7",
375 | "proxy-addr": "~2.0.7",
376 | "qs": "6.11.0",
377 | "range-parser": "~1.2.1",
378 | "safe-buffer": "5.2.1",
379 | "send": "0.18.0",
380 | "serve-static": "1.15.0",
381 | "setprototypeof": "1.2.0",
382 | "statuses": "2.0.1",
383 | "type-is": "~1.6.18",
384 | "utils-merge": "1.0.1",
385 | "vary": "~1.1.2"
386 | },
387 | "engines": {
388 | "node": ">= 0.10.0"
389 | }
390 | },
391 | "node_modules/express/node_modules/cookie": {
392 | "version": "0.5.0",
393 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
394 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
395 | "engines": {
396 | "node": ">= 0.6"
397 | }
398 | },
399 | "node_modules/extend": {
400 | "version": "3.0.2",
401 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
402 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
403 | },
404 | "node_modules/extsprintf": {
405 | "version": "1.3.0",
406 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
407 | "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
408 | "engines": [
409 | "node >=0.6.0"
410 | ]
411 | },
412 | "node_modules/fast-deep-equal": {
413 | "version": "3.1.3",
414 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
415 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
416 | },
417 | "node_modules/fast-json-stable-stringify": {
418 | "version": "2.1.0",
419 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
420 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
421 | },
422 | "node_modules/fill-range": {
423 | "version": "7.1.1",
424 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
425 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
426 | "license": "MIT",
427 | "dependencies": {
428 | "to-regex-range": "^5.0.1"
429 | },
430 | "engines": {
431 | "node": ">=8"
432 | }
433 | },
434 | "node_modules/finalhandler": {
435 | "version": "1.2.0",
436 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
437 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
438 | "dependencies": {
439 | "debug": "2.6.9",
440 | "encodeurl": "~1.0.2",
441 | "escape-html": "~1.0.3",
442 | "on-finished": "2.4.1",
443 | "parseurl": "~1.3.3",
444 | "statuses": "2.0.1",
445 | "unpipe": "~1.0.0"
446 | },
447 | "engines": {
448 | "node": ">= 0.8"
449 | }
450 | },
451 | "node_modules/forever-agent": {
452 | "version": "0.6.1",
453 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
454 | "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
455 | "engines": {
456 | "node": "*"
457 | }
458 | },
459 | "node_modules/forwarded": {
460 | "version": "0.2.0",
461 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
462 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
463 | "engines": {
464 | "node": ">= 0.6"
465 | }
466 | },
467 | "node_modules/fresh": {
468 | "version": "0.5.2",
469 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
470 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
471 | "engines": {
472 | "node": ">= 0.6"
473 | }
474 | },
475 | "node_modules/fsevents": {
476 | "version": "2.3.3",
477 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
478 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
479 | "hasInstallScript": true,
480 | "license": "MIT",
481 | "optional": true,
482 | "os": [
483 | "darwin"
484 | ],
485 | "engines": {
486 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
487 | }
488 | },
489 | "node_modules/function-bind": {
490 | "version": "1.1.1",
491 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
492 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
493 | },
494 | "node_modules/get-intrinsic": {
495 | "version": "1.2.0",
496 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
497 | "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
498 | "dependencies": {
499 | "function-bind": "^1.1.1",
500 | "has": "^1.0.3",
501 | "has-symbols": "^1.0.3"
502 | },
503 | "funding": {
504 | "url": "https://github.com/sponsors/ljharb"
505 | }
506 | },
507 | "node_modules/getpass": {
508 | "version": "0.1.7",
509 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
510 | "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
511 | "dependencies": {
512 | "assert-plus": "^1.0.0"
513 | }
514 | },
515 | "node_modules/glob-parent": {
516 | "version": "5.1.2",
517 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
518 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
519 | "license": "ISC",
520 | "dependencies": {
521 | "is-glob": "^4.0.1"
522 | },
523 | "engines": {
524 | "node": ">= 6"
525 | }
526 | },
527 | "node_modules/har-schema": {
528 | "version": "2.0.0",
529 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
530 | "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
531 | "engines": {
532 | "node": ">=4"
533 | }
534 | },
535 | "node_modules/har-validator": {
536 | "version": "5.1.5",
537 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
538 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
539 | "deprecated": "this library is no longer supported",
540 | "dependencies": {
541 | "ajv": "^6.12.3",
542 | "har-schema": "^2.0.0"
543 | },
544 | "engines": {
545 | "node": ">=6"
546 | }
547 | },
548 | "node_modules/has": {
549 | "version": "1.0.3",
550 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
551 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
552 | "dependencies": {
553 | "function-bind": "^1.1.1"
554 | },
555 | "engines": {
556 | "node": ">= 0.4.0"
557 | }
558 | },
559 | "node_modules/has-flag": {
560 | "version": "3.0.0",
561 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
562 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
563 | "license": "MIT",
564 | "engines": {
565 | "node": ">=4"
566 | }
567 | },
568 | "node_modules/has-symbols": {
569 | "version": "1.0.3",
570 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
571 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
572 | "engines": {
573 | "node": ">= 0.4"
574 | },
575 | "funding": {
576 | "url": "https://github.com/sponsors/ljharb"
577 | }
578 | },
579 | "node_modules/http-errors": {
580 | "version": "2.0.0",
581 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
582 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
583 | "dependencies": {
584 | "depd": "2.0.0",
585 | "inherits": "2.0.4",
586 | "setprototypeof": "1.2.0",
587 | "statuses": "2.0.1",
588 | "toidentifier": "1.0.1"
589 | },
590 | "engines": {
591 | "node": ">= 0.8"
592 | }
593 | },
594 | "node_modules/http-signature": {
595 | "version": "1.2.0",
596 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
597 | "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
598 | "dependencies": {
599 | "assert-plus": "^1.0.0",
600 | "jsprim": "^1.2.2",
601 | "sshpk": "^1.7.0"
602 | },
603 | "engines": {
604 | "node": ">=0.8",
605 | "npm": ">=1.3.7"
606 | }
607 | },
608 | "node_modules/iconv-lite": {
609 | "version": "0.4.24",
610 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
611 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
612 | "dependencies": {
613 | "safer-buffer": ">= 2.1.2 < 3"
614 | },
615 | "engines": {
616 | "node": ">=0.10.0"
617 | }
618 | },
619 | "node_modules/ignore-by-default": {
620 | "version": "1.0.1",
621 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
622 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
623 | "license": "ISC"
624 | },
625 | "node_modules/inherits": {
626 | "version": "2.0.4",
627 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
628 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
629 | },
630 | "node_modules/ipaddr.js": {
631 | "version": "1.9.1",
632 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
633 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
634 | "engines": {
635 | "node": ">= 0.10"
636 | }
637 | },
638 | "node_modules/is-binary-path": {
639 | "version": "2.1.0",
640 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
641 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
642 | "license": "MIT",
643 | "dependencies": {
644 | "binary-extensions": "^2.0.0"
645 | },
646 | "engines": {
647 | "node": ">=8"
648 | }
649 | },
650 | "node_modules/is-extglob": {
651 | "version": "2.1.1",
652 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
653 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
654 | "license": "MIT",
655 | "engines": {
656 | "node": ">=0.10.0"
657 | }
658 | },
659 | "node_modules/is-glob": {
660 | "version": "4.0.3",
661 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
662 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
663 | "license": "MIT",
664 | "dependencies": {
665 | "is-extglob": "^2.1.1"
666 | },
667 | "engines": {
668 | "node": ">=0.10.0"
669 | }
670 | },
671 | "node_modules/is-number": {
672 | "version": "7.0.0",
673 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
674 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
675 | "license": "MIT",
676 | "engines": {
677 | "node": ">=0.12.0"
678 | }
679 | },
680 | "node_modules/is-typedarray": {
681 | "version": "1.0.0",
682 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
683 | "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
684 | },
685 | "node_modules/isstream": {
686 | "version": "0.1.2",
687 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
688 | "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
689 | },
690 | "node_modules/jsbn": {
691 | "version": "0.1.1",
692 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
693 | "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
694 | },
695 | "node_modules/json-schema": {
696 | "version": "0.4.0",
697 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
698 | "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
699 | },
700 | "node_modules/json-schema-traverse": {
701 | "version": "0.4.1",
702 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
703 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
704 | },
705 | "node_modules/json-stringify-safe": {
706 | "version": "5.0.1",
707 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
708 | "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
709 | },
710 | "node_modules/jsprim": {
711 | "version": "1.4.2",
712 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
713 | "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
714 | "dependencies": {
715 | "assert-plus": "1.0.0",
716 | "extsprintf": "1.3.0",
717 | "json-schema": "0.4.0",
718 | "verror": "1.10.0"
719 | },
720 | "engines": {
721 | "node": ">=0.6.0"
722 | }
723 | },
724 | "node_modules/media-typer": {
725 | "version": "0.3.0",
726 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
727 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
728 | "engines": {
729 | "node": ">= 0.6"
730 | }
731 | },
732 | "node_modules/merge-descriptors": {
733 | "version": "1.0.1",
734 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
735 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
736 | },
737 | "node_modules/methods": {
738 | "version": "1.1.2",
739 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
740 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
741 | "engines": {
742 | "node": ">= 0.6"
743 | }
744 | },
745 | "node_modules/mime": {
746 | "version": "1.6.0",
747 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
748 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
749 | "bin": {
750 | "mime": "cli.js"
751 | },
752 | "engines": {
753 | "node": ">=4"
754 | }
755 | },
756 | "node_modules/mime-db": {
757 | "version": "1.52.0",
758 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
759 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
760 | "engines": {
761 | "node": ">= 0.6"
762 | }
763 | },
764 | "node_modules/mime-types": {
765 | "version": "2.1.35",
766 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
767 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
768 | "dependencies": {
769 | "mime-db": "1.52.0"
770 | },
771 | "engines": {
772 | "node": ">= 0.6"
773 | }
774 | },
775 | "node_modules/minimatch": {
776 | "version": "3.1.2",
777 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
778 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
779 | "license": "ISC",
780 | "dependencies": {
781 | "brace-expansion": "^1.1.7"
782 | },
783 | "engines": {
784 | "node": "*"
785 | }
786 | },
787 | "node_modules/ms": {
788 | "version": "2.0.0",
789 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
790 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
791 | },
792 | "node_modules/negotiator": {
793 | "version": "0.6.3",
794 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
795 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
796 | "engines": {
797 | "node": ">= 0.6"
798 | }
799 | },
800 | "node_modules/nodemon": {
801 | "version": "3.1.9",
802 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
803 | "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
804 | "license": "MIT",
805 | "dependencies": {
806 | "chokidar": "^3.5.2",
807 | "debug": "^4",
808 | "ignore-by-default": "^1.0.1",
809 | "minimatch": "^3.1.2",
810 | "pstree.remy": "^1.1.8",
811 | "semver": "^7.5.3",
812 | "simple-update-notifier": "^2.0.0",
813 | "supports-color": "^5.5.0",
814 | "touch": "^3.1.0",
815 | "undefsafe": "^2.0.5"
816 | },
817 | "bin": {
818 | "nodemon": "bin/nodemon.js"
819 | },
820 | "engines": {
821 | "node": ">=10"
822 | },
823 | "funding": {
824 | "type": "opencollective",
825 | "url": "https://opencollective.com/nodemon"
826 | }
827 | },
828 | "node_modules/nodemon/node_modules/debug": {
829 | "version": "4.4.0",
830 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
831 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
832 | "license": "MIT",
833 | "dependencies": {
834 | "ms": "^2.1.3"
835 | },
836 | "engines": {
837 | "node": ">=6.0"
838 | },
839 | "peerDependenciesMeta": {
840 | "supports-color": {
841 | "optional": true
842 | }
843 | }
844 | },
845 | "node_modules/nodemon/node_modules/ms": {
846 | "version": "2.1.3",
847 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
848 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
849 | "license": "MIT"
850 | },
851 | "node_modules/normalize-path": {
852 | "version": "3.0.0",
853 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
854 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
855 | "license": "MIT",
856 | "engines": {
857 | "node": ">=0.10.0"
858 | }
859 | },
860 | "node_modules/oauth-sign": {
861 | "version": "0.9.0",
862 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
863 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
864 | "engines": {
865 | "node": "*"
866 | }
867 | },
868 | "node_modules/object-assign": {
869 | "version": "4.1.1",
870 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
871 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
872 | "license": "MIT",
873 | "engines": {
874 | "node": ">=0.10.0"
875 | }
876 | },
877 | "node_modules/object-inspect": {
878 | "version": "1.12.3",
879 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
880 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
881 | "funding": {
882 | "url": "https://github.com/sponsors/ljharb"
883 | }
884 | },
885 | "node_modules/on-finished": {
886 | "version": "2.4.1",
887 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
888 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
889 | "dependencies": {
890 | "ee-first": "1.1.1"
891 | },
892 | "engines": {
893 | "node": ">= 0.8"
894 | }
895 | },
896 | "node_modules/parseurl": {
897 | "version": "1.3.3",
898 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
899 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
900 | "engines": {
901 | "node": ">= 0.8"
902 | }
903 | },
904 | "node_modules/path-to-regexp": {
905 | "version": "0.1.7",
906 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
907 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
908 | },
909 | "node_modules/performance-now": {
910 | "version": "2.1.0",
911 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
912 | "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
913 | },
914 | "node_modules/picomatch": {
915 | "version": "2.3.1",
916 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
917 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
918 | "license": "MIT",
919 | "engines": {
920 | "node": ">=8.6"
921 | },
922 | "funding": {
923 | "url": "https://github.com/sponsors/jonschlinkert"
924 | }
925 | },
926 | "node_modules/proxy-addr": {
927 | "version": "2.0.7",
928 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
929 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
930 | "dependencies": {
931 | "forwarded": "0.2.0",
932 | "ipaddr.js": "1.9.1"
933 | },
934 | "engines": {
935 | "node": ">= 0.10"
936 | }
937 | },
938 | "node_modules/psl": {
939 | "version": "1.9.0",
940 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
941 | "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
942 | },
943 | "node_modules/pstree.remy": {
944 | "version": "1.1.8",
945 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
946 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
947 | "license": "MIT"
948 | },
949 | "node_modules/punycode": {
950 | "version": "2.3.0",
951 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
952 | "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
953 | "engines": {
954 | "node": ">=6"
955 | }
956 | },
957 | "node_modules/qs": {
958 | "version": "6.11.0",
959 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
960 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
961 | "dependencies": {
962 | "side-channel": "^1.0.4"
963 | },
964 | "engines": {
965 | "node": ">=0.6"
966 | },
967 | "funding": {
968 | "url": "https://github.com/sponsors/ljharb"
969 | }
970 | },
971 | "node_modules/range-parser": {
972 | "version": "1.2.1",
973 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
974 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
975 | "engines": {
976 | "node": ">= 0.6"
977 | }
978 | },
979 | "node_modules/raw-body": {
980 | "version": "2.5.1",
981 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
982 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
983 | "dependencies": {
984 | "bytes": "3.1.2",
985 | "http-errors": "2.0.0",
986 | "iconv-lite": "0.4.24",
987 | "unpipe": "1.0.0"
988 | },
989 | "engines": {
990 | "node": ">= 0.8"
991 | }
992 | },
993 | "node_modules/readdirp": {
994 | "version": "3.6.0",
995 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
996 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
997 | "license": "MIT",
998 | "dependencies": {
999 | "picomatch": "^2.2.1"
1000 | },
1001 | "engines": {
1002 | "node": ">=8.10.0"
1003 | }
1004 | },
1005 | "node_modules/request": {
1006 | "version": "2.88.2",
1007 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
1008 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
1009 | "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
1010 | "dependencies": {
1011 | "aws-sign2": "~0.7.0",
1012 | "aws4": "^1.8.0",
1013 | "caseless": "~0.12.0",
1014 | "combined-stream": "~1.0.6",
1015 | "extend": "~3.0.2",
1016 | "forever-agent": "~0.6.1",
1017 | "form-data": "~2.3.2",
1018 | "har-validator": "~5.1.3",
1019 | "http-signature": "~1.2.0",
1020 | "is-typedarray": "~1.0.0",
1021 | "isstream": "~0.1.2",
1022 | "json-stringify-safe": "~5.0.1",
1023 | "mime-types": "~2.1.19",
1024 | "oauth-sign": "~0.9.0",
1025 | "performance-now": "^2.1.0",
1026 | "qs": "~6.5.2",
1027 | "safe-buffer": "^5.1.2",
1028 | "tough-cookie": "~2.5.0",
1029 | "tunnel-agent": "^0.6.0",
1030 | "uuid": "^3.3.2"
1031 | },
1032 | "engines": {
1033 | "node": ">= 6"
1034 | }
1035 | },
1036 | "node_modules/request/node_modules/form-data": {
1037 | "version": "2.3.3",
1038 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
1039 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
1040 | "dependencies": {
1041 | "asynckit": "^0.4.0",
1042 | "combined-stream": "^1.0.6",
1043 | "mime-types": "^2.1.12"
1044 | },
1045 | "engines": {
1046 | "node": ">= 0.12"
1047 | }
1048 | },
1049 | "node_modules/request/node_modules/qs": {
1050 | "version": "6.5.3",
1051 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
1052 | "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
1053 | "engines": {
1054 | "node": ">=0.6"
1055 | }
1056 | },
1057 | "node_modules/safe-buffer": {
1058 | "version": "5.2.1",
1059 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1060 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1061 | "funding": [
1062 | {
1063 | "type": "github",
1064 | "url": "https://github.com/sponsors/feross"
1065 | },
1066 | {
1067 | "type": "patreon",
1068 | "url": "https://www.patreon.com/feross"
1069 | },
1070 | {
1071 | "type": "consulting",
1072 | "url": "https://feross.org/support"
1073 | }
1074 | ]
1075 | },
1076 | "node_modules/safer-buffer": {
1077 | "version": "2.1.2",
1078 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1079 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1080 | },
1081 | "node_modules/semver": {
1082 | "version": "7.6.3",
1083 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
1084 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
1085 | "license": "ISC",
1086 | "bin": {
1087 | "semver": "bin/semver.js"
1088 | },
1089 | "engines": {
1090 | "node": ">=10"
1091 | }
1092 | },
1093 | "node_modules/send": {
1094 | "version": "0.18.0",
1095 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
1096 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
1097 | "dependencies": {
1098 | "debug": "2.6.9",
1099 | "depd": "2.0.0",
1100 | "destroy": "1.2.0",
1101 | "encodeurl": "~1.0.2",
1102 | "escape-html": "~1.0.3",
1103 | "etag": "~1.8.1",
1104 | "fresh": "0.5.2",
1105 | "http-errors": "2.0.0",
1106 | "mime": "1.6.0",
1107 | "ms": "2.1.3",
1108 | "on-finished": "2.4.1",
1109 | "range-parser": "~1.2.1",
1110 | "statuses": "2.0.1"
1111 | },
1112 | "engines": {
1113 | "node": ">= 0.8.0"
1114 | }
1115 | },
1116 | "node_modules/send/node_modules/ms": {
1117 | "version": "2.1.3",
1118 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1119 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1120 | },
1121 | "node_modules/serve-static": {
1122 | "version": "1.15.0",
1123 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
1124 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
1125 | "dependencies": {
1126 | "encodeurl": "~1.0.2",
1127 | "escape-html": "~1.0.3",
1128 | "parseurl": "~1.3.3",
1129 | "send": "0.18.0"
1130 | },
1131 | "engines": {
1132 | "node": ">= 0.8.0"
1133 | }
1134 | },
1135 | "node_modules/setprototypeof": {
1136 | "version": "1.2.0",
1137 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1138 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1139 | },
1140 | "node_modules/side-channel": {
1141 | "version": "1.0.4",
1142 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1143 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1144 | "dependencies": {
1145 | "call-bind": "^1.0.0",
1146 | "get-intrinsic": "^1.0.2",
1147 | "object-inspect": "^1.9.0"
1148 | },
1149 | "funding": {
1150 | "url": "https://github.com/sponsors/ljharb"
1151 | }
1152 | },
1153 | "node_modules/simple-update-notifier": {
1154 | "version": "2.0.0",
1155 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
1156 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
1157 | "license": "MIT",
1158 | "dependencies": {
1159 | "semver": "^7.5.3"
1160 | },
1161 | "engines": {
1162 | "node": ">=10"
1163 | }
1164 | },
1165 | "node_modules/sshpk": {
1166 | "version": "1.17.0",
1167 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
1168 | "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
1169 | "dependencies": {
1170 | "asn1": "~0.2.3",
1171 | "assert-plus": "^1.0.0",
1172 | "bcrypt-pbkdf": "^1.0.0",
1173 | "dashdash": "^1.12.0",
1174 | "ecc-jsbn": "~0.1.1",
1175 | "getpass": "^0.1.1",
1176 | "jsbn": "~0.1.0",
1177 | "safer-buffer": "^2.0.2",
1178 | "tweetnacl": "~0.14.0"
1179 | },
1180 | "bin": {
1181 | "sshpk-conv": "bin/sshpk-conv",
1182 | "sshpk-sign": "bin/sshpk-sign",
1183 | "sshpk-verify": "bin/sshpk-verify"
1184 | },
1185 | "engines": {
1186 | "node": ">=0.10.0"
1187 | }
1188 | },
1189 | "node_modules/statuses": {
1190 | "version": "2.0.1",
1191 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1192 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1193 | "engines": {
1194 | "node": ">= 0.8"
1195 | }
1196 | },
1197 | "node_modules/supports-color": {
1198 | "version": "5.5.0",
1199 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1200 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1201 | "license": "MIT",
1202 | "dependencies": {
1203 | "has-flag": "^3.0.0"
1204 | },
1205 | "engines": {
1206 | "node": ">=4"
1207 | }
1208 | },
1209 | "node_modules/to-regex-range": {
1210 | "version": "5.0.1",
1211 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1212 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1213 | "license": "MIT",
1214 | "dependencies": {
1215 | "is-number": "^7.0.0"
1216 | },
1217 | "engines": {
1218 | "node": ">=8.0"
1219 | }
1220 | },
1221 | "node_modules/toidentifier": {
1222 | "version": "1.0.1",
1223 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1224 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1225 | "engines": {
1226 | "node": ">=0.6"
1227 | }
1228 | },
1229 | "node_modules/touch": {
1230 | "version": "3.1.1",
1231 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
1232 | "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
1233 | "license": "ISC",
1234 | "bin": {
1235 | "nodetouch": "bin/nodetouch.js"
1236 | }
1237 | },
1238 | "node_modules/tough-cookie": {
1239 | "version": "2.5.0",
1240 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
1241 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
1242 | "dependencies": {
1243 | "psl": "^1.1.28",
1244 | "punycode": "^2.1.1"
1245 | },
1246 | "engines": {
1247 | "node": ">=0.8"
1248 | }
1249 | },
1250 | "node_modules/tunnel-agent": {
1251 | "version": "0.6.0",
1252 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
1253 | "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
1254 | "dependencies": {
1255 | "safe-buffer": "^5.0.1"
1256 | },
1257 | "engines": {
1258 | "node": "*"
1259 | }
1260 | },
1261 | "node_modules/tweetnacl": {
1262 | "version": "0.14.5",
1263 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
1264 | "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
1265 | },
1266 | "node_modules/type-is": {
1267 | "version": "1.6.18",
1268 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1269 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1270 | "dependencies": {
1271 | "media-typer": "0.3.0",
1272 | "mime-types": "~2.1.24"
1273 | },
1274 | "engines": {
1275 | "node": ">= 0.6"
1276 | }
1277 | },
1278 | "node_modules/undefsafe": {
1279 | "version": "2.0.5",
1280 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
1281 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1282 | "license": "MIT"
1283 | },
1284 | "node_modules/unpipe": {
1285 | "version": "1.0.0",
1286 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1287 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1288 | "engines": {
1289 | "node": ">= 0.8"
1290 | }
1291 | },
1292 | "node_modules/uri-js": {
1293 | "version": "4.4.1",
1294 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
1295 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
1296 | "dependencies": {
1297 | "punycode": "^2.1.0"
1298 | }
1299 | },
1300 | "node_modules/utils-merge": {
1301 | "version": "1.0.1",
1302 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1303 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1304 | "engines": {
1305 | "node": ">= 0.4.0"
1306 | }
1307 | },
1308 | "node_modules/uuid": {
1309 | "version": "3.4.0",
1310 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
1311 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
1312 | "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
1313 | "bin": {
1314 | "uuid": "bin/uuid"
1315 | }
1316 | },
1317 | "node_modules/vary": {
1318 | "version": "1.1.2",
1319 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1320 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1321 | "engines": {
1322 | "node": ">= 0.8"
1323 | }
1324 | },
1325 | "node_modules/verror": {
1326 | "version": "1.10.0",
1327 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
1328 | "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
1329 | "engines": [
1330 | "node >=0.6.0"
1331 | ],
1332 | "dependencies": {
1333 | "assert-plus": "^1.0.0",
1334 | "core-util-is": "1.0.2",
1335 | "extsprintf": "^1.2.0"
1336 | }
1337 | },
1338 | "node_modules/verror/node_modules/core-util-is": {
1339 | "version": "1.0.2",
1340 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
1341 | "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
1342 | }
1343 | }
1344 | }
1345 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "cors": "^2.8.5",
4 | "express": "^4.17.3",
5 | "nodemon": "^3.1.9",
6 | "request": "^2.88.2"
7 | },
8 | "name": "server",
9 | "version": "1.0.0",
10 | "main": "server.js",
11 | "scripts": {
12 | "test": "echo \"Error: no test specified\" && exit 1",
13 | "start": "nodemon server.js"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "ISC",
18 | "description": ""
19 | }
20 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const { ChessEngine } = require("./utils/engine")
4 | const { VARS } = require("./VARS")
5 | const cors = require("cors")
6 |
7 | try {
8 | app.listen(VARS.PORT, () => console.log(`Listening on port ${VARS.PORT}`))
9 | app.use(cors())
10 | } catch (error) {
11 | console.log("Server is already running !!")
12 | }
13 |
14 |
15 |
16 | // VARS
17 | const chessEngine = new ChessEngine()
18 | var counter = 0
19 |
20 |
21 |
22 |
23 |
24 | app.get("/getBestMove", (req, res) => {
25 | var fen = req.query.fen || "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
26 | var depth = req.query.depth || 10
27 | var movetime = req.query.movetime || 500
28 |
29 | var turn = req.query.turn || "w"
30 | //var engine_type = req.query.engine_type || VARS.ENGINE_TYPES[0]
31 | var engine_name = req.query.engine_name || "stockfish.exe"
32 | var engine_mode = req.query.engine_mode || 0
33 |
34 |
35 | if (depth > 20) {
36 | depth = 20
37 | }
38 |
39 | counter++
40 | console.log("\n#" + counter + " turn updated to: " + (turn === 'w' ? 'White' : 'Black'))
41 |
42 |
43 | chessEngine.start(counter, engine_mode, turn, depth, movetime, engine_name, fen).then((result) => {
44 |
45 | const parsedResult = {
46 | fen: result.fen,
47 | bestMove: result.bestMove,
48 | ponder: result.ponder,
49 | turn: result.turn,
50 | depth: depth,
51 | movetime: movetime,
52 | score: depth,
53 | provider: engine_name
54 | }
55 |
56 |
57 | console.log("#" + result.id + " best move: " + parsedResult.bestMove)
58 | return res.send({ success: true, data: parsedResult })
59 | }).catch((error) => {
60 | console.log("Error: " + error)
61 | return res.send({ success: false, data: error.message })
62 | })
63 | })
64 |
--------------------------------------------------------------------------------
/tampermonkey script/content/UserGui.js:
--------------------------------------------------------------------------------
1 | /*
2 | * usergui.js
3 | * v1.0.1
4 | * https://github.com/AugmentedWeb/UserGui
5 | * Apache 2.0 licensed
6 | */
7 |
8 | class UserGui {
9 | constructor() {
10 | const grantArr = GM_info?.script?.grant;
11 |
12 | if(typeof grantArr == "object") {
13 | if(!grantArr.includes("GM_xmlhttpRequest")) {
14 | prompt(`${this.#projectName} needs GM_xmlhttpRequest!\n\nPlease add this to your userscript's header...`, "// @grant GM_xmlhttpRequest");
15 | }
16 |
17 | if(!grantArr.includes("GM_getValue")) {
18 | prompt(`${this.#projectName} needs GM_getValue!\n\nPlease add this to your userscript's header...`, "// @grant GM_getValue");
19 | }
20 |
21 | if(!grantArr.includes("GM_setValue")) {
22 | prompt(`${this.#projectName} needs GM_setValue!\n\nPlease add this to your userscript's header...`, "// @grant GM_setValue");
23 | }
24 | }
25 | }
26 |
27 | #projectName = "UserGui";
28 | window = undefined;
29 | document = undefined;
30 | iFrame = undefined;
31 | settings = {
32 | "window" : {
33 | "title" : "Smart Chess Bot",
34 | "name" : "userscript-gui",
35 | "external" : false,
36 | "centered" : false,
37 | "size" : {
38 | "width" : 300,
39 | "height" : 500,
40 | "dynamicSize" : true
41 | }
42 | },
43 | "gui" : {
44 | "centeredItems" : false,
45 | "internal" : {
46 | "darkCloseButton" : false,
47 | "style" : `
48 | body {
49 | background-color: #ffffff;
50 | overflow: hidden;
51 | width: 100% !important;
52 | }
53 |
54 | form {
55 | padding: 10px;
56 | }
57 |
58 | #gui {
59 | height: fit-content;
60 | }
61 |
62 | .rendered-form {
63 | padding: 10px;
64 | }
65 |
66 | #header {
67 | padding: 10px;
68 | cursor: move;
69 | z-index: 10;
70 | background-color: #2196F3;
71 | color: #fff;
72 | height: fit-content;
73 | }
74 |
75 | .header-item-container {
76 | display: flex;
77 | justify-content: space-between;
78 | align-items: center;
79 | }
80 |
81 | .left-title {
82 | font-size: 14px;
83 | font-weight: bold;
84 | padding: 0;
85 | margin: 0;
86 | }
87 |
88 | #button-close-gui {
89 | vertical-align: middle;
90 |
91 | }
92 |
93 | div .form-group {
94 | margin-bottom: 15px;
95 | }
96 |
97 | #resizer {
98 | width: 10px;
99 | height: 10px;
100 | cursor: se-resize;
101 | position: absolute;
102 | bottom: 0;
103 | right: 0;
104 | }
105 |
106 | .formbuilder-button {
107 | width: fit-content;
108 | }
109 | `
110 | },
111 | "external" : {
112 | "popup" : true,
113 | "style" : `
114 | .rendered-form {
115 | padding: 10px;
116 | }
117 | div .form-group {
118 | margin-bottom: 15px;
119 | }
120 | `
121 | }
122 | },
123 | "messages" : {
124 | "blockedPopups" : () => alert(`The GUI (graphical user interface) failed to open!\n\nPossible reason: The popups are blocked.\n\nPlease allow popups for this site. (${window.location.hostname})`)
125 | }
126 | };
127 |
128 | // This error page will be shown if the user has not added any pages
129 | #errorPage = (title, code) => `
130 |
154 |
155 |
156 |
${title}
157 |
${code}
158 |
${this.#projectName} error message
159 |
160 |
`;
161 |
162 | // The user can add multiple pages to their GUI. The pages are stored in this array.
163 | #guiPages = [
164 | {
165 | "name" : "default_no_content_set",
166 | "content" : this.#errorPage("Content missing", "Gui.setContent(html, tabName);")
167 | }
168 | ];
169 |
170 | // The userscript manager's xmlHttpRequest is used to bypass CORS limitations (To load Bootstrap)
171 | async #bypassCors(externalFile) {
172 | const res = await new Promise(resolve => {
173 | GM_xmlhttpRequest({
174 | method: "GET",
175 | url: externalFile,
176 | onload: resolve
177 | });
178 | });
179 |
180 | return res.responseText;
181 | }
182 |
183 | // Returns one tab (as HTML) for the navigation tabs
184 | #createNavigationTab(page) {
185 | const name = page.name;
186 |
187 | if(name == undefined) {
188 | console.error(`[${this.#projectName}] Gui.addPage(html, name) <- name missing!`);
189 | return undefined;
190 | } else {
191 | const modifiedName = name.toLowerCase().replaceAll(' ', '').replace(/[^a-zA-Z0-9]/g, '') + Math.floor(Math.random() * 1000000000);
192 |
193 | const content = page.content;
194 | const indexOnArray = this.#guiPages.map(x => x.name).indexOf(name);
195 | const firstItem = indexOnArray == 0 ? true : false;
196 |
197 | return {
198 | "listItem" : `
199 |
200 |
201 |
202 | `,
203 | "panelItem" : `
204 | ${content}
205 | `
206 | };
207 | }
208 | }
209 |
210 | // Make tabs function without bootstrap.js (CSP might block bootstrap and make the GUI nonfunctional)
211 | #initializeTabs() {
212 | const handleTabClick = e => {
213 | const target = e.target;
214 | const contentID = target.getAttribute("data-bs-target");
215 |
216 | target.classList.add("active");
217 | this.document.querySelector(contentID).classList.add("active");
218 |
219 | [...this.document.querySelectorAll(".nav-link")].forEach(tab => {
220 | if(tab != target) {
221 | const contentID = tab.getAttribute("data-bs-target");
222 |
223 | tab.classList.remove("active");
224 | this.document.querySelector(contentID).classList.remove("active");
225 | }
226 | });
227 | }
228 |
229 | [...this.document.querySelectorAll(".nav-link")].forEach(tab => {
230 | tab.addEventListener("click", handleTabClick);
231 | });
232 | }
233 |
234 | // Will determine if a navbar is needed, returns either a regular GUI, or a GUI with a navbar
235 | #getContent() {
236 | // Only one page has been set, no navigation tabs will be created
237 | if(this.#guiPages.length == 1) {
238 | return this.#guiPages[0].content;
239 | }
240 | // Multiple pages has been set, dynamically creating the navigation tabs
241 | else if(this.#guiPages.length > 1) {
242 | const tabs = (list, panels) => `
243 |
246 |
247 | ${panels}
248 |
249 | `;
250 |
251 | let list = ``;
252 | let panels = ``;
253 |
254 | this.#guiPages.forEach(page => {
255 | const data = this.#createNavigationTab(page);
256 |
257 | if(data != undefined) {
258 | list += data.listItem + '\n';
259 | panels += data.panelItem + '\n';
260 | }
261 | });
262 |
263 | return tabs(list, panels);
264 | }
265 | }
266 |
267 | // Returns the GUI's whole document as string
268 | async #createDocument() {
269 | const bootstrapStyling = await this.#bypassCors("https://raw.githubusercontent.com/AugmentedWeb/UserGui/main/resources/bootstrap.css");
270 |
271 | const externalDocument = `
272 |
273 |
274 |
275 | ${this.settings.window.title}
276 |
288 |
289 |
290 | ${this.#getContent()}
291 |
292 |
293 | `;
294 |
295 | const internalDocument = `
296 |
297 |
298 |
299 |
311 |
312 |
313 |
314 |
322 |
323 | ${this.#getContent()}
324 |
325 |
326 |
327 |
328 |
329 | `;
330 |
331 | if(this.settings.window.external) {
332 | return externalDocument;
333 | } else {
334 | return internalDocument;
335 | }
336 | }
337 |
338 | // The user will use this function to add a page to their GUI, with their own HTML (Bootstrap 5)
339 | addPage(tabName, htmlString) {
340 | if(this.#guiPages[0].name == "default_no_content_set") {
341 | this.#guiPages = [];
342 | }
343 |
344 | this.#guiPages.push({
345 | "name" : tabName,
346 | "content" : htmlString
347 | });
348 | }
349 |
350 | #getCenterScreenPosition() {
351 | const guiWidth = this.settings.window.size.width;
352 | const guiHeight = this.settings.window.size.height;
353 |
354 | const x = (screen.width - guiWidth) / 2;
355 | const y = (screen.height - guiHeight) / 2;
356 |
357 | return { "x" : x, "y": y };
358 | }
359 |
360 | #getCenterWindowPosition() {
361 | const guiWidth = this.settings.window.size.width;
362 | const guiHeight = this.settings.window.size.height;
363 |
364 | const x = (window.innerWidth - guiWidth) / 2;
365 | const y = (window.innerHeight - guiHeight) / 2;
366 |
367 | return { "x" : x, "y": y };
368 | }
369 |
370 | #initializeInternalGuiEvents(iFrame) {
371 | // - The code below will consist mostly of drag and resize implementations
372 | // - iFrame window <-> Main window interaction requires these to be done
373 | // - Basically, iFrame document's event listeners make the whole iFrame move on the main window
374 |
375 | // Sets the iFrame's size
376 | function setFrameSize(x, y) {
377 | iFrame.style.width = `${x}px`;
378 | iFrame.style.height = `${y}px`;
379 | }
380 |
381 | // Gets the iFrame's size
382 | function getFrameSize() {
383 | const frameBounds = iFrame.getBoundingClientRect();
384 |
385 | return { "width" : frameBounds.width, "height" : frameBounds.height };
386 | }
387 |
388 | // Sets the iFrame's position relative to the main window's document
389 | function setFramePos(x, y) {
390 | iFrame.style.left = `${x}px`;
391 | iFrame.style.top = `${y}px`;
392 | }
393 |
394 | // Gets the iFrame's position relative to the main document
395 | function getFramePos() {
396 | const frameBounds = iFrame.getBoundingClientRect();
397 |
398 | return { "x": frameBounds.x, "y" : frameBounds.y };
399 | }
400 |
401 | // Gets the frame body's offsetHeight
402 | function getInnerFrameSize() {
403 | const innerFrameElem = iFrame.contentDocument.querySelector("#gui");
404 |
405 | return { "x": innerFrameElem.offsetWidth, "y" : innerFrameElem.offsetHeight };
406 | }
407 |
408 | // Sets the frame's size to the innerframe's size
409 | const adjustFrameSize = () => {
410 | const innerFrameSize = getInnerFrameSize();
411 |
412 | setFrameSize(innerFrameSize.x, innerFrameSize.y);
413 | }
414 |
415 | // Variables for draggable header
416 | let dragging = false,
417 | dragStartPos = { "x" : 0, "y" : 0 };
418 |
419 | // Variables for resizer
420 | let resizing = false,
421 | mousePos = { "x" : undefined, "y" : undefined },
422 | lastFrame;
423 |
424 | function handleResize(isInsideFrame, e) {
425 | if(mousePos.x == undefined && mousePos.y == undefined) {
426 | mousePos.x = e.clientX;
427 | mousePos.y = e.clientY;
428 |
429 | lastFrame = isInsideFrame;
430 | }
431 |
432 | const deltaX = mousePos.x - e.clientX,
433 | deltaY = mousePos.y - e.clientY;
434 |
435 | const frameSize = getFrameSize();
436 | const allowedSize = frameSize.width - deltaX > 160 && frameSize.height - deltaY > 90;
437 |
438 | if(isInsideFrame == lastFrame && allowedSize) {
439 | setFrameSize(frameSize.width - deltaX, frameSize.height - deltaY);
440 | }
441 |
442 | mousePos.x = e.clientX;
443 | mousePos.y = e.clientY;
444 |
445 | lastFrame = isInsideFrame;
446 | }
447 |
448 | function handleDrag(isInsideFrame, e) {
449 | const bR = iFrame.getBoundingClientRect();
450 |
451 | const windowWidth = window.innerWidth,
452 | windowHeight = window.innerHeight;
453 |
454 | let x, y;
455 |
456 | if(isInsideFrame) {
457 | x = getFramePos().x += e.clientX - dragStartPos.x;
458 | y = getFramePos().y += e.clientY - dragStartPos.y;
459 | } else {
460 | x = e.clientX - dragStartPos.x;
461 | y = e.clientY - dragStartPos.y;
462 | }
463 |
464 | // Check out of bounds: left
465 | if(x <= 0) {
466 | x = 0
467 | }
468 |
469 | // Check out of bounds: right
470 | if(x + bR.width >= windowWidth) {
471 | x = windowWidth - bR.width;
472 | }
473 |
474 | // Check out of bounds: top
475 | if(y <= 0) {
476 | y = 0;
477 | }
478 |
479 | // Check out of bounds: bottom
480 | if(y + bR.height >= windowHeight) {
481 | y = windowHeight - bR.height;
482 | }
483 |
484 | setFramePos(x, y);
485 | }
486 |
487 | // Dragging start (iFrame)
488 | this.document.querySelector("#header").addEventListener('mousedown', e => {
489 | e.preventDefault();
490 |
491 | dragging = true;
492 |
493 | dragStartPos.x = e.clientX;
494 | dragStartPos.y = e.clientY;
495 | });
496 |
497 | // Resizing start
498 | this.document.querySelector("#resizer").addEventListener('mousedown', e => {
499 | e.preventDefault();
500 |
501 | resizing = true;
502 | });
503 |
504 | // While dragging or resizing (iFrame)
505 | this.document.addEventListener('mousemove', e => {
506 | if(dragging)
507 | handleDrag(true, e);
508 |
509 | if(resizing)
510 | handleResize(true, e);
511 | });
512 |
513 | // While dragging or resizing (Main window)
514 | document.addEventListener('mousemove', e => {
515 | if(dragging)
516 | handleDrag(false, e);
517 |
518 | if(resizing)
519 | handleResize(false, e);
520 | });
521 |
522 | // Stop dragging and resizing (iFrame)
523 | this.document.addEventListener('mouseup', e => {
524 | e.preventDefault();
525 |
526 | dragging = false;
527 | resizing = false;
528 | });
529 |
530 | // Stop dragging and resizing (Main window)
531 | document.addEventListener('mouseup', e => {
532 | dragging = false;
533 | resizing = false;
534 | });
535 |
536 |
537 |
538 |
539 | const guiObserver = new MutationObserver(adjustFrameSize);
540 | const guiElement = this.document.querySelector("#gui");
541 |
542 | guiObserver.observe(guiElement, {
543 | childList: true,
544 | subtree: true,
545 | attributes: true
546 | });
547 |
548 | adjustFrameSize();
549 | }
550 |
551 | async #openExternalGui(readyFunction) {
552 | const noWindow = this.window?.closed;
553 |
554 | if(noWindow || this.window == undefined) {
555 | let pos = "";
556 | let windowSettings = "";
557 |
558 | if(this.settings.window.centered && this.settings.gui.external.popup) {
559 | const centerPos = this.#getCenterScreenPosition();
560 | pos = `left=${centerPos.x}, top=${centerPos.y}`;
561 | }
562 |
563 | if(this.settings.gui.external.popup) {
564 | windowSettings = `width=${this.settings.window.size.width}, height=${this.settings.window.size.height}, ${pos}`;
565 | }
566 |
567 | // Create a new window for the GUI
568 | this.window = window.open("", this.settings.windowName, windowSettings);
569 |
570 | if(!this.window) {
571 | this.settings.messages.blockedPopups();
572 | return;
573 | }
574 |
575 | // Write the document to the new window
576 | this.window.document.open();
577 | this.window.document.write(await this.#createDocument());
578 | this.window.document.close();
579 |
580 | if(!this.settings.gui.external.popup) {
581 | this.window.document.body.style.width = `${this.settings.window.size.width}px`;
582 |
583 | if(this.settings.window.centered) {
584 | const centerPos = this.#getCenterScreenPosition();
585 |
586 | this.window.document.body.style.position = "absolute";
587 | this.window.document.body.style.left = `${centerPos.x}px`;
588 | this.window.document.body.style.top = `${centerPos.y}px`;
589 | }
590 | }
591 |
592 | // Dynamic sizing (only height & window.outerHeight no longer works on some browsers...)
593 | this.window.resizeTo(
594 | this.settings.window.size.width,
595 | this.settings.window.size.dynamicSize
596 | ? this.window.document.body.offsetHeight + (this.window.outerHeight - this.window.innerHeight)
597 | : this.settings.window.size.height
598 | );
599 |
600 | this.document = this.window.document;
601 |
602 | this.#initializeTabs();
603 |
604 | // Call user's function
605 | if(typeof readyFunction == "function") {
606 | readyFunction();
607 | }
608 |
609 | window.onbeforeunload = () => {
610 | // Close the GUI if parent window closes
611 | this.close();
612 | }
613 | }
614 |
615 | else {
616 | // Window was already opened, bring the window back to focus
617 | this.window.focus();
618 | }
619 | }
620 |
621 | async #openInternalGui(readyFunction) {
622 | if(this.iFrame) {
623 | return;
624 | }
625 |
626 | const fadeInSpeedMs = 250;
627 |
628 | let left = 0, top = 0;
629 |
630 | if(this.settings.window.centered) {
631 | const centerPos = this.#getCenterWindowPosition();
632 |
633 | left = centerPos.x;
634 | top = centerPos.y;
635 | }
636 |
637 | const iframe = document.createElement("iframe");
638 | iframe.srcdoc = await this.#createDocument();
639 | iframe.style = `
640 | position: fixed;
641 | top: ${top}px;
642 | left: ${left}px;
643 | width: ${this.settings.window.size.width};
644 | height: ${this.settings.window.size.height};
645 | border: 0;
646 | opacity: 0;
647 | transition: all ${fadeInSpeedMs/1000}s;
648 | border-radius: 5px;
649 | box-shadow: rgb(0 0 0 / 6%) 10px 10px 10px;
650 | z-index: 2147483647;
651 | `;
652 |
653 | const waitForBody = setInterval(() => {
654 | if(document?.body) {
655 | clearInterval(waitForBody);
656 |
657 | // Prepend the GUI to the document's body
658 | document.body.prepend(iframe);
659 |
660 | iframe.contentWindow.onload = () => {
661 | // Fade-in implementation
662 | setTimeout(() => iframe.style["opacity"] = "1", fadeInSpeedMs/2);
663 | setTimeout(() => iframe.style["transition"] = "none", fadeInSpeedMs + 500);
664 |
665 | this.window = iframe.contentWindow;
666 | this.document = iframe.contentDocument;
667 | this.iFrame = iframe;
668 |
669 | this.#initializeInternalGuiEvents(iframe);
670 | this.#initializeTabs();
671 |
672 | readyFunction();
673 | }
674 | }
675 | }, 100);
676 | }
677 |
678 | // Determines if the window is to be opened externally or internally
679 | open(readyFunction) {
680 | if(this.settings.window.external) {
681 | this.#openExternalGui(readyFunction);
682 | } else {
683 | this.#openInternalGui(readyFunction);
684 | }
685 | }
686 |
687 | // Closes the GUI if it exists
688 | close() {
689 | if(this.settings.window.external) {
690 | if(this.window) {
691 | this.window.close();
692 | }
693 | } else {
694 | if(this.iFrame) {
695 | this.iFrame.remove();
696 | this.iFrame = undefined;
697 | }
698 | }
699 | }
700 |
701 | saveConfig() {
702 | let config = [];
703 |
704 | if(this.document) {
705 | [...this.document.querySelectorAll(".form-group")].forEach(elem => {
706 | const inputElem = elem.querySelector("[name]");
707 |
708 | const name = inputElem.getAttribute("name"),
709 | data = this.getData(name);
710 |
711 | if(data) {
712 | config.push({ "name" : name, "value" : data });
713 | }
714 | });
715 | }
716 |
717 | GM_setValue("config", config);
718 | }
719 |
720 | loadConfig() {
721 | const config = this.getConfig();
722 |
723 | if(this.document && config) {
724 | config.forEach(elemConfig => {
725 | this.setData(elemConfig.name, elemConfig.value);
726 | })
727 | }
728 | }
729 |
730 | getConfig() {
731 | return GM_getValue("config");
732 | }
733 |
734 | resetConfig() {
735 | const config = this.getConfig();
736 |
737 | if(config) {
738 | GM_setValue("config", []);
739 | }
740 | }
741 |
742 | dispatchFormEvent(name) {
743 | const type = name.split("-")[0].toLowerCase();
744 | const properties = this.#typeProperties.find(x => type == x.type);
745 | const event = new Event(properties.event);
746 |
747 | const field = this.document.querySelector(`.field-${name}`);
748 | field.dispatchEvent(event);
749 | }
750 |
751 | setPrimaryColor(hex) {
752 | const styles = `
753 | #header {
754 | background-color: ${hex} !important;
755 | }
756 | .nav-link {
757 | color: ${hex} !important;
758 | }
759 | .text-primary {
760 | color: ${hex} !important;
761 | }
762 | `;
763 |
764 | const styleSheet = document.createElement("style")
765 | styleSheet.innerText = styles;
766 | this.document.head.appendChild(styleSheet);
767 | }
768 |
769 | // Creates an event listener a GUI element
770 | event(name, event, eventFunction) {
771 | this.document.querySelector(`.field-${name}`).addEventListener(event, eventFunction);
772 | }
773 |
774 | // Disables a GUI element
775 | disable(name) {
776 | [...this.document.querySelector(`.field-${name}`).children].forEach(childElem => {
777 | childElem.setAttribute("disabled", "true");
778 | });
779 | }
780 |
781 | // Enables a GUI element
782 | enable(name) {
783 | [...this.document.querySelector(`.field-${name}`).children].forEach(childElem => {
784 | if(childElem.getAttribute("disabled")) {
785 | childElem.removeAttribute("disabled");
786 | }
787 | });
788 | }
789 |
790 | // Gets data from types: TEXT FIELD, TEXTAREA, DATE FIELD & NUMBER
791 | getValue(name) {
792 | return this.document.querySelector(`.field-${name}`).querySelector(`[id=${name}]`).value;
793 | }
794 |
795 | // Sets data to types: TEXT FIELD, TEXT AREA, DATE FIELD & NUMBER
796 | setValue(name, newValue) {
797 | this.document.querySelector(`.field-${name}`).querySelector(`[id=${name}]`).value = newValue;
798 |
799 | this.dispatchFormEvent(name);
800 | }
801 |
802 | // Gets data from types: RADIO GROUP
803 | getSelection(name) {
804 | return this.document.querySelector(`.field-${name}`).querySelector(`input[name=${name}]:checked`).value;
805 | }
806 |
807 | // Sets data to types: RADIO GROUP
808 | setSelection(name, newOptionsValue) {
809 | this.document.querySelector(`.field-${name}`).querySelector(`input[value=${newOptionsValue}]`).checked = true;
810 |
811 | this.dispatchFormEvent(name);
812 | }
813 |
814 | // Gets data from types: CHECKBOX GROUP
815 | getChecked(name) {
816 | return [...this.document.querySelector(`.field-${name}`).querySelectorAll(`input[name*=${name}]:checked`)]
817 | .map(checkbox => checkbox.value);
818 | }
819 |
820 | // Sets data to types: CHECKBOX GROUP
821 | setChecked(name, checkedArr) {
822 | const checkboxes = [...this.document.querySelector(`.field-${name}`).querySelectorAll(`input[name*=${name}]`)]
823 |
824 | checkboxes.forEach(checkbox => {
825 | if(checkedArr.includes(checkbox.value)) {
826 | checkbox.checked = true;
827 | }
828 | });
829 |
830 | this.dispatchFormEvent(name);
831 | }
832 |
833 | // Gets data from types: FILE UPLOAD
834 | getFiles(name) {
835 | return this.document.querySelector(`.field-${name}`).querySelector(`input[id=${name}]`).files;
836 | }
837 |
838 | // Gets data from types: SELECT
839 | getOption(name) {
840 | const selectedArr = [...this.document.querySelector(`.field-${name} #${name}`).selectedOptions].map(({value}) => value);
841 |
842 | return selectedArr.length == 1 ? selectedArr[0] : selectedArr;
843 | }
844 |
845 | // Sets data to types: SELECT
846 | setOption(name, newOptionsValue) {
847 | if(typeof newOptionsValue == 'object') {
848 | newOptionsValue.forEach(optionVal => {
849 | this.document.querySelector(`.field-${name}`).querySelector(`option[value=${optionVal}]`).selected = true;
850 | });
851 | } else {
852 | this.document.querySelector(`.field-${name}`).querySelector(`option[value=${newOptionsValue}]`).selected = true;
853 | }
854 |
855 | this.dispatchFormEvent(name);
856 | }
857 |
858 | #typeProperties = [
859 | {
860 | "type": "button",
861 | "event": "click",
862 | "function": {
863 | "get" : null,
864 | "set" : null
865 | }
866 | },
867 | {
868 | "type": "radio",
869 | "event": "change",
870 | "function": {
871 | "get" : n => this.getSelection(n),
872 | "set" : (n, nV) => this.setSelection(n, nV)
873 | }
874 | },
875 | {
876 | "type": "checkbox",
877 | "event": "change",
878 | "function": {
879 | "get" : n => this.getChecked(n),
880 | "set" : (n, nV) => this.setChecked(n, nV)
881 | }
882 | },
883 | {
884 | "type": "date",
885 | "event": "change",
886 | "function": {
887 | "get" : n => this.getValue(n),
888 | "set" : (n, nV) => this.setValue(n, nV)
889 | }
890 | },
891 | {
892 | "type": "file",
893 | "event": "change",
894 | "function": {
895 | "get" : n => this.getFiles(n),
896 | "set" : null
897 | }
898 | },
899 | {
900 | "type": "number",
901 | "event": "input",
902 | "function": {
903 | "get" : n => this.getValue(n),
904 | "set" : (n, nV) => this.setValue(n, nV)
905 | }
906 | },
907 | {
908 | "type": "select",
909 | "event": "change",
910 | "function": {
911 | "get" : n => this.getOption(n),
912 | "set" : (n, nV) => this.setOption(n, nV)
913 | }
914 | },
915 | {
916 | "type": "text",
917 | "event": "input",
918 | "function": {
919 | "get" : n => this.getValue(n),
920 | "set" : (n, nV) => this.setValue(n, nV)
921 | }
922 | },
923 | {
924 | "type": "textarea",
925 | "event": "input",
926 | "function": {
927 | "get" : n => this.getValue(n),
928 | "set" : (n, nV) => this.setValue(n, nV)
929 | }
930 | },
931 | ];
932 |
933 | // The same as the event() function, but automatically determines the best listener type for the element
934 | // (e.g. button -> listen for "click", textarea -> listen for "input")
935 | smartEvent(name, eventFunction) {
936 | if(name.includes("-")) {
937 | const type = name.split("-")[0].toLowerCase();
938 | const properties = this.#typeProperties.find(x => type == x.type);
939 |
940 | if(typeof properties == "object") {
941 | this.event(name, properties.event, eventFunction);
942 |
943 | } else {
944 | console.warn(`${this.#projectName}'s smartEvent function did not find any matches for the type "${type}". The event could not be made.`);
945 | }
946 |
947 | } else {
948 | console.warn(`The input name "${name}" is invalid for ${this.#projectName}'s smartEvent. The event could not be made.`);
949 | }
950 | }
951 |
952 | // Will automatically determine the suitable function for data retrivial
953 | // (e.g. file select -> use getFiles() function)
954 | getData(name) {
955 | if(name.includes("-")) {
956 | const type = name.split("-")[0].toLowerCase();
957 | const properties = this.#typeProperties.find(x => type == x.type);
958 |
959 | if(typeof properties == "object") {
960 | const getFunction = properties.function.get;
961 |
962 | if(typeof getFunction == "function") {
963 | return getFunction(name);
964 |
965 | } else {
966 | console.error(`${this.#projectName}'s getData function can't be used for the type "${type}". The data can't be taken.`);
967 | }
968 |
969 | } else {
970 | console.warn(`${this.#projectName}'s getData function did not find any matches for the type "${type}". The event could not be made.`);
971 | }
972 |
973 | } else {
974 | console.warn(`The input name "${name}" is invalid for ${this.#projectName}'s getData function. The event could not be made.`);
975 | }
976 | }
977 |
978 | // Will automatically determine the suitable function for data retrivial (e.g. checkbox -> use setChecked() function)
979 | setData(name, newData) {
980 | if(name.includes("-")) {
981 | const type = name.split("-")[0].toLowerCase();
982 | const properties = this.#typeProperties.find(x => type == x.type);
983 |
984 | if(typeof properties == "object") {
985 | const setFunction = properties.function.set;
986 |
987 | if(typeof setFunction == "function") {
988 | return setFunction(name, newData);
989 |
990 | } else {
991 | console.error(`${this.#projectName}'s setData function can't be used for the type "${type}". The data can't be taken.`);
992 | }
993 |
994 | } else {
995 | console.warn(`${this.#projectName}'s setData function did not find any matches for the type "${type}". The event could not be made.`);
996 | }
997 |
998 | } else {
999 | console.warn(`The input name "${name}" is invalid for ${this.#projectName}'s setData function. The event could not be made.`);
1000 | }
1001 | }
1002 | };
--------------------------------------------------------------------------------
/tampermonkey script/content/chessboard.css:
--------------------------------------------------------------------------------
1 | /*! chessboard.js v1.0.0 | (c) 2019 Chris Oakman | MIT License chessboardjs.com/license */
2 |
3 | .clearfix-7da63 {
4 | clear: both;
5 | }
6 |
7 | .board-b72b1 {
8 | border: 2px solid #404040;
9 | box-sizing: content-box;
10 | }
11 |
12 | .square-55d63 {
13 | float: left;
14 | position: relative;
15 |
16 | /* disable any native browser highlighting */
17 | -webkit-touch-callout: none;
18 | -webkit-user-select: none;
19 | -khtml-user-select: none;
20 | -moz-user-select: none;
21 | -ms-user-select: none;
22 | user-select: none;
23 | }
24 |
25 | .white-1e1d7 {
26 | background-color: #f0d9b5;
27 | color: #b58863;
28 | }
29 |
30 | .black-3c85d {
31 | background-color: #b58863;
32 | color: #f0d9b5;
33 | }
34 |
35 | .highlight1-32417, .highlight2-9c5d2 {
36 | box-shadow: inset 0 0 3px 3px yellow;
37 | }
38 |
39 | .notation-322f9 {
40 | cursor: default;
41 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
42 | font-size: 14px;
43 | position: absolute;
44 | }
45 |
46 | .alpha-d2270 {
47 | bottom: 1px;
48 | right: 3px;
49 | }
50 |
51 | .numeric-fc462 {
52 | top: 2px;
53 | left: 2px;
54 | }
55 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chessboard.js:
--------------------------------------------------------------------------------
1 | // chessboard.js v1.0.0
2 | // https://github.com/oakmac/chessboardjs/
3 | //
4 | // Copyright (c) 2019, Chris Oakman
5 | // Released under the MIT license
6 | // https://github.com/oakmac/chessboardjs/blob/master/LICENSE.md
7 |
8 | // start anonymous scope
9 | ;(function () {
10 | 'use strict'
11 |
12 | var $ = window['jQuery']
13 |
14 | // ---------------------------------------------------------------------------
15 | // Constants
16 | // ---------------------------------------------------------------------------
17 |
18 | var COLUMNS = 'abcdefgh'.split('')
19 | var DEFAULT_DRAG_THROTTLE_RATE = 20
20 | var ELLIPSIS = '…'
21 | var MINIMUM_JQUERY_VERSION = '1.8.3'
22 | var RUN_ASSERTS = false
23 | var START_FEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'
24 | var START_POSITION = fenToObj(START_FEN)
25 |
26 | // default animation speeds
27 | var DEFAULT_APPEAR_SPEED = 200
28 | var DEFAULT_MOVE_SPEED = 200
29 | var DEFAULT_SNAPBACK_SPEED = 60
30 | var DEFAULT_SNAP_SPEED = 30
31 | var DEFAULT_TRASH_SPEED = 100
32 |
33 | // use unique class names to prevent clashing with anything else on the page
34 | // and simplify selectors
35 | // NOTE: these should never change
36 | var CSS = {}
37 | CSS['alpha'] = 'alpha-d2270'
38 | CSS['black'] = 'black-3c85d'
39 | CSS['board'] = 'board-b72b1'
40 | CSS['chessboard'] = 'chessboard-63f37'
41 | CSS['clearfix'] = 'clearfix-7da63'
42 | CSS['highlight1'] = 'highlight1-32417'
43 | CSS['highlight2'] = 'highlight2-9c5d2'
44 | CSS['notation'] = 'notation-322f9'
45 | CSS['numeric'] = 'numeric-fc462'
46 | CSS['piece'] = 'piece-417db'
47 | CSS['row'] = 'row-5277c'
48 | CSS['sparePieces'] = 'spare-pieces-7492f'
49 | CSS['sparePiecesBottom'] = 'spare-pieces-bottom-ae20f'
50 | CSS['sparePiecesTop'] = 'spare-pieces-top-4028b'
51 | CSS['square'] = 'square-55d63'
52 | CSS['white'] = 'white-1e1d7'
53 |
54 | // ---------------------------------------------------------------------------
55 | // Misc Util Functions
56 | // ---------------------------------------------------------------------------
57 |
58 | function throttle (f, interval, scope) {
59 | var timeout = 0
60 | var shouldFire = false
61 | var args = []
62 |
63 | var handleTimeout = function () {
64 | timeout = 0
65 | if (shouldFire) {
66 | shouldFire = false
67 | fire()
68 | }
69 | }
70 |
71 | var fire = function () {
72 | timeout = window.setTimeout(handleTimeout, interval)
73 | f.apply(scope, args)
74 | }
75 |
76 | return function (_args) {
77 | args = arguments
78 | if (!timeout) {
79 | fire()
80 | } else {
81 | shouldFire = true
82 | }
83 | }
84 | }
85 |
86 | // function debounce (f, interval, scope) {
87 | // var timeout = 0
88 | // return function (_args) {
89 | // window.clearTimeout(timeout)
90 | // var args = arguments
91 | // timeout = window.setTimeout(function () {
92 | // f.apply(scope, args)
93 | // }, interval)
94 | // }
95 | // }
96 |
97 | function uuid () {
98 | return 'xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx'.replace(/x/g, function (c) {
99 | var r = (Math.random() * 16) | 0
100 | return r.toString(16)
101 | })
102 | }
103 |
104 | function deepCopy (thing) {
105 | return JSON.parse(JSON.stringify(thing))
106 | }
107 |
108 | function parseSemVer (version) {
109 | var tmp = version.split('.')
110 | return {
111 | major: parseInt(tmp[0], 10),
112 | minor: parseInt(tmp[1], 10),
113 | patch: parseInt(tmp[2], 10)
114 | }
115 | }
116 |
117 | // returns true if version is >= minimum
118 | function validSemanticVersion (version, minimum) {
119 | version = parseSemVer(version)
120 | minimum = parseSemVer(minimum)
121 |
122 | var versionNum = (version.major * 100000 * 100000) +
123 | (version.minor * 100000) +
124 | version.patch
125 | var minimumNum = (minimum.major * 100000 * 100000) +
126 | (minimum.minor * 100000) +
127 | minimum.patch
128 |
129 | return versionNum >= minimumNum
130 | }
131 |
132 | function interpolateTemplate (str, obj) {
133 | for (var key in obj) {
134 | if (!obj.hasOwnProperty(key)) continue
135 | var keyTemplateStr = '{' + key + '}'
136 | var value = obj[key]
137 | while (str.indexOf(keyTemplateStr) !== -1) {
138 | str = str.replace(keyTemplateStr, value)
139 | }
140 | }
141 | return str
142 | }
143 |
144 | if (RUN_ASSERTS) {
145 | console.assert(interpolateTemplate('abc', {a: 'x'}) === 'abc')
146 | console.assert(interpolateTemplate('{a}bc', {}) === '{a}bc')
147 | console.assert(interpolateTemplate('{a}bc', {p: 'q'}) === '{a}bc')
148 | console.assert(interpolateTemplate('{a}bc', {a: 'x'}) === 'xbc')
149 | console.assert(interpolateTemplate('{a}bc{a}bc', {a: 'x'}) === 'xbcxbc')
150 | console.assert(interpolateTemplate('{a}{a}{b}', {a: 'x', b: 'y'}) === 'xxy')
151 | }
152 |
153 | // ---------------------------------------------------------------------------
154 | // Predicates
155 | // ---------------------------------------------------------------------------
156 |
157 | function isString (s) {
158 | return typeof s === 'string'
159 | }
160 |
161 | function isFunction (f) {
162 | return typeof f === 'function'
163 | }
164 |
165 | function isInteger (n) {
166 | return typeof n === 'number' &&
167 | isFinite(n) &&
168 | Math.floor(n) === n
169 | }
170 |
171 | function validAnimationSpeed (speed) {
172 | if (speed === 'fast' || speed === 'slow') return true
173 | if (!isInteger(speed)) return false
174 | return speed >= 0
175 | }
176 |
177 | function validThrottleRate (rate) {
178 | return isInteger(rate) &&
179 | rate >= 1
180 | }
181 |
182 | function validMove (move) {
183 | // move should be a string
184 | if (!isString(move)) return false
185 |
186 | // move should be in the form of "e2-e4", "f6-d5"
187 | var squares = move.split('-')
188 | if (squares.length !== 2) return false
189 |
190 | return validSquare(squares[0]) && validSquare(squares[1])
191 | }
192 |
193 | function validSquare (square) {
194 | return isString(square) && square.search(/^[a-h][1-8]$/) !== -1
195 | }
196 |
197 | if (RUN_ASSERTS) {
198 | console.assert(validSquare('a1'))
199 | console.assert(validSquare('e2'))
200 | console.assert(!validSquare('D2'))
201 | console.assert(!validSquare('g9'))
202 | console.assert(!validSquare('a'))
203 | console.assert(!validSquare(true))
204 | console.assert(!validSquare(null))
205 | console.assert(!validSquare({}))
206 | }
207 |
208 | function validPieceCode (code) {
209 | return isString(code) && code.search(/^[bw][KQRNBP]$/) !== -1
210 | }
211 |
212 | if (RUN_ASSERTS) {
213 | console.assert(validPieceCode('bP'))
214 | console.assert(validPieceCode('bK'))
215 | console.assert(validPieceCode('wK'))
216 | console.assert(validPieceCode('wR'))
217 | console.assert(!validPieceCode('WR'))
218 | console.assert(!validPieceCode('Wr'))
219 | console.assert(!validPieceCode('a'))
220 | console.assert(!validPieceCode(true))
221 | console.assert(!validPieceCode(null))
222 | console.assert(!validPieceCode({}))
223 | }
224 |
225 | function validFen (fen) {
226 | if (!isString(fen)) return false
227 |
228 | // cut off any move, castling, etc info from the end
229 | // we're only interested in position information
230 | fen = fen.replace(/ .+$/, '')
231 |
232 | // expand the empty square numbers to just 1s
233 | fen = expandFenEmptySquares(fen)
234 |
235 | // FEN should be 8 sections separated by slashes
236 | var chunks = fen.split('/')
237 | if (chunks.length !== 8) return false
238 |
239 | // check each section
240 | for (var i = 0; i < 8; i++) {
241 | if (chunks[i].length !== 8 ||
242 | chunks[i].search(/[^kqrnbpKQRNBP1]/) !== -1) {
243 | return false
244 | }
245 | }
246 |
247 | return true
248 | }
249 |
250 | if (RUN_ASSERTS) {
251 | console.assert(validFen(START_FEN))
252 | console.assert(validFen('8/8/8/8/8/8/8/8'))
253 | console.assert(validFen('r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R'))
254 | console.assert(validFen('3r3r/1p4pp/2nb1k2/pP3p2/8/PB2PN2/p4PPP/R4RK1 b - - 0 1'))
255 | console.assert(!validFen('3r3z/1p4pp/2nb1k2/pP3p2/8/PB2PN2/p4PPP/R4RK1 b - - 0 1'))
256 | console.assert(!validFen('anbqkbnr/8/8/8/8/8/PPPPPPPP/8'))
257 | console.assert(!validFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/'))
258 | console.assert(!validFen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBN'))
259 | console.assert(!validFen('888888/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'))
260 | console.assert(!validFen('888888/pppppppp/74/8/8/8/PPPPPPPP/RNBQKBNR'))
261 | console.assert(!validFen({}))
262 | }
263 |
264 | function validPositionObject (pos) {
265 | if (!$.isPlainObject(pos)) return false
266 |
267 | for (var i in pos) {
268 | if (!pos.hasOwnProperty(i)) continue
269 |
270 | if (!validSquare(i) || !validPieceCode(pos[i])) {
271 | return false
272 | }
273 | }
274 |
275 | return true
276 | }
277 |
278 | if (RUN_ASSERTS) {
279 | console.assert(validPositionObject(START_POSITION))
280 | console.assert(validPositionObject({}))
281 | console.assert(validPositionObject({e2: 'wP'}))
282 | console.assert(validPositionObject({e2: 'wP', d2: 'wP'}))
283 | console.assert(!validPositionObject({e2: 'BP'}))
284 | console.assert(!validPositionObject({y2: 'wP'}))
285 | console.assert(!validPositionObject(null))
286 | console.assert(!validPositionObject('start'))
287 | console.assert(!validPositionObject(START_FEN))
288 | }
289 |
290 | function isTouchDevice () {
291 | return 'ontouchstart' in document.documentElement
292 | }
293 |
294 | function validJQueryVersion () {
295 | return typeof window.$ &&
296 | $.fn &&
297 | $.fn.jquery &&
298 | validSemanticVersion($.fn.jquery, MINIMUM_JQUERY_VERSION)
299 | }
300 |
301 | // ---------------------------------------------------------------------------
302 | // Chess Util Functions
303 | // ---------------------------------------------------------------------------
304 |
305 | // convert FEN piece code to bP, wK, etc
306 | function fenToPieceCode (piece) {
307 | // black piece
308 | if (piece.toLowerCase() === piece) {
309 | return 'b' + piece.toUpperCase()
310 | }
311 |
312 | // white piece
313 | return 'w' + piece.toUpperCase()
314 | }
315 |
316 | // convert bP, wK, etc code to FEN structure
317 | function pieceCodeToFen (piece) {
318 | var pieceCodeLetters = piece.split('')
319 |
320 | // white piece
321 | if (pieceCodeLetters[0] === 'w') {
322 | return pieceCodeLetters[1].toUpperCase()
323 | }
324 |
325 | // black piece
326 | return pieceCodeLetters[1].toLowerCase()
327 | }
328 |
329 | // convert FEN string to position object
330 | // returns false if the FEN string is invalid
331 | function fenToObj (fen) {
332 | if (!validFen(fen)) return false
333 |
334 | // cut off any move, castling, etc info from the end
335 | // we're only interested in position information
336 | fen = fen.replace(/ .+$/, '')
337 |
338 | var rows = fen.split('/')
339 | var position = {}
340 |
341 | var currentRow = 8
342 | for (var i = 0; i < 8; i++) {
343 | var row = rows[i].split('')
344 | var colIdx = 0
345 |
346 | // loop through each character in the FEN section
347 | for (var j = 0; j < row.length; j++) {
348 | // number / empty squares
349 | if (row[j].search(/[1-8]/) !== -1) {
350 | var numEmptySquares = parseInt(row[j], 10)
351 | colIdx = colIdx + numEmptySquares
352 | } else {
353 | // piece
354 | var square = COLUMNS[colIdx] + currentRow
355 | position[square] = fenToPieceCode(row[j])
356 | colIdx = colIdx + 1
357 | }
358 | }
359 |
360 | currentRow = currentRow - 1
361 | }
362 |
363 | return position
364 | }
365 |
366 | // position object to FEN string
367 | // returns false if the obj is not a valid position object
368 | function objToFen (obj) {
369 | if (!validPositionObject(obj)) return false
370 |
371 | var fen = ''
372 |
373 | var currentRow = 8
374 | for (var i = 0; i < 8; i++) {
375 | for (var j = 0; j < 8; j++) {
376 | var square = COLUMNS[j] + currentRow
377 |
378 | // piece exists
379 | if (obj.hasOwnProperty(square)) {
380 | fen = fen + pieceCodeToFen(obj[square])
381 | } else {
382 | // empty space
383 | fen = fen + '1'
384 | }
385 | }
386 |
387 | if (i !== 7) {
388 | fen = fen + '/'
389 | }
390 |
391 | currentRow = currentRow - 1
392 | }
393 |
394 | // squeeze the empty numbers together
395 | fen = squeezeFenEmptySquares(fen)
396 |
397 | return fen
398 | }
399 |
400 | if (RUN_ASSERTS) {
401 | console.assert(objToFen(START_POSITION) === START_FEN)
402 | console.assert(objToFen({}) === '8/8/8/8/8/8/8/8')
403 | console.assert(objToFen({a2: 'wP', 'b2': 'bP'}) === '8/8/8/8/8/8/Pp6/8')
404 | }
405 |
406 | function squeezeFenEmptySquares (fen) {
407 | return fen.replace(/11111111/g, '8')
408 | .replace(/1111111/g, '7')
409 | .replace(/111111/g, '6')
410 | .replace(/11111/g, '5')
411 | .replace(/1111/g, '4')
412 | .replace(/111/g, '3')
413 | .replace(/11/g, '2')
414 | }
415 |
416 | function expandFenEmptySquares (fen) {
417 | return fen.replace(/8/g, '11111111')
418 | .replace(/7/g, '1111111')
419 | .replace(/6/g, '111111')
420 | .replace(/5/g, '11111')
421 | .replace(/4/g, '1111')
422 | .replace(/3/g, '111')
423 | .replace(/2/g, '11')
424 | }
425 |
426 | // returns the distance between two squares
427 | function squareDistance (squareA, squareB) {
428 | var squareAArray = squareA.split('')
429 | var squareAx = COLUMNS.indexOf(squareAArray[0]) + 1
430 | var squareAy = parseInt(squareAArray[1], 10)
431 |
432 | var squareBArray = squareB.split('')
433 | var squareBx = COLUMNS.indexOf(squareBArray[0]) + 1
434 | var squareBy = parseInt(squareBArray[1], 10)
435 |
436 | var xDelta = Math.abs(squareAx - squareBx)
437 | var yDelta = Math.abs(squareAy - squareBy)
438 |
439 | if (xDelta >= yDelta) return xDelta
440 | return yDelta
441 | }
442 |
443 | // returns the square of the closest instance of piece
444 | // returns false if no instance of piece is found in position
445 | function findClosestPiece (position, piece, square) {
446 | // create array of closest squares from square
447 | var closestSquares = createRadius(square)
448 |
449 | // search through the position in order of distance for the piece
450 | for (var i = 0; i < closestSquares.length; i++) {
451 | var s = closestSquares[i]
452 |
453 | if (position.hasOwnProperty(s) && position[s] === piece) {
454 | return s
455 | }
456 | }
457 |
458 | return false
459 | }
460 |
461 | // returns an array of closest squares from square
462 | function createRadius (square) {
463 | var squares = []
464 |
465 | // calculate distance of all squares
466 | for (var i = 0; i < 8; i++) {
467 | for (var j = 0; j < 8; j++) {
468 | var s = COLUMNS[i] + (j + 1)
469 |
470 | // skip the square we're starting from
471 | if (square === s) continue
472 |
473 | squares.push({
474 | square: s,
475 | distance: squareDistance(square, s)
476 | })
477 | }
478 | }
479 |
480 | // sort by distance
481 | squares.sort(function (a, b) {
482 | return a.distance - b.distance
483 | })
484 |
485 | // just return the square code
486 | var surroundingSquares = []
487 | for (i = 0; i < squares.length; i++) {
488 | surroundingSquares.push(squares[i].square)
489 | }
490 |
491 | return surroundingSquares
492 | }
493 |
494 | // given a position and a set of moves, return a new position
495 | // with the moves executed
496 | function calculatePositionFromMoves (position, moves) {
497 | var newPosition = deepCopy(position)
498 |
499 | for (var i in moves) {
500 | if (!moves.hasOwnProperty(i)) continue
501 |
502 | // skip the move if the position doesn't have a piece on the source square
503 | if (!newPosition.hasOwnProperty(i)) continue
504 |
505 | var piece = newPosition[i]
506 | delete newPosition[i]
507 | newPosition[moves[i]] = piece
508 | }
509 |
510 | return newPosition
511 | }
512 |
513 | // TODO: add some asserts here for calculatePositionFromMoves
514 |
515 | // ---------------------------------------------------------------------------
516 | // HTML
517 | // ---------------------------------------------------------------------------
518 |
519 | function buildContainerHTML (hasSparePieces) {
520 | var html = ''
521 |
522 | if (hasSparePieces) {
523 | html += '
'
524 | }
525 |
526 | html += '
'
527 |
528 | if (hasSparePieces) {
529 | html += '
'
530 | }
531 |
532 | html += '
'
533 |
534 | return interpolateTemplate(html, CSS)
535 | }
536 |
537 | // ---------------------------------------------------------------------------
538 | // Config
539 | // ---------------------------------------------------------------------------
540 |
541 | function expandConfigArgumentShorthand (config) {
542 | if (config === 'start') {
543 | config = {position: deepCopy(START_POSITION)}
544 | } else if (validFen(config)) {
545 | config = {position: fenToObj(config)}
546 | } else if (validPositionObject(config)) {
547 | config = {position: deepCopy(config)}
548 | }
549 |
550 | // config must be an object
551 | if (!$.isPlainObject(config)) config = {}
552 |
553 | return config
554 | }
555 |
556 | // validate config / set default options
557 | function expandConfig (config) {
558 | // default for orientation is white
559 | if (config.orientation !== 'black') config.orientation = 'white'
560 |
561 | // default for showNotation is true
562 | if (config.showNotation !== false) config.showNotation = true
563 |
564 | // default for draggable is false
565 | if (config.draggable !== true) config.draggable = false
566 |
567 | // default for dropOffBoard is 'snapback'
568 | if (config.dropOffBoard !== 'trash') config.dropOffBoard = 'snapback'
569 |
570 | // default for sparePieces is false
571 | if (config.sparePieces !== true) config.sparePieces = false
572 |
573 | // draggable must be true if sparePieces is enabled
574 | if (config.sparePieces) config.draggable = true
575 |
576 | // default piece theme is wikipedia
577 | if (!config.hasOwnProperty('pieceTheme') ||
578 | (!isString(config.pieceTheme) && !isFunction(config.pieceTheme))) {
579 | config.pieceTheme = 'img/chesspieces/wikipedia/{piece}.png'
580 | }
581 |
582 | // animation speeds
583 | if (!validAnimationSpeed(config.appearSpeed)) config.appearSpeed = DEFAULT_APPEAR_SPEED
584 | if (!validAnimationSpeed(config.moveSpeed)) config.moveSpeed = DEFAULT_MOVE_SPEED
585 | if (!validAnimationSpeed(config.snapbackSpeed)) config.snapbackSpeed = DEFAULT_SNAPBACK_SPEED
586 | if (!validAnimationSpeed(config.snapSpeed)) config.snapSpeed = DEFAULT_SNAP_SPEED
587 | if (!validAnimationSpeed(config.trashSpeed)) config.trashSpeed = DEFAULT_TRASH_SPEED
588 |
589 | // throttle rate
590 | if (!validThrottleRate(config.dragThrottleRate)) config.dragThrottleRate = DEFAULT_DRAG_THROTTLE_RATE
591 |
592 | return config
593 | }
594 |
595 | // ---------------------------------------------------------------------------
596 | // Dependencies
597 | // ---------------------------------------------------------------------------
598 |
599 | // check for a compatible version of jQuery
600 | function checkJQuery () {
601 | if (!validJQueryVersion()) {
602 | var errorMsg = 'Chessboard Error 1005: Unable to find a valid version of jQuery. ' +
603 | 'Please include jQuery ' + MINIMUM_JQUERY_VERSION + ' or higher on the page' +
604 | '\n\n' +
605 | 'Exiting' + ELLIPSIS
606 | window.alert(errorMsg)
607 | return false
608 | }
609 |
610 | return true
611 | }
612 |
613 | // return either boolean false or the $container element
614 | function checkContainerArg (containerElOrString) {
615 | if (containerElOrString === '') {
616 | var errorMsg1 = 'Chessboard Error 1001: ' +
617 | 'The first argument to Chessboard() cannot be an empty string.' +
618 | '\n\n' +
619 | 'Exiting' + ELLIPSIS
620 | window.alert(errorMsg1)
621 | return false
622 | }
623 |
624 | // convert containerEl to query selector if it is a string
625 | if (isString(containerElOrString) &&
626 | containerElOrString.charAt(0) !== '#') {
627 | containerElOrString = '#' + containerElOrString
628 | }
629 |
630 | // containerEl must be something that becomes a jQuery collection of size 1
631 | var $container = $(containerElOrString)
632 | if ($container.length !== 1) {
633 | var errorMsg2 = 'Chessboard Error 1003: ' +
634 | 'The first argument to Chessboard() must be the ID of a DOM node, ' +
635 | 'an ID query selector, or a single DOM node.' +
636 | '\n\n' +
637 | 'Exiting' + ELLIPSIS
638 | window.alert(errorMsg2)
639 | return false
640 | }
641 |
642 | return $container
643 | }
644 |
645 | // ---------------------------------------------------------------------------
646 | // Constructor
647 | // ---------------------------------------------------------------------------
648 |
649 | function constructor (containerElOrString, config) {
650 | // first things first: check basic dependencies
651 | if (!checkJQuery()) return null
652 | var $container = checkContainerArg(containerElOrString)
653 | if (!$container) return null
654 |
655 | // ensure the config object is what we expect
656 | config = expandConfigArgumentShorthand(config)
657 | config = expandConfig(config)
658 |
659 | // DOM elements
660 | var $board = null
661 | var $draggedPiece = null
662 | var $sparePiecesTop = null
663 | var $sparePiecesBottom = null
664 |
665 | // constructor return object
666 | var widget = {}
667 |
668 | // -------------------------------------------------------------------------
669 | // Stateful
670 | // -------------------------------------------------------------------------
671 |
672 | var boardBorderSize = 2
673 | var currentOrientation = 'white'
674 | var currentPosition = {}
675 | var draggedPiece = null
676 | var draggedPieceLocation = null
677 | var draggedPieceSource = null
678 | var isDragging = false
679 | var sparePiecesElsIds = {}
680 | var squareElsIds = {}
681 | var squareElsOffsets = {}
682 | var squareSize = 16
683 |
684 | // -------------------------------------------------------------------------
685 | // Validation / Errors
686 | // -------------------------------------------------------------------------
687 |
688 | function error (code, msg, obj) {
689 | // do nothing if showErrors is not set
690 | if (
691 | config.hasOwnProperty('showErrors') !== true ||
692 | config.showErrors === false
693 | ) {
694 | return
695 | }
696 |
697 | var errorText = 'Chessboard Error ' + code + ': ' + msg
698 |
699 | // print to console
700 | if (
701 | config.showErrors === 'console' &&
702 | typeof console === 'object' &&
703 | typeof console.log === 'function'
704 | ) {
705 | console.log(errorText)
706 | if (arguments.length >= 2) {
707 | console.log(obj)
708 | }
709 | return
710 | }
711 |
712 | // alert errors
713 | if (config.showErrors === 'alert') {
714 | if (obj) {
715 | errorText += '\n\n' + JSON.stringify(obj)
716 | }
717 | window.alert(errorText)
718 | return
719 | }
720 |
721 | // custom function
722 | if (isFunction(config.showErrors)) {
723 | config.showErrors(code, msg, obj)
724 | }
725 | }
726 |
727 | function setInitialState () {
728 | currentOrientation = config.orientation
729 |
730 | // make sure position is valid
731 | if (config.hasOwnProperty('position')) {
732 | if (config.position === 'start') {
733 | currentPosition = deepCopy(START_POSITION)
734 | } else if (validFen(config.position)) {
735 | currentPosition = fenToObj(config.position)
736 | } else if (validPositionObject(config.position)) {
737 | currentPosition = deepCopy(config.position)
738 | } else {
739 | error(
740 | 7263,
741 | 'Invalid value passed to config.position.',
742 | config.position
743 | )
744 | }
745 | }
746 | }
747 |
748 | // -------------------------------------------------------------------------
749 | // DOM Misc
750 | // -------------------------------------------------------------------------
751 |
752 | // calculates square size based on the width of the container
753 | // got a little CSS black magic here, so let me explain:
754 | // get the width of the container element (could be anything), reduce by 1 for
755 | // fudge factor, and then keep reducing until we find an exact mod 8 for
756 | // our square size
757 | function calculateSquareSize () {
758 | var containerWidth = parseInt($container.width(), 10)
759 |
760 | // defensive, prevent infinite loop
761 | if (!containerWidth || containerWidth <= 0) {
762 | return 0
763 | }
764 |
765 | // pad one pixel
766 | var boardWidth = containerWidth - 1
767 |
768 | while (boardWidth % 8 !== 0 && boardWidth > 0) {
769 | boardWidth = boardWidth - 1
770 | }
771 |
772 | return boardWidth / 8
773 | }
774 |
775 | // create random IDs for elements
776 | function createElIds () {
777 | // squares on the board
778 | for (var i = 0; i < COLUMNS.length; i++) {
779 | for (var j = 1; j <= 8; j++) {
780 | var square = COLUMNS[i] + j
781 | squareElsIds[square] = square + '-' + uuid()
782 | }
783 | }
784 |
785 | // spare pieces
786 | var pieces = 'KQRNBP'.split('')
787 | for (i = 0; i < pieces.length; i++) {
788 | var whitePiece = 'w' + pieces[i]
789 | var blackPiece = 'b' + pieces[i]
790 | sparePiecesElsIds[whitePiece] = whitePiece + '-' + uuid()
791 | sparePiecesElsIds[blackPiece] = blackPiece + '-' + uuid()
792 | }
793 | }
794 |
795 | // -------------------------------------------------------------------------
796 | // Markup Building
797 | // -------------------------------------------------------------------------
798 |
799 | function buildBoardHTML (orientation) {
800 | if (orientation !== 'black') {
801 | orientation = 'white'
802 | }
803 |
804 | var html = ''
805 |
806 | // algebraic notation / orientation
807 | var alpha = deepCopy(COLUMNS)
808 | var row = 8
809 | if (orientation === 'black') {
810 | alpha.reverse()
811 | row = 1
812 | }
813 |
814 | var squareColor = 'white'
815 | for (var i = 0; i < 8; i++) {
816 | html += ''
817 | for (var j = 0; j < 8; j++) {
818 | var square = alpha[j] + row
819 |
820 | html += '
'
825 |
826 | if (config.showNotation) {
827 | // alpha notation
828 | if ((orientation === 'white' && row === 1) ||
829 | (orientation === 'black' && row === 8)) {
830 | html += '
' + alpha[j] + '
'
831 | }
832 |
833 | // numeric notation
834 | if (j === 0) {
835 | html += '
' + row + '
'
836 | }
837 | }
838 |
839 | html += '
' // end .square
840 |
841 | squareColor = (squareColor === 'white') ? 'black' : 'white'
842 | }
843 | html += '
'
844 |
845 | squareColor = (squareColor === 'white') ? 'black' : 'white'
846 |
847 | if (orientation === 'white') {
848 | row = row - 1
849 | } else {
850 | row = row + 1
851 | }
852 | }
853 |
854 | return interpolateTemplate(html, CSS)
855 | }
856 |
857 | function buildPieceImgSrc (piece) {
858 | if (isFunction(config.pieceTheme)) {
859 | return config.pieceTheme(piece)
860 | }
861 |
862 | if (isString(config.pieceTheme)) {
863 | return interpolateTemplate(config.pieceTheme, {piece: piece})
864 | }
865 |
866 | // NOTE: this should never happen
867 | error(8272, 'Unable to build image source for config.pieceTheme.')
868 | return ''
869 | }
870 |
871 | function buildPieceHTML (piece, hidden, id) {
872 | var html = '
'
886 |
887 | return interpolateTemplate(html, CSS)
888 | }
889 |
890 | function buildSparePiecesHTML (color) {
891 | var pieces = ['wK', 'wQ', 'wR', 'wB', 'wN', 'wP']
892 | if (color === 'black') {
893 | pieces = ['bK', 'bQ', 'bR', 'bB', 'bN', 'bP']
894 | }
895 |
896 | var html = ''
897 | for (var i = 0; i < pieces.length; i++) {
898 | html += buildPieceHTML(pieces[i], false, sparePiecesElsIds[pieces[i]])
899 | }
900 |
901 | return html
902 | }
903 |
904 | // -------------------------------------------------------------------------
905 | // Animations
906 | // -------------------------------------------------------------------------
907 |
908 | function animateSquareToSquare (src, dest, piece, completeFn) {
909 | // get information about the source and destination squares
910 | var $srcSquare = $('#' + squareElsIds[src])
911 | var srcSquarePosition = $srcSquare.offset()
912 | var $destSquare = $('#' + squareElsIds[dest])
913 | var destSquarePosition = $destSquare.offset()
914 |
915 | // create the animated piece and absolutely position it
916 | // over the source square
917 | var animatedPieceId = uuid()
918 | $('body').append(buildPieceHTML(piece, true, animatedPieceId))
919 | var $animatedPiece = $('#' + animatedPieceId)
920 | $animatedPiece.css({
921 | display: '',
922 | position: 'absolute',
923 | top: srcSquarePosition.top,
924 | left: srcSquarePosition.left
925 | })
926 |
927 | // remove original piece from source square
928 | $srcSquare.find('.' + CSS.piece).remove()
929 |
930 | function onFinishAnimation1 () {
931 | // add the "real" piece to the destination square
932 | $destSquare.append(buildPieceHTML(piece))
933 |
934 | // remove the animated piece
935 | $animatedPiece.remove()
936 |
937 | // run complete function
938 | if (isFunction(completeFn)) {
939 | completeFn()
940 | }
941 | }
942 |
943 | // animate the piece to the destination square
944 | var opts = {
945 | duration: config.moveSpeed,
946 | complete: onFinishAnimation1
947 | }
948 | $animatedPiece.animate(destSquarePosition, opts)
949 | }
950 |
951 | function animateSparePieceToSquare (piece, dest, completeFn) {
952 | var srcOffset = $('#' + sparePiecesElsIds[piece]).offset()
953 | var $destSquare = $('#' + squareElsIds[dest])
954 | var destOffset = $destSquare.offset()
955 |
956 | // create the animate piece
957 | var pieceId = uuid()
958 | $('body').append(buildPieceHTML(piece, true, pieceId))
959 | var $animatedPiece = $('#' + pieceId)
960 | $animatedPiece.css({
961 | display: '',
962 | position: 'absolute',
963 | left: srcOffset.left,
964 | top: srcOffset.top
965 | })
966 |
967 | // on complete
968 | function onFinishAnimation2 () {
969 | // add the "real" piece to the destination square
970 | $destSquare.find('.' + CSS.piece).remove()
971 | $destSquare.append(buildPieceHTML(piece))
972 |
973 | // remove the animated piece
974 | $animatedPiece.remove()
975 |
976 | // run complete function
977 | if (isFunction(completeFn)) {
978 | completeFn()
979 | }
980 | }
981 |
982 | // animate the piece to the destination square
983 | var opts = {
984 | duration: config.moveSpeed,
985 | complete: onFinishAnimation2
986 | }
987 | $animatedPiece.animate(destOffset, opts)
988 | }
989 |
990 | // execute an array of animations
991 | function doAnimations (animations, oldPos, newPos) {
992 | if (animations.length === 0) return
993 |
994 | var numFinished = 0
995 | function onFinishAnimation3 () {
996 | // exit if all the animations aren't finished
997 | numFinished = numFinished + 1
998 | if (numFinished !== animations.length) return
999 |
1000 | drawPositionInstant()
1001 |
1002 | // run their onMoveEnd function
1003 | if (isFunction(config.onMoveEnd)) {
1004 | config.onMoveEnd(deepCopy(oldPos), deepCopy(newPos))
1005 | }
1006 | }
1007 |
1008 | for (var i = 0; i < animations.length; i++) {
1009 | var animation = animations[i]
1010 |
1011 | // clear a piece
1012 | if (animation.type === 'clear') {
1013 | $('#' + squareElsIds[animation.square] + ' .' + CSS.piece)
1014 | .fadeOut(config.trashSpeed, onFinishAnimation3)
1015 |
1016 | // add a piece with no spare pieces - fade the piece onto the square
1017 | } else if (animation.type === 'add' && !config.sparePieces) {
1018 | $('#' + squareElsIds[animation.square])
1019 | .append(buildPieceHTML(animation.piece, true))
1020 | .find('.' + CSS.piece)
1021 | .fadeIn(config.appearSpeed, onFinishAnimation3)
1022 |
1023 | // add a piece with spare pieces - animate from the spares
1024 | } else if (animation.type === 'add' && config.sparePieces) {
1025 | animateSparePieceToSquare(animation.piece, animation.square, onFinishAnimation3)
1026 |
1027 | // move a piece from squareA to squareB
1028 | } else if (animation.type === 'move') {
1029 | animateSquareToSquare(animation.source, animation.destination, animation.piece, onFinishAnimation3)
1030 | }
1031 | }
1032 | }
1033 |
1034 | // calculate an array of animations that need to happen in order to get
1035 | // from pos1 to pos2
1036 | function calculateAnimations (pos1, pos2) {
1037 | // make copies of both
1038 | pos1 = deepCopy(pos1)
1039 | pos2 = deepCopy(pos2)
1040 |
1041 | var animations = []
1042 | var squaresMovedTo = {}
1043 |
1044 | // remove pieces that are the same in both positions
1045 | for (var i in pos2) {
1046 | if (!pos2.hasOwnProperty(i)) continue
1047 |
1048 | if (pos1.hasOwnProperty(i) && pos1[i] === pos2[i]) {
1049 | delete pos1[i]
1050 | delete pos2[i]
1051 | }
1052 | }
1053 |
1054 | // find all the "move" animations
1055 | for (i in pos2) {
1056 | if (!pos2.hasOwnProperty(i)) continue
1057 |
1058 | var closestPiece = findClosestPiece(pos1, pos2[i], i)
1059 | if (closestPiece) {
1060 | animations.push({
1061 | type: 'move',
1062 | source: closestPiece,
1063 | destination: i,
1064 | piece: pos2[i]
1065 | })
1066 |
1067 | delete pos1[closestPiece]
1068 | delete pos2[i]
1069 | squaresMovedTo[i] = true
1070 | }
1071 | }
1072 |
1073 | // "add" animations
1074 | for (i in pos2) {
1075 | if (!pos2.hasOwnProperty(i)) continue
1076 |
1077 | animations.push({
1078 | type: 'add',
1079 | square: i,
1080 | piece: pos2[i]
1081 | })
1082 |
1083 | delete pos2[i]
1084 | }
1085 |
1086 | // "clear" animations
1087 | for (i in pos1) {
1088 | if (!pos1.hasOwnProperty(i)) continue
1089 |
1090 | // do not clear a piece if it is on a square that is the result
1091 | // of a "move", ie: a piece capture
1092 | if (squaresMovedTo.hasOwnProperty(i)) continue
1093 |
1094 | animations.push({
1095 | type: 'clear',
1096 | square: i,
1097 | piece: pos1[i]
1098 | })
1099 |
1100 | delete pos1[i]
1101 | }
1102 |
1103 | return animations
1104 | }
1105 |
1106 | // -------------------------------------------------------------------------
1107 | // Control Flow
1108 | // -------------------------------------------------------------------------
1109 |
1110 | function drawPositionInstant () {
1111 | // clear the board
1112 | $board.find('.' + CSS.piece).remove()
1113 |
1114 | // add the pieces
1115 | for (var i in currentPosition) {
1116 | if (!currentPosition.hasOwnProperty(i)) continue
1117 |
1118 | $('#' + squareElsIds[i]).append(buildPieceHTML(currentPosition[i]))
1119 | }
1120 | }
1121 |
1122 | function drawBoard () {
1123 | $board.html(buildBoardHTML(currentOrientation, squareSize, config.showNotation))
1124 | drawPositionInstant()
1125 |
1126 | if (config.sparePieces) {
1127 | if (currentOrientation === 'white') {
1128 | $sparePiecesTop.html(buildSparePiecesHTML('black'))
1129 | $sparePiecesBottom.html(buildSparePiecesHTML('white'))
1130 | } else {
1131 | $sparePiecesTop.html(buildSparePiecesHTML('white'))
1132 | $sparePiecesBottom.html(buildSparePiecesHTML('black'))
1133 | }
1134 | }
1135 | }
1136 |
1137 | function setCurrentPosition (position) {
1138 | var oldPos = deepCopy(currentPosition)
1139 | var newPos = deepCopy(position)
1140 | var oldFen = objToFen(oldPos)
1141 | var newFen = objToFen(newPos)
1142 |
1143 | // do nothing if no change in position
1144 | if (oldFen === newFen) return
1145 |
1146 | // run their onChange function
1147 | if (isFunction(config.onChange)) {
1148 | config.onChange(oldPos, newPos)
1149 | }
1150 |
1151 | // update state
1152 | currentPosition = position
1153 | }
1154 |
1155 | function isXYOnSquare (x, y) {
1156 | for (var i in squareElsOffsets) {
1157 | if (!squareElsOffsets.hasOwnProperty(i)) continue
1158 |
1159 | var s = squareElsOffsets[i]
1160 | if (x >= s.left &&
1161 | x < s.left + squareSize &&
1162 | y >= s.top &&
1163 | y < s.top + squareSize) {
1164 | return i
1165 | }
1166 | }
1167 |
1168 | return 'offboard'
1169 | }
1170 |
1171 | // records the XY coords of every square into memory
1172 | function captureSquareOffsets () {
1173 | squareElsOffsets = {}
1174 |
1175 | for (var i in squareElsIds) {
1176 | if (!squareElsIds.hasOwnProperty(i)) continue
1177 |
1178 | squareElsOffsets[i] = $('#' + squareElsIds[i]).offset()
1179 | }
1180 | }
1181 |
1182 | function removeSquareHighlights () {
1183 | $board
1184 | .find('.' + CSS.square)
1185 | .removeClass(CSS.highlight1 + ' ' + CSS.highlight2)
1186 | }
1187 |
1188 | function snapbackDraggedPiece () {
1189 | // there is no "snapback" for spare pieces
1190 | if (draggedPieceSource === 'spare') {
1191 | trashDraggedPiece()
1192 | return
1193 | }
1194 |
1195 | removeSquareHighlights()
1196 |
1197 | // animation complete
1198 | function complete () {
1199 | drawPositionInstant()
1200 | $draggedPiece.css('display', 'none')
1201 |
1202 | // run their onSnapbackEnd function
1203 | if (isFunction(config.onSnapbackEnd)) {
1204 | config.onSnapbackEnd(
1205 | draggedPiece,
1206 | draggedPieceSource,
1207 | deepCopy(currentPosition),
1208 | currentOrientation
1209 | )
1210 | }
1211 | }
1212 |
1213 | // get source square position
1214 | var sourceSquarePosition = $('#' + squareElsIds[draggedPieceSource]).offset()
1215 |
1216 | // animate the piece to the target square
1217 | var opts = {
1218 | duration: config.snapbackSpeed,
1219 | complete: complete
1220 | }
1221 | $draggedPiece.animate(sourceSquarePosition, opts)
1222 |
1223 | // set state
1224 | isDragging = false
1225 | }
1226 |
1227 | function trashDraggedPiece () {
1228 | removeSquareHighlights()
1229 |
1230 | // remove the source piece
1231 | var newPosition = deepCopy(currentPosition)
1232 | delete newPosition[draggedPieceSource]
1233 | setCurrentPosition(newPosition)
1234 |
1235 | // redraw the position
1236 | drawPositionInstant()
1237 |
1238 | // hide the dragged piece
1239 | $draggedPiece.fadeOut(config.trashSpeed)
1240 |
1241 | // set state
1242 | isDragging = false
1243 | }
1244 |
1245 | function dropDraggedPieceOnSquare (square) {
1246 | removeSquareHighlights()
1247 |
1248 | // update position
1249 | var newPosition = deepCopy(currentPosition)
1250 | delete newPosition[draggedPieceSource]
1251 | newPosition[square] = draggedPiece
1252 | setCurrentPosition(newPosition)
1253 |
1254 | // get target square information
1255 | var targetSquarePosition = $('#' + squareElsIds[square]).offset()
1256 |
1257 | // animation complete
1258 | function onAnimationComplete () {
1259 | drawPositionInstant()
1260 | $draggedPiece.css('display', 'none')
1261 |
1262 | // execute their onSnapEnd function
1263 | if (isFunction(config.onSnapEnd)) {
1264 | config.onSnapEnd(draggedPieceSource, square, draggedPiece)
1265 | }
1266 | }
1267 |
1268 | // snap the piece to the target square
1269 | var opts = {
1270 | duration: config.snapSpeed,
1271 | complete: onAnimationComplete
1272 | }
1273 | $draggedPiece.animate(targetSquarePosition, opts)
1274 |
1275 | // set state
1276 | isDragging = false
1277 | }
1278 |
1279 | function beginDraggingPiece (source, piece, x, y) {
1280 | // run their custom onDragStart function
1281 | // their custom onDragStart function can cancel drag start
1282 | if (isFunction(config.onDragStart) &&
1283 | config.onDragStart(source, piece, deepCopy(currentPosition), currentOrientation) === false) {
1284 | return
1285 | }
1286 |
1287 | // set state
1288 | isDragging = true
1289 | draggedPiece = piece
1290 | draggedPieceSource = source
1291 |
1292 | // if the piece came from spare pieces, location is offboard
1293 | if (source === 'spare') {
1294 | draggedPieceLocation = 'offboard'
1295 | } else {
1296 | draggedPieceLocation = source
1297 | }
1298 |
1299 | // capture the x, y coords of all squares in memory
1300 | captureSquareOffsets()
1301 |
1302 | // create the dragged piece
1303 | $draggedPiece.attr('src', buildPieceImgSrc(piece)).css({
1304 | display: '',
1305 | position: 'absolute',
1306 | left: x - squareSize / 2,
1307 | top: y - squareSize / 2
1308 | })
1309 |
1310 | if (source !== 'spare') {
1311 | // highlight the source square and hide the piece
1312 | $('#' + squareElsIds[source])
1313 | .addClass(CSS.highlight1)
1314 | .find('.' + CSS.piece)
1315 | .css('display', 'none')
1316 | }
1317 | }
1318 |
1319 | function updateDraggedPiece (x, y) {
1320 | // put the dragged piece over the mouse cursor
1321 | $draggedPiece.css({
1322 | left: x - squareSize / 2,
1323 | top: y - squareSize / 2
1324 | })
1325 |
1326 | // get location
1327 | var location = isXYOnSquare(x, y)
1328 |
1329 | // do nothing if the location has not changed
1330 | if (location === draggedPieceLocation) return
1331 |
1332 | // remove highlight from previous square
1333 | if (validSquare(draggedPieceLocation)) {
1334 | $('#' + squareElsIds[draggedPieceLocation]).removeClass(CSS.highlight2)
1335 | }
1336 |
1337 | // add highlight to new square
1338 | if (validSquare(location)) {
1339 | $('#' + squareElsIds[location]).addClass(CSS.highlight2)
1340 | }
1341 |
1342 | // run onDragMove
1343 | if (isFunction(config.onDragMove)) {
1344 | config.onDragMove(
1345 | location,
1346 | draggedPieceLocation,
1347 | draggedPieceSource,
1348 | draggedPiece,
1349 | deepCopy(currentPosition),
1350 | currentOrientation
1351 | )
1352 | }
1353 |
1354 | // update state
1355 | draggedPieceLocation = location
1356 | }
1357 |
1358 | function stopDraggedPiece (location) {
1359 | // determine what the action should be
1360 | var action = 'drop'
1361 | if (location === 'offboard' && config.dropOffBoard === 'snapback') {
1362 | action = 'snapback'
1363 | }
1364 | if (location === 'offboard' && config.dropOffBoard === 'trash') {
1365 | action = 'trash'
1366 | }
1367 |
1368 | // run their onDrop function, which can potentially change the drop action
1369 | if (isFunction(config.onDrop)) {
1370 | var newPosition = deepCopy(currentPosition)
1371 |
1372 | // source piece is a spare piece and position is off the board
1373 | // if (draggedPieceSource === 'spare' && location === 'offboard') {...}
1374 | // position has not changed; do nothing
1375 |
1376 | // source piece is a spare piece and position is on the board
1377 | if (draggedPieceSource === 'spare' && validSquare(location)) {
1378 | // add the piece to the board
1379 | newPosition[location] = draggedPiece
1380 | }
1381 |
1382 | // source piece was on the board and position is off the board
1383 | if (validSquare(draggedPieceSource) && location === 'offboard') {
1384 | // remove the piece from the board
1385 | delete newPosition[draggedPieceSource]
1386 | }
1387 |
1388 | // source piece was on the board and position is on the board
1389 | if (validSquare(draggedPieceSource) && validSquare(location)) {
1390 | // move the piece
1391 | delete newPosition[draggedPieceSource]
1392 | newPosition[location] = draggedPiece
1393 | }
1394 |
1395 | var oldPosition = deepCopy(currentPosition)
1396 |
1397 | var result = config.onDrop(
1398 | draggedPieceSource,
1399 | location,
1400 | draggedPiece,
1401 | newPosition,
1402 | oldPosition,
1403 | currentOrientation
1404 | )
1405 | if (result === 'snapback' || result === 'trash') {
1406 | action = result
1407 | }
1408 | }
1409 |
1410 | // do it!
1411 | if (action === 'snapback') {
1412 | snapbackDraggedPiece()
1413 | } else if (action === 'trash') {
1414 | trashDraggedPiece()
1415 | } else if (action === 'drop') {
1416 | dropDraggedPieceOnSquare(location)
1417 | }
1418 | }
1419 |
1420 | // -------------------------------------------------------------------------
1421 | // Public Methods
1422 | // -------------------------------------------------------------------------
1423 |
1424 | // clear the board
1425 | widget.clear = function (useAnimation) {
1426 | widget.position({}, useAnimation)
1427 | }
1428 |
1429 | // remove the widget from the page
1430 | widget.destroy = function () {
1431 | // remove markup
1432 | $container.html('')
1433 | $draggedPiece.remove()
1434 |
1435 | // remove event handlers
1436 | $container.unbind()
1437 | }
1438 |
1439 | // shorthand method to get the current FEN
1440 | widget.fen = function () {
1441 | return widget.position('fen')
1442 | }
1443 |
1444 | // flip orientation
1445 | widget.flip = function () {
1446 | return widget.orientation('flip')
1447 | }
1448 |
1449 | // move pieces
1450 | // TODO: this method should be variadic as well as accept an array of moves
1451 | widget.move = function () {
1452 | // no need to throw an error here; just do nothing
1453 | // TODO: this should return the current position
1454 | if (arguments.length === 0) return
1455 |
1456 | var useAnimation = true
1457 |
1458 | // collect the moves into an object
1459 | var moves = {}
1460 | for (var i = 0; i < arguments.length; i++) {
1461 | // any "false" to this function means no animations
1462 | if (arguments[i] === false) {
1463 | useAnimation = false
1464 | continue
1465 | }
1466 |
1467 | // skip invalid arguments
1468 | if (!validMove(arguments[i])) {
1469 | error(2826, 'Invalid move passed to the move method.', arguments[i])
1470 | continue
1471 | }
1472 |
1473 | var tmp = arguments[i].split('-')
1474 | moves[tmp[0]] = tmp[1]
1475 | }
1476 |
1477 | // calculate position from moves
1478 | var newPos = calculatePositionFromMoves(currentPosition, moves)
1479 |
1480 | // update the board
1481 | widget.position(newPos, useAnimation)
1482 |
1483 | // return the new position object
1484 | return newPos
1485 | }
1486 |
1487 | widget.orientation = function (arg) {
1488 | // no arguments, return the current orientation
1489 | if (arguments.length === 0) {
1490 | return currentOrientation
1491 | }
1492 |
1493 | // set to white or black
1494 | if (arg === 'white' || arg === 'black') {
1495 | currentOrientation = arg
1496 | drawBoard()
1497 | return currentOrientation
1498 | }
1499 |
1500 | // flip orientation
1501 | if (arg === 'flip') {
1502 | currentOrientation = currentOrientation === 'white' ? 'black' : 'white'
1503 | drawBoard()
1504 | return currentOrientation
1505 | }
1506 |
1507 | error(5482, 'Invalid value passed to the orientation method.', arg)
1508 | }
1509 |
1510 | widget.position = function (position, useAnimation) {
1511 | // no arguments, return the current position
1512 | if (arguments.length === 0) {
1513 | return deepCopy(currentPosition)
1514 | }
1515 |
1516 | // get position as FEN
1517 | if (isString(position) && position.toLowerCase() === 'fen') {
1518 | return objToFen(currentPosition)
1519 | }
1520 |
1521 | // start position
1522 | if (isString(position) && position.toLowerCase() === 'start') {
1523 | position = deepCopy(START_POSITION)
1524 | }
1525 |
1526 | // convert FEN to position object
1527 | if (validFen(position)) {
1528 | position = fenToObj(position)
1529 | }
1530 |
1531 | // validate position object
1532 | if (!validPositionObject(position)) {
1533 | error(6482, 'Invalid value passed to the position method.', position)
1534 | return
1535 | }
1536 |
1537 | // default for useAnimations is true
1538 | if (useAnimation !== false) useAnimation = true
1539 |
1540 | if (useAnimation) {
1541 | // start the animations
1542 | var animations = calculateAnimations(currentPosition, position)
1543 | doAnimations(animations, currentPosition, position)
1544 |
1545 | // set the new position
1546 | setCurrentPosition(position)
1547 | } else {
1548 | // instant update
1549 | setCurrentPosition(position)
1550 | drawPositionInstant()
1551 | }
1552 | }
1553 |
1554 | widget.resize = function () {
1555 | // calulate the new square size
1556 | squareSize = calculateSquareSize()
1557 |
1558 | // set board width
1559 | $board.css('width', squareSize * 8 + 'px')
1560 |
1561 | // set drag piece size
1562 | $draggedPiece.css({
1563 | height: squareSize,
1564 | width: squareSize
1565 | })
1566 |
1567 | // spare pieces
1568 | if (config.sparePieces) {
1569 | $container
1570 | .find('.' + CSS.sparePieces)
1571 | .css('paddingLeft', squareSize + boardBorderSize + 'px')
1572 | }
1573 |
1574 | // redraw the board
1575 | drawBoard()
1576 | }
1577 |
1578 | // set the starting position
1579 | widget.start = function (useAnimation) {
1580 | widget.position('start', useAnimation)
1581 | }
1582 |
1583 | // -------------------------------------------------------------------------
1584 | // Browser Events
1585 | // -------------------------------------------------------------------------
1586 |
1587 | function stopDefault (evt) {
1588 | evt.preventDefault()
1589 | }
1590 |
1591 | function mousedownSquare (evt) {
1592 | // do nothing if we're not draggable
1593 | if (!config.draggable) return
1594 |
1595 | // do nothing if there is no piece on this square
1596 | var square = $(this).attr('data-square')
1597 | if (!validSquare(square)) return
1598 | if (!currentPosition.hasOwnProperty(square)) return
1599 |
1600 | beginDraggingPiece(square, currentPosition[square], evt.pageX, evt.pageY)
1601 | }
1602 |
1603 | function touchstartSquare (e) {
1604 | // do nothing if we're not draggable
1605 | if (!config.draggable) return
1606 |
1607 | // do nothing if there is no piece on this square
1608 | var square = $(this).attr('data-square')
1609 | if (!validSquare(square)) return
1610 | if (!currentPosition.hasOwnProperty(square)) return
1611 |
1612 | e = e.originalEvent
1613 | beginDraggingPiece(
1614 | square,
1615 | currentPosition[square],
1616 | e.changedTouches[0].pageX,
1617 | e.changedTouches[0].pageY
1618 | )
1619 | }
1620 |
1621 | function mousedownSparePiece (evt) {
1622 | // do nothing if sparePieces is not enabled
1623 | if (!config.sparePieces) return
1624 |
1625 | var piece = $(this).attr('data-piece')
1626 |
1627 | beginDraggingPiece('spare', piece, evt.pageX, evt.pageY)
1628 | }
1629 |
1630 | function touchstartSparePiece (e) {
1631 | // do nothing if sparePieces is not enabled
1632 | if (!config.sparePieces) return
1633 |
1634 | var piece = $(this).attr('data-piece')
1635 |
1636 | e = e.originalEvent
1637 | beginDraggingPiece(
1638 | 'spare',
1639 | piece,
1640 | e.changedTouches[0].pageX,
1641 | e.changedTouches[0].pageY
1642 | )
1643 | }
1644 |
1645 | function mousemoveWindow (evt) {
1646 | if (isDragging) {
1647 | updateDraggedPiece(evt.pageX, evt.pageY)
1648 | }
1649 | }
1650 |
1651 | var throttledMousemoveWindow = throttle(mousemoveWindow, config.dragThrottleRate)
1652 |
1653 | function touchmoveWindow (evt) {
1654 | // do nothing if we are not dragging a piece
1655 | if (!isDragging) return
1656 |
1657 | // prevent screen from scrolling
1658 | evt.preventDefault()
1659 |
1660 | updateDraggedPiece(evt.originalEvent.changedTouches[0].pageX,
1661 | evt.originalEvent.changedTouches[0].pageY)
1662 | }
1663 |
1664 | var throttledTouchmoveWindow = throttle(touchmoveWindow, config.dragThrottleRate)
1665 |
1666 | function mouseupWindow (evt) {
1667 | // do nothing if we are not dragging a piece
1668 | if (!isDragging) return
1669 |
1670 | // get the location
1671 | var location = isXYOnSquare(evt.pageX, evt.pageY)
1672 |
1673 | stopDraggedPiece(location)
1674 | }
1675 |
1676 | function touchendWindow (evt) {
1677 | // do nothing if we are not dragging a piece
1678 | if (!isDragging) return
1679 |
1680 | // get the location
1681 | var location = isXYOnSquare(evt.originalEvent.changedTouches[0].pageX,
1682 | evt.originalEvent.changedTouches[0].pageY)
1683 |
1684 | stopDraggedPiece(location)
1685 | }
1686 |
1687 | function mouseenterSquare (evt) {
1688 | // do not fire this event if we are dragging a piece
1689 | // NOTE: this should never happen, but it's a safeguard
1690 | if (isDragging) return
1691 |
1692 | // exit if they did not provide a onMouseoverSquare function
1693 | if (!isFunction(config.onMouseoverSquare)) return
1694 |
1695 | // get the square
1696 | var square = $(evt.currentTarget).attr('data-square')
1697 |
1698 | // NOTE: this should never happen; defensive
1699 | if (!validSquare(square)) return
1700 |
1701 | // get the piece on this square
1702 | var piece = false
1703 | if (currentPosition.hasOwnProperty(square)) {
1704 | piece = currentPosition[square]
1705 | }
1706 |
1707 | // execute their function
1708 | config.onMouseoverSquare(square, piece, deepCopy(currentPosition), currentOrientation)
1709 | }
1710 |
1711 | function mouseleaveSquare (evt) {
1712 | // do not fire this event if we are dragging a piece
1713 | // NOTE: this should never happen, but it's a safeguard
1714 | if (isDragging) return
1715 |
1716 | // exit if they did not provide an onMouseoutSquare function
1717 | if (!isFunction(config.onMouseoutSquare)) return
1718 |
1719 | // get the square
1720 | var square = $(evt.currentTarget).attr('data-square')
1721 |
1722 | // NOTE: this should never happen; defensive
1723 | if (!validSquare(square)) return
1724 |
1725 | // get the piece on this square
1726 | var piece = false
1727 | if (currentPosition.hasOwnProperty(square)) {
1728 | piece = currentPosition[square]
1729 | }
1730 |
1731 | // execute their function
1732 | config.onMouseoutSquare(square, piece, deepCopy(currentPosition), currentOrientation)
1733 | }
1734 |
1735 | // -------------------------------------------------------------------------
1736 | // Initialization
1737 | // -------------------------------------------------------------------------
1738 |
1739 | function addEvents () {
1740 | // prevent "image drag"
1741 | $('body').on('mousedown mousemove', '.' + CSS.piece, stopDefault)
1742 |
1743 | // mouse drag pieces
1744 | $board.on('mousedown', '.' + CSS.square, mousedownSquare)
1745 | $container.on('mousedown', '.' + CSS.sparePieces + ' .' + CSS.piece, mousedownSparePiece)
1746 |
1747 | // mouse enter / leave square
1748 | $board
1749 | .on('mouseenter', '.' + CSS.square, mouseenterSquare)
1750 | .on('mouseleave', '.' + CSS.square, mouseleaveSquare)
1751 |
1752 | // piece drag
1753 | var $window = $(window)
1754 | $window
1755 | .on('mousemove', throttledMousemoveWindow)
1756 | .on('mouseup', mouseupWindow)
1757 |
1758 | // touch drag pieces
1759 | if (isTouchDevice()) {
1760 | $board.on('touchstart', '.' + CSS.square, touchstartSquare)
1761 | $container.on('touchstart', '.' + CSS.sparePieces + ' .' + CSS.piece, touchstartSparePiece)
1762 | $window
1763 | .on('touchmove', throttledTouchmoveWindow)
1764 | .on('touchend', touchendWindow)
1765 | }
1766 | }
1767 |
1768 | function initDOM () {
1769 | // create unique IDs for all the elements we will create
1770 | createElIds()
1771 |
1772 | // build board and save it in memory
1773 | $container.html(buildContainerHTML(config.sparePieces))
1774 | $board = $container.find('.' + CSS.board)
1775 |
1776 | if (config.sparePieces) {
1777 | $sparePiecesTop = $container.find('.' + CSS.sparePiecesTop)
1778 | $sparePiecesBottom = $container.find('.' + CSS.sparePiecesBottom)
1779 | }
1780 |
1781 | // create the drag piece
1782 | var draggedPieceId = uuid()
1783 | $('body').append(buildPieceHTML('wP', true, draggedPieceId))
1784 | $draggedPiece = $('#' + draggedPieceId)
1785 |
1786 | // TODO: need to remove this dragged piece element if the board is no
1787 | // longer in the DOM
1788 |
1789 | // get the border size
1790 | boardBorderSize = parseInt($board.css('borderLeftWidth'), 10)
1791 |
1792 | // set the size and draw the board
1793 | widget.resize()
1794 | }
1795 |
1796 | // -------------------------------------------------------------------------
1797 | // Initialization
1798 | // -------------------------------------------------------------------------
1799 |
1800 | setInitialState()
1801 | initDOM()
1802 | addEvents()
1803 |
1804 | // return the widget object
1805 | return widget
1806 | } // end constructor
1807 |
1808 | // TODO: do module exports here
1809 | window['Chessboard'] = constructor
1810 |
1811 | // support legacy ChessBoard name
1812 | window['ChessBoard'] = window['Chessboard']
1813 |
1814 | // expose util functions
1815 | window['Chessboard']['fenToObj'] = fenToObj
1816 | window['Chessboard']['objToFen'] = objToFen
1817 | })() // end anonymous wrapper
1818 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/bB.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/bK.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/bN.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/bP.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/bQ.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
28 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/bR.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
40 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/wB.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/wK.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/wN.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/wP.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/wQ.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/tampermonkey script/content/chesspieces/wR.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
--------------------------------------------------------------------------------
/tampermonkey script/content/stockfish-multi-thread/stockfish.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Stockfish copyright T. Romstad, M. Costalba, J. Kiiski, G. Linscott
3 | * and other contributors.
4 | *
5 | * Released under the GNU General Public License v3.
6 | *
7 | * Compiled to JavaScript and WebAssembly by Niklas Fiekas
8 | * using Emscripten.
9 | *
10 | * https://github.com/niklasf/stockfish.wasm
11 | */
12 |
13 | var Stockfish = (function() {
14 | var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
15 | if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
16 | return (
17 | function(Stockfish) {
18 | Stockfish = Stockfish || {};
19 |
20 |
21 | function d(){k.buffer!=l&&n(k.buffer);return ba}function t(){k.buffer!=l&&n(k.buffer);return ca}function v(){k.buffer!=l&&n(k.buffer);return da}function y(){k.buffer!=l&&n(k.buffer);return ea}function fa(){k.buffer!=l&&n(k.buffer);return ha}null;var z;z||(z=typeof Stockfish !== 'undefined' ? Stockfish : {});var ia,ja;z.ready=new Promise(function(a,b){ia=a;ja=b});
22 | (function(){function a(){var g=e.shift();if(!b&&void 0!==g){if("quit"===g)return z.terminate();var m=z.ccall("uci_command","number",["string"],[g]);m&&e.unshift(g);h=m?2*h:1;setTimeout(a,h)}}var b=!1,c=[];z.print=function(g){0===c.length?console.log(g):setTimeout(function(){for(var m=0;m=c);){var h=a[b++];if(!h)break;if(h&128){var g=a[b++]&63;if(192==(h&224))e+=String.fromCharCode((h&31)<<6|g);else{var m=a[b++]&63;h=224==(h&240)?(h&15)<<12|g<<6|m:(h&7)<<18|g<<12|m<<6|a[b++]&63;65536>h?e+=String.fromCharCode(h):(h-=65536,e+=String.fromCharCode(55296|h>>10,56320|h&1023))}}else e+=String.fromCharCode(h)}return e}function Q(a){return a?wa(t(),a,void 0):""}
30 | function xa(a,b,c,e){if(0=g){var m=a.charCodeAt(++h);g=65536+((g&1023)<<10)|m&1023}if(127>=g){if(c>=e)break;b[c++]=g}else{if(2047>=g){if(c+1>=e)break;b[c++]=192|g>>6}else{if(65535>=g){if(c+2>=e)break;b[c++]=224|g>>12}else{if(c+3>=e)break;b[c++]=240|g>>18;b[c++]=128|g>>12&63}b[c++]=128|g>>6&63}b[c++]=128|g&63}}b[c]=0}}
31 | function ya(a){for(var b=0,c=0;c=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++c)&1023);127>=e?++b:b=2047>=e?b+2:65535>=e?b+3:b+4}return b}function za(a){var b=ya(a)+1,c=R(b);xa(a,d(),c,b);return c}function Aa(a,b){d().set(a,b)}var l,ba,ca,da,ea,ha;
32 | function n(a){l=a;z.HEAP8=ba=new Int8Array(a);z.HEAP16=new Int16Array(a);z.HEAP32=da=new Int32Array(a);z.HEAPU8=ca=new Uint8Array(a);z.HEAPU16=new Uint16Array(a);z.HEAPU32=ea=new Uint32Array(a);z.HEAPF32=new Float32Array(a);z.HEAPF64=ha=new Float64Array(a)}var Ba=z.INITIAL_MEMORY||67108864;
33 | if(F)k=z.wasmMemory,l=z.buffer;else if(z.wasmMemory)k=z.wasmMemory;else if(k=new WebAssembly.Memory({initial:Ba/65536,maximum:32768,shared:!0}),!(k.buffer instanceof SharedArrayBuffer))throw N("requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag"),E&&console.log("(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)"),
34 | Error("bad memory");k&&(l=k.buffer);Ba=l.byteLength;n(l);var Ca,Da=[],Ea=[],Fa=[],Ga=[];F||Ea.push({Ia:function(){Ha()}});function Ia(){var a=z.preRun.shift();Da.unshift(a)}var S=0,Ja=null,T=null;z.preloadedImages={};z.preloadedAudios={};function M(a){if(z.onAbort)z.onAbort(a);F&&console.error("Pthread aborting at "+Error().stack);N(a);ua=!0;a=new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");ja(a);throw a;}
35 | function Ka(){var a=U;return String.prototype.startsWith?a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}var U="stockfish.wasm";Ka()||(U=oa(U));function La(){var a=U;try{if(a==U&&P)return new Uint8Array(P);if(I)return I(a);throw"both async and sync fetching of the wasm failed";}catch(b){M(b)}}
36 | function Ma(){return P||!na&&!D||"function"!==typeof fetch?Promise.resolve().then(function(){return La()}):fetch(U,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+U+"'";return a.arrayBuffer()}).catch(function(){return La()})}var Oa={11752:function(){throw"Canceled!";},12164:function(a,b){setTimeout(function(){Na(a,b)},0)}};
37 | function Pa(a){for(;0=a||a>d().length||a&1||0>b)return-28;if(0==b)return 0;2147483647<=b&&(b=Infinity);var c=Atomics.load(v(),V>>2),e=0;if(c==a&&Atomics.compareExchange(v(),V>>2,c,0)==c&&(--b,e=1,0>=b))return 1;a=Atomics.notify(v(),a>>2,b);if(0<=a)return a+e;throw"Atomics.notify returned an unexpected value "+a;}
38 | z._emscripten_futex_wake=Qa;function Ra(a){if(F)throw"Internal Error! cleanupThread() can only ever be called from main application thread!";if(!a)throw"Internal Error! Null pthread_ptr in cleanupThread!";v()[a+12>>2]=0;(a=A.ga[a])&&A.ta(a.worker)}
39 | var A={fa:[],ia:[],La:function(){for(var a=0;1>a;++a)A.za()},Ma:function(){for(var a=W(228),b=0;57>b;++b)y()[a/4+b]=0;v()[a+12>>2]=a;b=a+152;v()[b>>2]=b;var c=W(512);for(b=0;128>b;++b)y()[c/4+b]=0;Atomics.store(y(),a+100>>2,c);Atomics.store(y(),a+40>>2,a);Sa(a,!D,1);Ta(a)},Na:function(){A.receiveObjectTransfer=A.Pa;A.setThreadStatus=A.Ra;A.threadCancel=A.Ta;A.threadExit=A.Ua},ga:{},Fa:[],Ra:function(){},Da:function(){for(;0>2,a),Atomics.store(y(),b+0>>2,1),Atomics.store(y(),b+56>>2,1),Atomics.store(y(),b+60>>2,0),A.Da(),Qa(b+0,2147483647),Sa(0,0,0),F&&postMessage({cmd:"exit"}))},Ta:function(){A.Da();var a=X();Atomics.store(y(),a+4>>2,-1);Atomics.store(y(),a+0>>2,1);Qa(a+0,2147483647);Sa(0,0,0);postMessage({cmd:"cancelDone"})},Ea:function(){for(var a in A.ga){var b=A.ga[a];b&&b.worker&&A.ta(b.worker)}A.ga={};for(a=0;a>2];v()[a.ha+100>>2]=0;Va(b);Va(a.ha)}a.ha=0;a.xa&&a.ja&&Va(a.ja);a.ja=0;a.worker&&(a.worker.ea=null)}},ta:function(a){A.Qa(function(){delete A.ga[a.ea.ha];A.fa.push(a);A.ia.splice(A.ia.indexOf(a),1);A.ya(a.ea);a.ea=void 0})},Qa:function(a){v()[Wa>>2]=0;try{a()}finally{v()[Wa>>2]=1}},Pa:function(){},Ba:function(a,b){a.onmessage=function(c){var e=c.data,h=e.cmd;a.ea&&(A.Ga=a.ea.ha);if(e.targetThread&&e.targetThread!=X()){var g=
42 | A.ga[e.lb];g?g.worker.postMessage(c.data,e.transferList):console.error('Internal error! Worker sent a message "'+h+'" to target pthread '+e.targetThread+", but that thread no longer exists!")}else if("processQueuedMainThreadWork"===h)Xa();else if("spawnThread"===h)Ya(c.data);else if("cleanupThread"===h)Ra(e.thread);else if("killThread"===h){c=e.thread;if(F)throw"Internal Error! killThread() can only ever be called from main application thread!";if(!c)throw"Internal Error! Null pthread_ptr in killThread!";
43 | v()[c+12>>2]=0;c=A.ga[c];c.worker.terminate();A.ya(c);A.ia.splice(A.ia.indexOf(c.worker),1);c.worker.ea=void 0}else if("cancelThread"===h){c=e.thread;if(F)throw"Internal Error! cancelThread() can only ever be called from main application thread!";if(!c)throw"Internal Error! Null pthread_ptr in cancelThread!";A.ga[c].worker.postMessage({cmd:"cancel"})}else if("loaded"===h)a.loaded=!0,b&&b(a),a.na&&(a.na(),delete a.na);else if("print"===h)qa("Thread "+e.threadId+": "+e.text);else if("printErr"===h)N("Thread "+
44 | e.threadId+": "+e.text);else if("alert"===h)alert("Thread "+e.threadId+": "+e.text);else if("exit"===h)a.ea&&Atomics.load(y(),a.ea.ha+64>>2)&&A.ta(a);else if("exitProcess"===h)try{Za(e.returnCode)}catch(m){if(m instanceof L)return;throw m;}else"cancelDone"===h?A.ta(a):"objectTransfer"!==h&&("setimmediate"===c.data.target?a.postMessage(c.data):N("worker sent an unknown command "+h));A.Ga=void 0};a.onerror=function(c){N("pthread sent an error! "+c.filename+":"+c.lineno+": "+c.message)};E&&(a.on("message",
45 | function(c){a.onmessage({data:c})}),a.on("error",function(c){a.onerror(c)}),a.on("exit",function(){}));a.postMessage({cmd:"load",urlOrBlob:z.mainScriptUrlOrBlob||_scriptDir,wasmMemory:k,wasmModule:ta})},za:function(){var a=oa("stockfish.worker.js");A.fa.push(new Worker(a))},Ja:function(){0==A.fa.length&&(A.za(),A.Ba(A.fa[0]));return 0=a||a>d().length||a&1)return-28;if(na){if(Atomics.load(v(),a>>2)!=b)return-6;var e=performance.now();c=e+c;for(Atomics.exchange(v(),V>>2,a);;){e=performance.now();if(e>c)return Atomics.exchange(v(),V>>2,0),-73;e=Atomics.exchange(v(),V>>2,0);if(0==e)break;Xa();if(Atomics.load(v(),a>>2)!=b)return-6;Atomics.exchange(v(),V>>2,a)}return 0}a=Atomics.wait(v(),a>>2,b,c);if("timed-out"===a)return-73;if("not-equal"===a)return-6;if("ok"===a)return 0;throw"Atomics.wait returned an unexpected value "+
49 | a;}function Z(a,b){for(var c=arguments.length-2,e=ib(),h=R(8*c),g=h>>3,m=0;m>2]=b,v()[e.ra+4>>2]=c);if(e.Ca||!e.bb)e.Ca&&(e=e.Ca),a=!1,e.qa&&e.qa.pa&&(a=e.qa.pa.getParameter(2978),a=0===a[0]&&0===a[1]&&a[2]===e.width&&a[3]===e.height),e.width=b,e.height=c,a&&e.qa.pa.viewport(0,0,b,c);else{if(e.ra){e=v()[e.ra+8>>2];a=a?Q(a):"";var h=ib(),g=R(12),m=0;if(a){m=ya(a)+1;var p=W(m);xa(a,t(),p,m);m=p}v()[g>>2]=m;v()[g+4>>2]=b;v()[g+8>>2]=c;pb(0,e,657457152,0,m,g);Y(h);return 1}return-4}return 0}
51 | function qb(a,b,c){return F?Z(5,1,a,b,c):ob(a,b,c)}function rb(a){var b=a.getExtension("ANGLE_instanced_arrays");b&&(a.vertexAttribDivisor=function(c,e){b.vertexAttribDivisorANGLE(c,e)},a.drawArraysInstanced=function(c,e,h,g){b.drawArraysInstancedANGLE(c,e,h,g)},a.drawElementsInstanced=function(c,e,h,g,m){b.drawElementsInstancedANGLE(c,e,h,g,m)})}
52 | function sb(a){var b=a.getExtension("OES_vertex_array_object");b&&(a.createVertexArray=function(){return b.createVertexArrayOES()},a.deleteVertexArray=function(c){b.deleteVertexArrayOES(c)},a.bindVertexArray=function(c){b.bindVertexArrayOES(c)},a.isVertexArray=function(c){return b.isVertexArrayOES(c)})}function tb(a){var b=a.getExtension("WEBGL_draw_buffers");b&&(a.drawBuffers=function(c,e){b.drawBuffersWEBGL(c,e)})}
53 | function ub(a){a||(a=vb);if(!a.Ka){a.Ka=!0;var b=a.pa;rb(b);sb(b);tb(b);b.cb=b.getExtension("EXT_disjoint_timer_query");b.ib=b.getExtension("WEBGL_multi_draw");(b.getSupportedExtensions()||[]).forEach(function(c){0>c.indexOf("lose_context")&&0>c.indexOf("debug")&&b.getExtension(c)})}}var vb,wb=["default","low-power","high-performance"],xb={};
54 | function yb(){if(!zb){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"===typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:la||"./this.program"},b;for(b in xb)a[b]=xb[b];var c=[];for(b in a)c.push(b+"="+a[b]);zb=c}return zb}var zb;
55 | function Ab(a,b){if(F)return Z(6,1,a,b);var c=0;yb().forEach(function(e,h){var g=b+c;h=v()[a+4*h>>2]=g;for(g=0;g>0]=e.charCodeAt(g);d()[h>>0]=0;c+=e.length+1});return 0}function Bb(a,b){if(F)return Z(7,1,a,b);var c=yb();v()[a>>2]=c.length;var e=0;c.forEach(function(h){e+=h.length+1});v()[b>>2]=e;return 0}function Cb(a){return F?Z(8,1,a):0}function Eb(a,b,c,e){if(F)return Z(9,1,a,b,c,e);a=cb.fb(a);b=cb.eb(a,b,c);v()[e>>2]=b;return 0}
56 | function Fb(a,b,c,e,h){if(F)return Z(10,1,a,b,c,e,h)}function Gb(a,b,c,e){if(F)return Z(11,1,a,b,c,e);for(var h=0,g=0;g>2],p=v()[b+(8*g+4)>>2],u=0;u>2]=h;return 0}
57 | function Ya(a){if(F)throw"Internal Error! spawnThread() can only ever be called from main application thread!";var b=A.Ja();if(void 0!==b.ea)throw"Internal error!";if(!a.sa)throw"Internal error, no pthread ptr!";A.ia.push(b);for(var c=W(512),e=0;128>e;++e)v()[c+4*e>>2]=0;var h=a.ja+a.ka;e=A.ga[a.sa]={worker:b,ja:a.ja,ka:a.ka,xa:a.xa,ha:a.sa};var g=e.ha>>2;Atomics.store(y(),g+16,a.detached);Atomics.store(y(),g+25,c);Atomics.store(y(),g+10,e.ha);Atomics.store(y(),g+20,a.ka);Atomics.store(y(),g+19,h);
58 | Atomics.store(y(),g+26,a.ka);Atomics.store(y(),g+28,h);Atomics.store(y(),g+29,a.detached);c=Hb()+40;Atomics.store(y(),g+43,c);b.ea=e;var m={cmd:"run",start_routine:a.Sa,arg:a.ma,threadInfoStruct:a.sa,stackBase:a.ja,stackSize:a.ka};b.na=function(){m.time=performance.now();b.postMessage(m,a.Za)};b.loaded&&(b.na(),delete b.na)}
59 | function Ib(a,b){if(!a)return N("pthread_join attempted on a null thread pointer!"),71;if(F&&X()==a)return N("PThread "+a+" is attempting to join to itself!"),16;if(!F&&Jb()==a)return N("Main thread "+a+" is attempting to join to itself!"),16;if(v()[a+12>>2]!==a)return N("pthread_join attempted on thread "+a+", which does not point to a valid thread, or does not exist anymore!"),71;if(Atomics.load(y(),a+64>>2))return N("Attempted to join thread "+a+", which was already detached!"),28;for(gb();;){var c=
60 | Atomics.load(y(),a+0>>2);if(1==c)return c=Atomics.load(y(),a+4>>2),b&&(v()[b>>2]=c),Atomics.store(y(),a+64>>2,1),F?postMessage({cmd:"cleanupThread",thread:a}):Ra(a),0;if(F){var e=X();if(e&&!Atomics.load(y(),e+56>>2)&&2==Atomics.load(y(),e+0>>2))throw"Canceled!";}F||Xa();hb(a+0,c,F?100:1)}}function Kb(a){return 0===a%4&&(0!==a%100||0===a%400)}function Lb(a,b){for(var c=0,e=0;e<=b;c+=a[e++]);return c}var Mb=[31,29,31,30,31,30,31,31,30,31,30,31],Nb=[31,28,31,30,31,30,31,31,30,31,30,31];
61 | function Ob(a,b){for(a=new Date(a.getTime());0e-a.getDate())b-=e-a.getDate()+1,a.setDate(1),11>c?a.setMonth(c+1):(a.setMonth(0),a.setFullYear(a.getFullYear()+1));else{a.setDate(a.getDate()+b);break}}return a}
62 | function Pb(a,b,c,e){function h(f,q,x){for(f="number"===typeof f?f.toString():f||"";f.lengthDb?-1:0=m(x,f)?0>=m(q,f)?f.getFullYear()+1:f.getFullYear():f.getFullYear()-1}var r=v()[e+40>>2];e={Xa:v()[e>>2],Wa:v()[e+4>>2],ua:v()[e+8>>2],oa:v()[e+12>>2],la:v()[e+16>>2],da:v()[e+20>>2],va:v()[e+24>>2],wa:v()[e+28>>2],mb:v()[e+
64 | 32>>2],Va:v()[e+36>>2],Ya:r?Q(r):""};c=Q(c);r={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var w in r)c=c.replace(new RegExp(w,"g"),r[w]);var aa="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),
65 | sa="January February March April May June July August September October November December".split(" ");r={"%a":function(f){return aa[f.va].substring(0,3)},"%A":function(f){return aa[f.va]},"%b":function(f){return sa[f.la].substring(0,3)},"%B":function(f){return sa[f.la]},"%C":function(f){return g((f.da+1900)/100|0,2)},"%d":function(f){return g(f.oa,2)},"%e":function(f){return h(f.oa,2," ")},"%g":function(f){return u(f).toString().substring(2)},"%G":function(f){return u(f)},"%H":function(f){return g(f.ua,
66 | 2)},"%I":function(f){f=f.ua;0==f?f=12:12f.ua?"AM":"PM"},"%S":function(f){return g(f.Xa,2)},"%t":function(){return"\t"},"%u":function(f){return f.va||7},"%U":function(f){var q=new Date(f.da+1900,0,1),x=0===q.getDay()?q:Ob(q,7-q.getDay());f=new Date(f.da+1900,f.la,f.oa);return 0>
67 | m(x,f)?g(Math.ceil((31-x.getDate()+(Lb(Kb(f.getFullYear())?Mb:Nb,f.getMonth()-1)-31)+f.getDate())/7),2):0===m(x,q)?"01":"00"},"%V":function(f){var q=new Date(f.da+1901,0,4),x=p(new Date(f.da+1900,0,4));q=p(q);var O=Ob(new Date(f.da+1900,0,1),f.wa);return 0>m(O,x)?"53":0>=m(q,O)?"01":g(Math.ceil((x.getFullYear()m(x,f)?g(Math.ceil((31-x.getDate()+(Lb(Kb(f.getFullYear())?Mb:Nb,f.getMonth()-1)-31)+f.getDate())/7),2):0===m(x,q)?"01":"00"},"%y":function(f){return(f.da+1900).toString().substring(2)},"%Y":function(f){return f.da+1900},"%z":function(f){f=f.Va;var q=0<=f;f=Math.abs(f)/60;return(q?"+":"-")+String("0000"+(f/60*100+f%60)).slice(-4)},"%Z":function(f){return f.Ya},"%%":function(){return"%"}};for(w in r)0<=c.indexOf(w)&&(c=c.replace(new RegExp(w,"g"),r[w](e)));w=
69 | Qb(c);if(w.length>b)return 0;Aa(w,a);return w.length-1}F||A.La();var Rb=[null,function(a,b){if(F)return Z(1,1,a,b)},db,eb,fb,qb,Ab,Bb,Cb,Eb,Fb,Gb];function Qb(a){var b=Array(ya(a)+1);xa(a,b,0,b.length);return b}
70 | var Vb={c:function(a,b,c,e){M("Assertion failed: "+Q(a)+", at: "+[b?Q(b):"unknown filename",c,e?Q(e):"unknown function"])},i:db,q:eb,r:fb,E:function(a,b){if(a==b)postMessage({cmd:"processQueuedMainThreadWork"});else if(F)postMessage({targetThread:a,cmd:"processThreadQueue"});else{a=(a=A.ga[a])&&a.worker;if(!a)return;a.postMessage({cmd:"processThreadQueue"})}return 1},b:function(){M()},v:function(a,b){if(0===a)a=Date.now();else if(1===a||4===a)a=ab();else return v()[Sb()>>2]=28,-1;v()[b>>2]=a/1E3|
71 | 0;v()[b+4>>2]=a%1E3*1E6|0;return 0},n:function(a,b,c){lb.length=0;var e;for(c>>=2;e=t()[b++];)(e=105>e)&&c&1&&c++,lb.push(e?fa()[c++>>1]:v()[c]),++c;return Oa[a].apply(null,lb)},z:gb,m:function(){},f:hb,e:Qa,g:ab,u:function(a,b,c){t().copyWithin(a,b,b+c)},A:function(a,b,c){kb.length=b;c>>=3;for(var e=0;ea?Oa[-a-1]:Rb[a]).apply(null,kb)},d:function(a){a>>>=0;var b=t().length;if(a<=b||2147483648=c;c*=2){var e=b*(1+.2/c);e=Math.min(e,a+100663296);
72 | e=Math.max(16777216,a,e);0>>16);n(k.buffer);var h=1;break a}catch(g){}h=void 0}if(h)return!0}return!1},C:function(a,b,c){return nb(a)?ob(a,b,c):qb(a,b,c)},l:function(){},D:function(a,b){b>>=2;var c=v()[b+6];b={alpha:!!v()[b],depth:!!v()[b+1],stencil:!!v()[b+2],antialias:!!v()[b+3],premultipliedAlpha:!!v()[b+4],preserveDrawingBuffer:!!v()[b+5],powerPreference:wb[c],failIfMajorPerformanceCaveat:!!v()[b+7],Oa:v()[b+
73 | 8],hb:v()[b+9],Aa:v()[b+10],Ha:v()[b+11],jb:v()[b+12],kb:v()[b+13]};a=nb(a);if(!a||b.Ha)b=0;else if(a=a.getContext("webgl",b)){c=W(8);v()[c+4>>2]=X();var e={gb:c,attributes:b,version:b.Oa,pa:a};a.canvas&&(a.canvas.qa=e);("undefined"===typeof b.Aa||b.Aa)&&ub(e);b=c}else b=0;return b},x:Ab,y:Bb,h:function(a){Za(a)},j:Cb,p:Eb,s:Fb,o:Gb,t:function(){A.Ma()},a:k||z.wasmMemory,k:function(a,b,c,e){if("undefined"===typeof SharedArrayBuffer)return N("Current environment does not support SharedArrayBuffer, pthreads are not available!"),
74 | 6;if(!a)return N("pthread_create called with a null thread pointer!"),28;var h=[];if(F&&0===h.length)return Tb(687865856,a,b,c,e);var g=0,m=0;if(b&&-1!=b){var p=v()[b>>2];p+=81920;g=v()[b+8>>2];m=0!==v()[b+12>>2]}else p=2097152;(b=0==g)?g=Ub(16,p):(g-=p,assert(0r;++r)y()[(u>>2)+r]=0;v()[a>>2]=u;v()[u+12>>2]=u;a=u+152;v()[a>>2]=a;c={ja:g,ka:p,xa:b,detached:m,Sa:c,sa:u,ma:e,Za:h};F?(c.ab="spawnThread",postMessage(c,h)):Ya(c);return 0},B:function(a,b){return Ib(a,b)},w:function(a,
75 | b,c,e){return Pb(a,b,c,e)}};
76 | (function(){function a(h,g){z.asm=h.exports;Ca=z.asm.ca;ta=g;if(!F){var m=A.fa.length;A.fa.forEach(function(p){A.Ba(p,function(){if(!--m&&(S--,z.monitorRunDependencies&&z.monitorRunDependencies(S),0==S&&(null!==Ja&&(clearInterval(Ja),Ja=null),T))){var u=T;T=null;u()}})})}}function b(h){a(h.instance,h.module)}function c(h){return Ma().then(function(g){return WebAssembly.instantiate(g,e)}).then(h,function(g){N("failed to asynchronously prepare wasm: "+g);M(g)})}var e={a:Vb};F||(assert(!F,"addRunDependency cannot be used in a pthread worker"),
77 | S++,z.monitorRunDependencies&&z.monitorRunDependencies(S));if(z.instantiateWasm)try{return z.instantiateWasm(e,a)}catch(h){return N("Module.instantiateWasm callback failed with error: "+h),!1}(function(){return P||"function"!==typeof WebAssembly.instantiateStreaming||Ka()||"function"!==typeof fetch?c(b):fetch(U,{credentials:"same-origin"}).then(function(h){return WebAssembly.instantiateStreaming(h,e).then(b,function(g){N("wasm streaming compile failed: "+g);N("falling back to ArrayBuffer instantiation");
78 | return c(b)})})})().catch(ja);return{}})();var Ha=z.___wasm_call_ctors=function(){return(Ha=z.___wasm_call_ctors=z.asm.F).apply(null,arguments)};z._main=function(){return(z._main=z.asm.G).apply(null,arguments)};var W=z._malloc=function(){return(W=z._malloc=z.asm.H).apply(null,arguments)},Va=z._free=function(){return(Va=z._free=z.asm.I).apply(null,arguments)};z._uci_command=function(){return(z._uci_command=z.asm.J).apply(null,arguments)};
79 | var Hb=z._emscripten_get_global_libc=function(){return(Hb=z._emscripten_get_global_libc=z.asm.K).apply(null,arguments)},Sb=z.___errno_location=function(){return(Sb=z.___errno_location=z.asm.L).apply(null,arguments)};z.___em_js__initPthreadsJS=function(){return(z.___em_js__initPthreadsJS=z.asm.M).apply(null,arguments)};
80 | var X=z._pthread_self=function(){return(X=z._pthread_self=z.asm.N).apply(null,arguments)},Ua=z.___pthread_tsd_run_dtors=function(){return(Ua=z.___pthread_tsd_run_dtors=z.asm.O).apply(null,arguments)};z._emscripten_current_thread_process_queued_calls=function(){return(z._emscripten_current_thread_process_queued_calls=z.asm.P).apply(null,arguments)};
81 | var Ta=z._emscripten_register_main_browser_thread_id=function(){return(Ta=z._emscripten_register_main_browser_thread_id=z.asm.Q).apply(null,arguments)},Jb=z._emscripten_main_browser_thread_id=function(){return(Jb=z._emscripten_main_browser_thread_id=z.asm.R).apply(null,arguments)},Na=z.__emscripten_do_dispatch_to_thread=function(){return(Na=z.__emscripten_do_dispatch_to_thread=z.asm.S).apply(null,arguments)},Tb=z._emscripten_sync_run_in_main_thread_4=function(){return(Tb=z._emscripten_sync_run_in_main_thread_4=
82 | z.asm.T).apply(null,arguments)},Xa=z._emscripten_main_thread_process_queued_calls=function(){return(Xa=z._emscripten_main_thread_process_queued_calls=z.asm.U).apply(null,arguments)},jb=z._emscripten_run_in_main_runtime_thread_js=function(){return(jb=z._emscripten_run_in_main_runtime_thread_js=z.asm.V).apply(null,arguments)},pb=z.__emscripten_call_on_thread=function(){return(pb=z.__emscripten_call_on_thread=z.asm.W).apply(null,arguments)};
83 | z._emscripten_tls_init=function(){return(z._emscripten_tls_init=z.asm.X).apply(null,arguments)};
84 | var Sa=z.__emscripten_thread_init=function(){return(Sa=z.__emscripten_thread_init=z.asm.Y).apply(null,arguments)},ib=z.stackSave=function(){return(ib=z.stackSave=z.asm.Z).apply(null,arguments)},Y=z.stackRestore=function(){return(Y=z.stackRestore=z.asm._).apply(null,arguments)},R=z.stackAlloc=function(){return(R=z.stackAlloc=z.asm.$).apply(null,arguments)},$a=z._emscripten_stack_set_limits=function(){return($a=z._emscripten_stack_set_limits=z.asm.aa).apply(null,arguments)},Ub=z._memalign=function(){return(Ub=
85 | z._memalign=z.asm.ba).apply(null,arguments)},Wa=z.__emscripten_allow_main_runtime_queued_calls=25580,V=z.__emscripten_main_thread_futex=1088592;
86 | z.ccall=function(a,b,c,e){var h={string:function(r){var w=0;if(null!==r&&void 0!==r&&0!==r){var aa=(r.length<<2)+1,sa=w=R(aa);xa(r,t(),sa,aa)}return w},array:function(r){var w=R(r.length);Aa(r,w);return w}},g=va(a),m=[];a=0;if(e)for(var p=0;p>2]=za(la);for(var m=1;m>2)+m]=za(c[m-1]);v()[(g>>2)+h]=0;try{var p=e(h,g);Za(p,!0)}catch(u){u instanceof L||("unwind"==u?noExitRuntime=!0:((c=u)&&"object"===typeof u&&u.stack&&(c=[u,u.stack]),N("exception thrown: "+c),ma(1,u)))}finally{}}if(!F){if(z.postRun)for("function"==typeof z.postRun&&
89 | (z.postRun=[z.postRun]);z.postRun.length;)c=z.postRun.shift(),Ga.unshift(c);Pa(Ga)}}}a=a||ka;if(!(0 {
8 | return new Promise((resolve, reject) => {
9 | const engines_path = path.resolve(process.cwd(), 'utils/engine');
10 | const engine_path = path.resolve(engines_path, engine_name);
11 |
12 | if (!fs.existsSync(engine_path)) {
13 | reject("Engine not found: " + engine_name);
14 | }
15 |
16 | console.log("Using engine: " + engine_name);
17 |
18 | const engine = spawn(engine_path, {
19 | shell: true,
20 | cwd: engines_path
21 | });
22 |
23 | engine.stdin.write(`${command}\n`);
24 | engine.stdin.write(`${engineCmd}\n`);
25 |
26 |
27 |
28 |
29 | engine.stdout.on('data', (chunk) => {
30 | const result = chunk.toString();
31 | if (result.includes('bestmove')) {
32 | engine.kill();
33 |
34 | const depth = result.match(/info\sdepth\s\d+/);
35 | const seldepth = result.match(/seldepth\s\d+/);
36 | const bestmove = result.match(/bestmove\s\w+/);
37 | const ponder = result.match(/ponder\s\w+/);
38 |
39 | resolve({
40 | depth: depth ? Number(depth[0].match(/\d+/)[0]) : null,
41 | seldepth: seldepth ? Number(seldepth[0].match(/\d+/)[0]) : null,
42 | bestmove: bestmove ? bestmove[0].replace('bestmove ', '') : '',
43 | possible_human_move: ponder ? ponder[0].replace('ponder ', '') : '',
44 | });
45 | }
46 | });
47 |
48 | engine.on('error', (err) => {
49 | reject(err);
50 | });
51 |
52 | engine.stderr.on('data', (data) => {
53 | reject(data);
54 | });
55 | });
56 | }
57 |
58 | module.exports = {
59 | executeEngine,
60 | }
61 |
--------------------------------------------------------------------------------