262 |
263 |
264 |
270 |
271 |
272 |
273 |
274 | `;
275 | }
276 | }
277 |
278 | // Function to generate nonce
279 | function getNonce() {
280 | let text = "";
281 | const possible =
282 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
283 | for (let i = 0; i < 32; i++) {
284 | text += possible.charAt(Math.floor(Math.random() * possible.length));
285 | }
286 | return text;
287 | }
288 |
--------------------------------------------------------------------------------
/webview/style/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --container-paddding: 20px;
3 | --input-padding-vertical: 6px;
4 | --input-padding-horizontal: 4px;
5 | --input-margin-vertical: 4px;
6 | --input-margin-horizontal: 0;
7 | }
8 | body {
9 | padding: 0 var(--container-paddding);
10 | color: var(--vscode-editor-foreground);
11 | font-size: var(--vscode-font-size);
12 | font-weight: var(--vscode-font-weight);
13 | background-color: var(--vscode-editor-background);
14 | font-family: var(--vscode-editor-font-family);
15 | }
16 | svg {
17 | width: calc(var(--vscode-font-size) * 1.75);
18 | stroke: var(--vscode-foreground);
19 | }
20 | ol,
21 | ul {
22 | padding-left: var(--container-paddding);
23 | }
24 | body > *,
25 | form > * {
26 | -webkit-margin-before: var(--input-margin-vertical);
27 | margin-block-start: var(--input-margin-vertical);
28 | -webkit-margin-after: var(--input-margin-vertical);
29 | margin-block-end: var(--input-margin-vertical);
30 | }
31 | *:focus {
32 | outline-color: var(--vscode-foreground) !important;
33 | }
34 | a {
35 | color: var(--vscode-textLink-foreground);
36 | }
37 | a:hover,
38 | a:active {
39 | color: var(--vscode-textLink-activeForeground);
40 | }
41 | code {
42 | font-size: var(--vscode-editor-font-size);
43 | font-family: var(--vscode-editor-font-family);
44 | }
45 | button {
46 | border: none;
47 | padding: var(--input-padding-vertical) var(--input-padding-horizontal);
48 | text-align: center;
49 | outline: 1px solid transparent;
50 | color: var(--vscode-button-foreground);
51 | background: var(--vscode-button-background);
52 | }
53 | button:hover {
54 | background: var(--vscode-button-hoverBackground);
55 | }
56 | button:focus {
57 | outline-color: var(--vscode-foreground);
58 | }
59 | button.secondary {
60 | color: var(--vscode-button-secondaryForeground);
61 | background: var(--vscode-button-secondaryBackground);
62 | }
63 | button.secondary:hover {
64 | background: var(--vscode-button-secondaryHoverBackground);
65 | }
66 | input:not([type="checkbox"]),
67 | textarea {
68 | display: block;
69 | width: 100%;
70 | border: none;
71 | font-family: var(--vscode-font-family);
72 | padding: var(--input-padding-vertical) var(--input-padding-horizontal);
73 | color: var(--vscode-input-foreground);
74 | outline-color: var(--vscode-foreground);
75 | background-color: var(--vscode-input-background);
76 | }
77 | input::-webkit-input-placeholder,
78 | textarea::-webkit-input-placeholder {
79 | color: var(--vscode-input-placeholderForeground);
80 | }
81 | input:-ms-input-placeholder,
82 | textarea:-ms-input-placeholder {
83 | color: var(--vscode-input-placeholderForeground);
84 | }
85 | input::-ms-input-placeholder,
86 | textarea::-ms-input-placeholder {
87 | color: var(--vscode-input-placeholderForeground);
88 | }
89 | input::placeholder,
90 | textarea::placeholder {
91 | color: var(--vscode-input-placeholderForeground);
92 | }
93 | :root {
94 | --editorBackgroundColor: var(--vscode-editor-background);
95 | --boxBackgroundColor: white;
96 | --charSize: var(--vscode-editor-font-size);
97 | }
98 | body {
99 | margin: 0;
100 | height: 100vh;
101 | min-height: 100vh;
102 | display: -webkit-box;
103 | display: -ms-flexbox;
104 | display: flex;
105 | -webkit-box-orient: vertical;
106 | -webkit-box-direction: normal;
107 | -ms-flex-direction: column;
108 | flex-direction: column;
109 | -webkit-box-align: center;
110 | -ms-flex-align: center;
111 | align-items: center;
112 | -webkit-box-pack: justify;
113 | -ms-flex-pack: justify;
114 | justify-content: space-between;
115 | }
116 | *[onclick] {
117 | cursor: pointer;
118 | }
119 | .hidden {
120 | display: none !important;
121 | }
122 | #top {
123 | display: -webkit-box;
124 | display: -ms-flexbox;
125 | display: flex;
126 | -webkit-box-orient: vertical;
127 | -webkit-box-direction: normal;
128 | -ms-flex-direction: column;
129 | flex-direction: column;
130 | -webkit-box-pack: center;
131 | -ms-flex-pack: center;
132 | justify-content: center;
133 | -webkit-box-align: center;
134 | -ms-flex-align: center;
135 | align-items: center;
136 | }
137 | #header {
138 | display: -webkit-box;
139 | display: -ms-flexbox;
140 | display: flex;
141 | -webkit-box-pack: center;
142 | -ms-flex-pack: center;
143 | justify-content: center;
144 | -webkit-box-align: center;
145 | -ms-flex-align: center;
146 | align-items: center;
147 | margin: 0;
148 | margin: 1.2rem 0 0 0;
149 | line-height: 2rem;
150 | }
151 | #command-center {
152 | margin: 0 0.4rem;
153 | display: -webkit-box;
154 | display: -ms-flexbox;
155 | display: flex;
156 | -webkit-box-orient: vertical;
157 | -webkit-box-direction: normal;
158 | -ms-flex-direction: column;
159 | flex-direction: column;
160 | -webkit-box-pack: center;
161 | -ms-flex-pack: center;
162 | justify-content: center;
163 | -webkit-transform: translateY(-2vh);
164 | transform: translateY(-2vh);
165 | max-height: calc(70vh - 4rem);
166 | }
167 | .bar {
168 | display: -webkit-box;
169 | display: -ms-flexbox;
170 | display: flex;
171 | -webkit-box-pack: justify;
172 | -ms-flex-pack: justify;
173 | justify-content: space-between;
174 | }
175 | #left-wing {
176 | color: inherit;
177 | }
178 | #word-count {
179 | color: inherit;
180 | }
181 | #word-count span {
182 | cursor: pointer;
183 | }
184 | #time-count {
185 | cursor: pointer;
186 | }
187 | #time-count span {
188 | cursor: pointer;
189 | }
190 | #language-selected {
191 | color: inherit;
192 | }
193 | #right-wing {
194 | color: inherit;
195 | }
196 | #typing-area {
197 | margin-top: 1rem;
198 | padding: 1.4rem 1rem;
199 | border-radius: 0.4rem;
200 | width: 80vw;
201 | max-width: 45rem;
202 | }
203 | #text-display {
204 | margin-bottom: 1rem;
205 | overflow: hidden;
206 | }
207 | #text-display > span {
208 | display: inline-block;
209 | margin: calc(var(--vscode-editor-font-size) * 0.3)
210 | calc(var(--vscode-editor-font-size) * 0.3);
211 | }
212 | #text-display span {
213 | font-size: var(--vscode-editor-font-size);
214 | }
215 | #input-field {
216 | width: 100%;
217 | border: none;
218 | font: inherit;
219 | padding: 0.4rem 1rem;
220 | border-radius: 0.2rem;
221 | font-size: 1.2rem;
222 | background: var(--vscode-editor-background);
223 | color: inherit;
224 | }
225 | #restart-button {
226 | margin-left: 0.7rem;
227 | border: none;
228 | font: inherit;
229 | padding: 0.4rem 1rem;
230 | border-radius: 0.2rem;
231 | font-size: 1.2rem;
232 | cursor: pointer;
233 | background: var(--vscode-editor-background);
234 | color: inherit;
235 | }
236 | #coding-area {
237 | margin-top: 1rem;
238 | padding: 1rem 1rem;
239 | border-radius: 0.4rem;
240 | outline: none;
241 | width: 75vw;
242 | max-width: 55rem;
243 | max-height: 60vh;
244 | overflow: scroll;
245 | min-height: 4rem;
246 | position: relative;
247 | }
248 | #coding-area:after {
249 | content: "";
250 | width: 98%;
251 | height: 102%;
252 | position: fixed;
253 | left: 0;
254 | top: -0.9rem;
255 | background: -webkit-gradient(
256 | linear,
257 | left top,
258 | left bottom,
259 | color-stop(88%, transparent),
260 | to(var(--boxBackgroundColor))
261 | );
262 | background: linear-gradient(transparent 88%, var(--boxBackgroundColor) 100%);
263 | }
264 | #code-display {
265 | overflow: hidden;
266 | }
267 | .code {
268 | position: relative;
269 | }
270 | #code-pre {
271 | border: none;
272 | outline: none;
273 | margin: 0;
274 | padding: 0;
275 | overflow: visible;
276 | }
277 | #code-code {
278 | display: inline;
279 | width: -webkit-fit-content;
280 | width: -moz-fit-content;
281 | width: fit-content;
282 | font-family: var(--vscode-editor-font-family);
283 | border: none;
284 | outline: none;
285 | margin: 0;
286 | padding: 0;
287 | }
288 | .char.topass {
289 | opacity: 0.57;
290 | }
291 | .charDiv {
292 | display: inline;
293 | overflow: hidden;
294 | }
295 | .char {
296 | color: inherit;
297 | }
298 | #cursor {
299 | display: inline-block;
300 | position: absolute;
301 | height: calc(var(--vscode-editor-font-size) * 1.25);
302 | width: calc(var(--vscode-editor-font-size) * 0.15);
303 | opacity: 1;
304 | top: 0;
305 | -webkit-animation: cursor 0.85s infinite ease-in;
306 | animation: cursor 0.85s infinite ease-in;
307 | }
308 | @-webkit-keyframes cursor {
309 | 0% {
310 | opacity: 0.6;
311 | }
312 | 100% {
313 | opacity: 0;
314 | }
315 | }
316 | @keyframes cursor {
317 | 0% {
318 | opacity: 0.6;
319 | }
320 | 100% {
321 | opacity: 0;
322 | }
323 | }
324 | .codeButton {
325 | position: fixed;
326 | right: 1.5rem;
327 | bottom: 1.5rem;
328 | z-index: 999;
329 | opacity: 70%;
330 | }
331 | .codeButton:hover {
332 | opacity: 100%;
333 | }
334 | #charDimensions {
335 | height: --vscode-editor-font-size;
336 | width: --vscode-editor-font-size;
337 | }
338 | #editorBackgroundColor {
339 | background: var(--vscode-editor-background);
340 | }
341 | code[class*="language-"],
342 | pre[class*="language-"] {
343 | background: none;
344 | text-align: left;
345 | white-space: pre;
346 | word-spacing: normal;
347 | word-break: normal;
348 | word-wrap: normal;
349 | -moz-tab-size: 4;
350 | -o-tab-size: 4;
351 | tab-size: 4;
352 | -webkit-hyphens: none;
353 | -ms-hyphens: none;
354 | hyphens: none;
355 | }
356 | @media print {
357 | code[class*="language-"],
358 | pre[class*="language-"] {
359 | text-shadow: none;
360 | }
361 | }
362 | pre[class*="language-"] {
363 | padding: 1em;
364 | margin: 0.5em 0;
365 | overflow: auto;
366 | }
367 | :not(pre) > code[class*="language-"] {
368 | padding: 0.1em;
369 | border-radius: 0.3em;
370 | white-space: normal;
371 | }
372 | .token.important,
373 | .token.bold {
374 | font-weight: bold;
375 | }
376 | .token.italic {
377 | font-style: italic;
378 | }
379 | .token.entity {
380 | cursor: help;
381 | }
382 | pre.line-numbers {
383 | position: relative;
384 | padding-left: 3.8em;
385 | counter-reset: linenumber;
386 | }
387 | pre.line-numbers > code {
388 | position: relative;
389 | }
390 | .line-numbers .line-numbers-rows {
391 | position: absolute;
392 | pointer-events: none;
393 | top: 0;
394 | font-size: 100%;
395 | left: -3.8em;
396 | width: 3em;
397 | letter-spacing: -1px;
398 | border-right: 0;
399 | -webkit-user-select: none;
400 | -moz-user-select: none;
401 | -ms-user-select: none;
402 | user-select: none;
403 | }
404 | .line-numbers-rows > span {
405 | pointer-events: none;
406 | display: block;
407 | counter-increment: linenumber;
408 | }
409 | .line-numbers-rows > span:before {
410 | content: counter(linenumber);
411 | display: block;
412 | padding-right: 0.8em;
413 | text-align: right;
414 | }
415 | #text-display,
416 | #code-display,
417 | #typing-area,
418 | #coding-area {
419 | background: var(--boxBackgroundColor);
420 | }
421 | ::-webkit-scrollbar-corner {
422 | background-color: var(--boxBackgroundColor);
423 | }
424 | #input-field.wrong {
425 | color: #e05561;
426 | }
427 | .highlight {
428 | color: #f0a45d;
429 | }
430 | .correct {
431 | color: #8cc265;
432 | }
433 | .wrong {
434 | color: #e05561;
435 | }
436 | .char.notpassed {
437 | background: #e0556170;
438 | }
439 | #cursor {
440 | background: #5dbeff;
441 | }
442 | code[class*="language-"],
443 | pre[class*="language-"] {
444 | color: #c1c9d4;
445 | }
446 | .token.comment,
447 | .token.prolog,
448 | .token.doctype,
449 | .token.cdata {
450 | color: #909aad;
451 | }
452 | .token.punctuation {
453 | color: #c1c9d4;
454 | }
455 | .vscode-light code[class*="language-"],
456 | .vscode-light pre[class*="language-"] {
457 | color: #454d5e;
458 | }
459 | .vscode-light .token.comment,
460 | .vscode-light .token.prolog,
461 | .vscode-light .token.doctype,
462 | .vscode-light .token.cdata {
463 | color: #99a5b6;
464 | }
465 | .vscode-light .token.punctuation {
466 | color: #454d5e;
467 | }
468 | .token.selector,
469 | .token.tag {
470 | color: #e06c75;
471 | }
472 | .token.property,
473 | .token.boolean,
474 | .token.number,
475 | .token.constant,
476 | .token.symbol,
477 | .token.attr-name,
478 | .token.deleted {
479 | color: #d19a66;
480 | }
481 | .token.string,
482 | .token.char,
483 | .token.attr-value,
484 | .token.builtin,
485 | .token.inserted {
486 | color: #98c379;
487 | }
488 | .token.operator,
489 | .token.entity,
490 | .token.url,
491 | .language-css .token.string,
492 | .style .token.string {
493 | color: #56b6c2;
494 | }
495 | .token.atrule,
496 | .token.keyword {
497 | color: #c678dd;
498 | }
499 | .token.function {
500 | color: #61afef;
501 | }
502 | .token.regex,
503 | .token.important,
504 | .token.variable {
505 | color: #c678dd;
506 | }
507 | body.colorblind #text-display {
508 | color: #b4bdc7;
509 | }
510 | body.colorblind #input-field.wrong {
511 | color: #b2a048;
512 | }
513 | body.colorblind .highlight {
514 | color: #ffffff;
515 | }
516 | body.colorblind .correct {
517 | color: #6a91dc;
518 | }
519 | body.colorblind .wrong {
520 | color: #b2a048;
521 | }
522 | /*# sourceMappingURL=style.css.map */
523 |
--------------------------------------------------------------------------------
/webview/modules/tinycolor.min.js:
--------------------------------------------------------------------------------
1 | !(function (t) {
2 | var e = /^\s+/,
3 | r = /\s+$/,
4 | n = 0,
5 | a = t.round,
6 | i = t.min,
7 | s = t.max,
8 | o = t.random;
9 | function f(o, h) {
10 | if (((h = h || {}), (o = o || "") instanceof f)) return o;
11 | if (!(this instanceof f)) return new f(o, h);
12 | var u = (function (n) {
13 | var a = { r: 0, g: 0, b: 0 },
14 | o = 1,
15 | f = null,
16 | h = null,
17 | u = null,
18 | l = !1,
19 | c = !1;
20 | "string" == typeof n &&
21 | (n = (function (t) {
22 | t = t.replace(e, "").replace(r, "").toLowerCase();
23 | var n,
24 | a = !1;
25 | if (H[t]) (t = H[t]), (a = !0);
26 | else if ("transparent" == t)
27 | return { r: 0, g: 0, b: 0, a: 0, format: "name" };
28 | if ((n = O.rgb.exec(t))) return { r: n[1], g: n[2], b: n[3] };
29 | if ((n = O.rgba.exec(t)))
30 | return { r: n[1], g: n[2], b: n[3], a: n[4] };
31 | if ((n = O.hsl.exec(t))) return { h: n[1], s: n[2], l: n[3] };
32 | if ((n = O.hsla.exec(t)))
33 | return { h: n[1], s: n[2], l: n[3], a: n[4] };
34 | if ((n = O.hsv.exec(t))) return { h: n[1], s: n[2], v: n[3] };
35 | if ((n = O.hsva.exec(t)))
36 | return { h: n[1], s: n[2], v: n[3], a: n[4] };
37 | if ((n = O.hex8.exec(t)))
38 | return {
39 | r: M(n[1]),
40 | g: M(n[2]),
41 | b: M(n[3]),
42 | a: z(n[4]),
43 | format: a ? "name" : "hex8",
44 | };
45 | if ((n = O.hex6.exec(t)))
46 | return {
47 | r: M(n[1]),
48 | g: M(n[2]),
49 | b: M(n[3]),
50 | format: a ? "name" : "hex",
51 | };
52 | if ((n = O.hex4.exec(t)))
53 | return {
54 | r: M(n[1] + "" + n[1]),
55 | g: M(n[2] + "" + n[2]),
56 | b: M(n[3] + "" + n[3]),
57 | a: z(n[4] + "" + n[4]),
58 | format: a ? "name" : "hex8",
59 | };
60 | if ((n = O.hex3.exec(t)))
61 | return {
62 | r: M(n[1] + "" + n[1]),
63 | g: M(n[2] + "" + n[2]),
64 | b: M(n[3] + "" + n[3]),
65 | format: a ? "name" : "hex",
66 | };
67 | return !1;
68 | })(n));
69 | "object" == typeof n &&
70 | (P(n.r) && P(n.g) && P(n.b)
71 | ? ((g = n.r),
72 | (b = n.g),
73 | (d = n.b),
74 | (a = {
75 | r: 255 * C(g, 255),
76 | g: 255 * C(b, 255),
77 | b: 255 * C(d, 255),
78 | }),
79 | (l = !0),
80 | (c = "%" === String(n.r).substr(-1) ? "prgb" : "rgb"))
81 | : P(n.h) && P(n.s) && P(n.v)
82 | ? ((f = L(n.s)),
83 | (h = L(n.v)),
84 | (a = (function (e, r, n) {
85 | (e = 6 * C(e, 360)), (r = C(r, 100)), (n = C(n, 100));
86 | var a = t.floor(e),
87 | i = e - a,
88 | s = n * (1 - r),
89 | o = n * (1 - i * r),
90 | f = n * (1 - (1 - i) * r),
91 | h = a % 6;
92 | return {
93 | r: 255 * [n, o, s, s, f, n][h],
94 | g: 255 * [f, n, n, o, s, s][h],
95 | b: 255 * [s, s, f, n, n, o][h],
96 | };
97 | })(n.h, f, h)),
98 | (l = !0),
99 | (c = "hsv"))
100 | : P(n.h) &&
101 | P(n.s) &&
102 | P(n.l) &&
103 | ((f = L(n.s)),
104 | (u = L(n.l)),
105 | (a = (function (t, e, r) {
106 | var n, a, i;
107 | function s(t, e, r) {
108 | return (
109 | r < 0 && (r += 1),
110 | r > 1 && (r -= 1),
111 | r < 1 / 6
112 | ? t + 6 * (e - t) * r
113 | : r < 0.5
114 | ? e
115 | : r < 2 / 3
116 | ? t + (e - t) * (2 / 3 - r) * 6
117 | : t
118 | );
119 | }
120 | if (((t = C(t, 360)), (e = C(e, 100)), (r = C(r, 100)), 0 === e))
121 | n = a = i = r;
122 | else {
123 | var o = r < 0.5 ? r * (1 + e) : r + e - r * e,
124 | f = 2 * r - o;
125 | (n = s(f, o, t + 1 / 3)),
126 | (a = s(f, o, t)),
127 | (i = s(f, o, t - 1 / 3));
128 | }
129 | return { r: 255 * n, g: 255 * a, b: 255 * i };
130 | })(n.h, f, u)),
131 | (l = !0),
132 | (c = "hsl")),
133 | n.hasOwnProperty("a") && (o = n.a));
134 | var g, b, d;
135 | return (
136 | (o = F(o)),
137 | {
138 | ok: l,
139 | format: n.format || c,
140 | r: i(255, s(a.r, 0)),
141 | g: i(255, s(a.g, 0)),
142 | b: i(255, s(a.b, 0)),
143 | a: o,
144 | }
145 | );
146 | })(o);
147 | (this._originalInput = o),
148 | (this._r = u.r),
149 | (this._g = u.g),
150 | (this._b = u.b),
151 | (this._a = u.a),
152 | (this._roundA = a(100 * this._a) / 100),
153 | (this._format = h.format || u.format),
154 | (this._gradientType = h.gradientType),
155 | this._r < 1 && (this._r = a(this._r)),
156 | this._g < 1 && (this._g = a(this._g)),
157 | this._b < 1 && (this._b = a(this._b)),
158 | (this._ok = u.ok),
159 | (this._tc_id = n++);
160 | }
161 | function h(t, e, r) {
162 | (t = C(t, 255)), (e = C(e, 255)), (r = C(r, 255));
163 | var n,
164 | a,
165 | o = s(t, e, r),
166 | f = i(t, e, r),
167 | h = (o + f) / 2;
168 | if (o == f) n = a = 0;
169 | else {
170 | var u = o - f;
171 | switch (((a = h > 0.5 ? u / (2 - o - f) : u / (o + f)), o)) {
172 | case t:
173 | n = (e - r) / u + (e < r ? 6 : 0);
174 | break;
175 | case e:
176 | n = (r - t) / u + 2;
177 | break;
178 | case r:
179 | n = (t - e) / u + 4;
180 | }
181 | n /= 6;
182 | }
183 | return { h: n, s: a, l: h };
184 | }
185 | function u(t, e, r) {
186 | (t = C(t, 255)), (e = C(e, 255)), (r = C(r, 255));
187 | var n,
188 | a,
189 | o = s(t, e, r),
190 | f = i(t, e, r),
191 | h = o,
192 | u = o - f;
193 | if (((a = 0 === o ? 0 : u / o), o == f)) n = 0;
194 | else {
195 | switch (o) {
196 | case t:
197 | n = (e - r) / u + (e < r ? 6 : 0);
198 | break;
199 | case e:
200 | n = (r - t) / u + 2;
201 | break;
202 | case r:
203 | n = (t - e) / u + 4;
204 | }
205 | n /= 6;
206 | }
207 | return { h: n, s: a, v: h };
208 | }
209 | function l(t, e, r, n) {
210 | var i = [I(a(t).toString(16)), I(a(e).toString(16)), I(a(r).toString(16))];
211 | return n &&
212 | i[0].charAt(0) == i[0].charAt(1) &&
213 | i[1].charAt(0) == i[1].charAt(1) &&
214 | i[2].charAt(0) == i[2].charAt(1)
215 | ? i[0].charAt(0) + i[1].charAt(0) + i[2].charAt(0)
216 | : i.join("");
217 | }
218 | function c(t, e, r, n) {
219 | return [
220 | I(N(n)),
221 | I(a(t).toString(16)),
222 | I(a(e).toString(16)),
223 | I(a(r).toString(16)),
224 | ].join("");
225 | }
226 | function g(t, e) {
227 | e = 0 === e ? 0 : e || 10;
228 | var r = f(t).toHsl();
229 | return (r.s -= e / 100), (r.s = q(r.s)), f(r);
230 | }
231 | function b(t, e) {
232 | e = 0 === e ? 0 : e || 10;
233 | var r = f(t).toHsl();
234 | return (r.s += e / 100), (r.s = q(r.s)), f(r);
235 | }
236 | function d(t) {
237 | return f(t).desaturate(100);
238 | }
239 | function _(t, e) {
240 | e = 0 === e ? 0 : e || 10;
241 | var r = f(t).toHsl();
242 | return (r.l += e / 100), (r.l = q(r.l)), f(r);
243 | }
244 | function p(t, e) {
245 | e = 0 === e ? 0 : e || 10;
246 | var r = f(t).toRgb();
247 | return (
248 | (r.r = s(0, i(255, r.r - a((-e / 100) * 255)))),
249 | (r.g = s(0, i(255, r.g - a((-e / 100) * 255)))),
250 | (r.b = s(0, i(255, r.b - a((-e / 100) * 255)))),
251 | f(r)
252 | );
253 | }
254 | function m(t, e) {
255 | e = 0 === e ? 0 : e || 10;
256 | var r = f(t).toHsl();
257 | return (r.l -= e / 100), (r.l = q(r.l)), f(r);
258 | }
259 | function v(t, e) {
260 | var r = f(t).toHsl(),
261 | n = (r.h + e) % 360;
262 | return (r.h = n < 0 ? 360 + n : n), f(r);
263 | }
264 | function y(t) {
265 | var e = f(t).toHsl();
266 | return (e.h = (e.h + 180) % 360), f(e);
267 | }
268 | function A(t) {
269 | var e = f(t).toHsl(),
270 | r = e.h;
271 | return [
272 | f(t),
273 | f({ h: (r + 120) % 360, s: e.s, l: e.l }),
274 | f({ h: (r + 240) % 360, s: e.s, l: e.l }),
275 | ];
276 | }
277 | function x(t) {
278 | var e = f(t).toHsl(),
279 | r = e.h;
280 | return [
281 | f(t),
282 | f({ h: (r + 90) % 360, s: e.s, l: e.l }),
283 | f({ h: (r + 180) % 360, s: e.s, l: e.l }),
284 | f({ h: (r + 270) % 360, s: e.s, l: e.l }),
285 | ];
286 | }
287 | function k(t) {
288 | var e = f(t).toHsl(),
289 | r = e.h;
290 | return [
291 | f(t),
292 | f({ h: (r + 72) % 360, s: e.s, l: e.l }),
293 | f({ h: (r + 216) % 360, s: e.s, l: e.l }),
294 | ];
295 | }
296 | function w(t, e, r) {
297 | (e = e || 6), (r = r || 30);
298 | var n = f(t).toHsl(),
299 | a = 360 / r,
300 | i = [f(t)];
301 | for (n.h = (n.h - ((a * e) >> 1) + 720) % 360; --e; )
302 | (n.h = (n.h + a) % 360), i.push(f(n));
303 | return i;
304 | }
305 | function S(t, e) {
306 | e = e || 6;
307 | for (
308 | var r = f(t).toHsv(), n = r.h, a = r.s, i = r.v, s = [], o = 1 / e;
309 | e--;
310 |
311 | )
312 | s.push(f({ h: n, s: a, v: i })), (i = (i + o) % 1);
313 | return s;
314 | }
315 | (f.prototype = {
316 | isDark: function () {
317 | return this.getBrightness() < 128;
318 | },
319 | isLight: function () {
320 | return !this.isDark();
321 | },
322 | isValid: function () {
323 | return this._ok;
324 | },
325 | getOriginalInput: function () {
326 | return this._originalInput;
327 | },
328 | getFormat: function () {
329 | return this._format;
330 | },
331 | getAlpha: function () {
332 | return this._a;
333 | },
334 | getBrightness: function () {
335 | var t = this.toRgb();
336 | return (299 * t.r + 587 * t.g + 114 * t.b) / 1e3;
337 | },
338 | getLuminance: function () {
339 | var e,
340 | r,
341 | n,
342 | a = this.toRgb();
343 | return (
344 | (e = a.r / 255),
345 | (r = a.g / 255),
346 | (n = a.b / 255),
347 | 0.2126 * (e <= 0.03928 ? e / 12.92 : t.pow((e + 0.055) / 1.055, 2.4)) +
348 | 0.7152 *
349 | (r <= 0.03928 ? r / 12.92 : t.pow((r + 0.055) / 1.055, 2.4)) +
350 | 0.0722 * (n <= 0.03928 ? n / 12.92 : t.pow((n + 0.055) / 1.055, 2.4))
351 | );
352 | },
353 | setAlpha: function (t) {
354 | return (this._a = F(t)), (this._roundA = a(100 * this._a) / 100), this;
355 | },
356 | toHsv: function () {
357 | var t = u(this._r, this._g, this._b);
358 | return { h: 360 * t.h, s: t.s, v: t.v, a: this._a };
359 | },
360 | toHsvString: function () {
361 | var t = u(this._r, this._g, this._b),
362 | e = a(360 * t.h),
363 | r = a(100 * t.s),
364 | n = a(100 * t.v);
365 | return 1 == this._a
366 | ? "hsv(" + e + ", " + r + "%, " + n + "%)"
367 | : "hsva(" + e + ", " + r + "%, " + n + "%, " + this._roundA + ")";
368 | },
369 | toHsl: function () {
370 | var t = h(this._r, this._g, this._b);
371 | return { h: 360 * t.h, s: t.s, l: t.l, a: this._a };
372 | },
373 | toHslString: function () {
374 | var t = h(this._r, this._g, this._b),
375 | e = a(360 * t.h),
376 | r = a(100 * t.s),
377 | n = a(100 * t.l);
378 | return 1 == this._a
379 | ? "hsl(" + e + ", " + r + "%, " + n + "%)"
380 | : "hsla(" + e + ", " + r + "%, " + n + "%, " + this._roundA + ")";
381 | },
382 | toHex: function (t) {
383 | return l(this._r, this._g, this._b, t);
384 | },
385 | toHexString: function (t) {
386 | return "#" + this.toHex(t);
387 | },
388 | toHex8: function (t) {
389 | return (function (t, e, r, n, i) {
390 | var s = [
391 | I(a(t).toString(16)),
392 | I(a(e).toString(16)),
393 | I(a(r).toString(16)),
394 | I(N(n)),
395 | ];
396 | if (
397 | i &&
398 | s[0].charAt(0) == s[0].charAt(1) &&
399 | s[1].charAt(0) == s[1].charAt(1) &&
400 | s[2].charAt(0) == s[2].charAt(1) &&
401 | s[3].charAt(0) == s[3].charAt(1)
402 | )
403 | return (
404 | s[0].charAt(0) + s[1].charAt(0) + s[2].charAt(0) + s[3].charAt(0)
405 | );
406 | return s.join("");
407 | })(this._r, this._g, this._b, this._a, t);
408 | },
409 | toHex8String: function (t) {
410 | return "#" + this.toHex8(t);
411 | },
412 | toRgb: function () {
413 | return { r: a(this._r), g: a(this._g), b: a(this._b), a: this._a };
414 | },
415 | toRgbString: function () {
416 | return 1 == this._a
417 | ? "rgb(" + a(this._r) + ", " + a(this._g) + ", " + a(this._b) + ")"
418 | : "rgba(" +
419 | a(this._r) +
420 | ", " +
421 | a(this._g) +
422 | ", " +
423 | a(this._b) +
424 | ", " +
425 | this._roundA +
426 | ")";
427 | },
428 | toPercentageRgb: function () {
429 | return {
430 | r: a(100 * C(this._r, 255)) + "%",
431 | g: a(100 * C(this._g, 255)) + "%",
432 | b: a(100 * C(this._b, 255)) + "%",
433 | a: this._a,
434 | };
435 | },
436 | toPercentageRgbString: function () {
437 | return 1 == this._a
438 | ? "rgb(" +
439 | a(100 * C(this._r, 255)) +
440 | "%, " +
441 | a(100 * C(this._g, 255)) +
442 | "%, " +
443 | a(100 * C(this._b, 255)) +
444 | "%)"
445 | : "rgba(" +
446 | a(100 * C(this._r, 255)) +
447 | "%, " +
448 | a(100 * C(this._g, 255)) +
449 | "%, " +
450 | a(100 * C(this._b, 255)) +
451 | "%, " +
452 | this._roundA +
453 | ")";
454 | },
455 | toName: function () {
456 | return 0 === this._a
457 | ? "transparent"
458 | : !(this._a < 1) && (R[l(this._r, this._g, this._b, !0)] || !1);
459 | },
460 | toFilter: function (t) {
461 | var e = "#" + c(this._r, this._g, this._b, this._a),
462 | r = e,
463 | n = this._gradientType ? "GradientType = 1, " : "";
464 | if (t) {
465 | var a = f(t);
466 | r = "#" + c(a._r, a._g, a._b, a._a);
467 | }
468 | return (
469 | "progid:DXImageTransform.Microsoft.gradient(" +
470 | n +
471 | "startColorstr=" +
472 | e +
473 | ",endColorstr=" +
474 | r +
475 | ")"
476 | );
477 | },
478 | toString: function (t) {
479 | var e = !!t;
480 | t = t || this._format;
481 | var r = !1,
482 | n = this._a < 1 && this._a >= 0;
483 | return e ||
484 | !n ||
485 | ("hex" !== t &&
486 | "hex6" !== t &&
487 | "hex3" !== t &&
488 | "hex4" !== t &&
489 | "hex8" !== t &&
490 | "name" !== t)
491 | ? ("rgb" === t && (r = this.toRgbString()),
492 | "prgb" === t && (r = this.toPercentageRgbString()),
493 | ("hex" !== t && "hex6" !== t) || (r = this.toHexString()),
494 | "hex3" === t && (r = this.toHexString(!0)),
495 | "hex4" === t && (r = this.toHex8String(!0)),
496 | "hex8" === t && (r = this.toHex8String()),
497 | "name" === t && (r = this.toName()),
498 | "hsl" === t && (r = this.toHslString()),
499 | "hsv" === t && (r = this.toHsvString()),
500 | r || this.toHexString())
501 | : "name" === t && 0 === this._a
502 | ? this.toName()
503 | : this.toRgbString();
504 | },
505 | clone: function () {
506 | return f(this.toString());
507 | },
508 | _applyModification: function (t, e) {
509 | var r = t.apply(null, [this].concat([].slice.call(e)));
510 | return (
511 | (this._r = r._r),
512 | (this._g = r._g),
513 | (this._b = r._b),
514 | this.setAlpha(r._a),
515 | this
516 | );
517 | },
518 | lighten: function () {
519 | return this._applyModification(_, arguments);
520 | },
521 | brighten: function () {
522 | return this._applyModification(p, arguments);
523 | },
524 | darken: function () {
525 | return this._applyModification(m, arguments);
526 | },
527 | desaturate: function () {
528 | return this._applyModification(g, arguments);
529 | },
530 | saturate: function () {
531 | return this._applyModification(b, arguments);
532 | },
533 | greyscale: function () {
534 | return this._applyModification(d, arguments);
535 | },
536 | spin: function () {
537 | return this._applyModification(v, arguments);
538 | },
539 | _applyCombination: function (t, e) {
540 | return t.apply(null, [this].concat([].slice.call(e)));
541 | },
542 | analogous: function () {
543 | return this._applyCombination(w, arguments);
544 | },
545 | complement: function () {
546 | return this._applyCombination(y, arguments);
547 | },
548 | monochromatic: function () {
549 | return this._applyCombination(S, arguments);
550 | },
551 | splitcomplement: function () {
552 | return this._applyCombination(k, arguments);
553 | },
554 | triad: function () {
555 | return this._applyCombination(A, arguments);
556 | },
557 | tetrad: function () {
558 | return this._applyCombination(x, arguments);
559 | },
560 | }),
561 | (f.fromRatio = function (t, e) {
562 | if ("object" == typeof t) {
563 | var r = {};
564 | for (var n in t)
565 | t.hasOwnProperty(n) && (r[n] = "a" === n ? t[n] : L(t[n]));
566 | t = r;
567 | }
568 | return f(t, e);
569 | }),
570 | (f.equals = function (t, e) {
571 | return !(!t || !e) && f(t).toRgbString() == f(e).toRgbString();
572 | }),
573 | (f.random = function () {
574 | return f.fromRatio({ r: o(), g: o(), b: o() });
575 | }),
576 | (f.mix = function (t, e, r) {
577 | r = 0 === r ? 0 : r || 50;
578 | var n = f(t).toRgb(),
579 | a = f(e).toRgb(),
580 | i = r / 100;
581 | return f({
582 | r: (a.r - n.r) * i + n.r,
583 | g: (a.g - n.g) * i + n.g,
584 | b: (a.b - n.b) * i + n.b,
585 | a: (a.a - n.a) * i + n.a,
586 | });
587 | }),
588 | (f.readability = function (e, r) {
589 | var n = f(e),
590 | a = f(r);
591 | return (
592 | (t.max(n.getLuminance(), a.getLuminance()) + 0.05) /
593 | (t.min(n.getLuminance(), a.getLuminance()) + 0.05)
594 | );
595 | }),
596 | (f.isReadable = function (t, e, r) {
597 | var n,
598 | a,
599 | i = f.readability(t, e);
600 | switch (
601 | ((a = !1),
602 | (n = (function (t) {
603 | var e, r;
604 | (e = (
605 | (t = t || { level: "AA", size: "small" }).level || "AA"
606 | ).toUpperCase()),
607 | (r = (t.size || "small").toLowerCase()),
608 | "AA" !== e && "AAA" !== e && (e = "AA");
609 | "small" !== r && "large" !== r && (r = "small");
610 | return { level: e, size: r };
611 | })(r)).level + n.size)
612 | ) {
613 | case "AAsmall":
614 | case "AAAlarge":
615 | a = i >= 4.5;
616 | break;
617 | case "AAlarge":
618 | a = i >= 3;
619 | break;
620 | case "AAAsmall":
621 | a = i >= 7;
622 | }
623 | return a;
624 | }),
625 | (f.mostReadable = function (t, e, r) {
626 | var n,
627 | a,
628 | i,
629 | s,
630 | o = null,
631 | h = 0;
632 | (a = (r = r || {}).includeFallbackColors), (i = r.level), (s = r.size);
633 | for (var u = 0; u < e.length; u++)
634 | (n = f.readability(t, e[u])) > h && ((h = n), (o = f(e[u])));
635 | return f.isReadable(t, o, { level: i, size: s }) || !a
636 | ? o
637 | : ((r.includeFallbackColors = !1),
638 | f.mostReadable(t, ["#fff", "#000"], r));
639 | });
640 | var H = (f.names = {
641 | aliceblue: "f0f8ff",
642 | antiquewhite: "faebd7",
643 | aqua: "0ff",
644 | aquamarine: "7fffd4",
645 | azure: "f0ffff",
646 | beige: "f5f5dc",
647 | bisque: "ffe4c4",
648 | black: "000",
649 | blanchedalmond: "ffebcd",
650 | blue: "00f",
651 | blueviolet: "8a2be2",
652 | brown: "a52a2a",
653 | burlywood: "deb887",
654 | burntsienna: "ea7e5d",
655 | cadetblue: "5f9ea0",
656 | chartreuse: "7fff00",
657 | chocolate: "d2691e",
658 | coral: "ff7f50",
659 | cornflowerblue: "6495ed",
660 | cornsilk: "fff8dc",
661 | crimson: "dc143c",
662 | cyan: "0ff",
663 | darkblue: "00008b",
664 | darkcyan: "008b8b",
665 | darkgoldenrod: "b8860b",
666 | darkgray: "a9a9a9",
667 | darkgreen: "006400",
668 | darkgrey: "a9a9a9",
669 | darkkhaki: "bdb76b",
670 | darkmagenta: "8b008b",
671 | darkolivegreen: "556b2f",
672 | darkorange: "ff8c00",
673 | darkorchid: "9932cc",
674 | darkred: "8b0000",
675 | darksalmon: "e9967a",
676 | darkseagreen: "8fbc8f",
677 | darkslateblue: "483d8b",
678 | darkslategray: "2f4f4f",
679 | darkslategrey: "2f4f4f",
680 | darkturquoise: "00ced1",
681 | darkviolet: "9400d3",
682 | deeppink: "ff1493",
683 | deepskyblue: "00bfff",
684 | dimgray: "696969",
685 | dimgrey: "696969",
686 | dodgerblue: "1e90ff",
687 | firebrick: "b22222",
688 | floralwhite: "fffaf0",
689 | forestgreen: "228b22",
690 | fuchsia: "f0f",
691 | gainsboro: "dcdcdc",
692 | ghostwhite: "f8f8ff",
693 | gold: "ffd700",
694 | goldenrod: "daa520",
695 | gray: "808080",
696 | green: "008000",
697 | greenyellow: "adff2f",
698 | grey: "808080",
699 | honeydew: "f0fff0",
700 | hotpink: "ff69b4",
701 | indianred: "cd5c5c",
702 | indigo: "4b0082",
703 | ivory: "fffff0",
704 | khaki: "f0e68c",
705 | lavender: "e6e6fa",
706 | lavenderblush: "fff0f5",
707 | lawngreen: "7cfc00",
708 | lemonchiffon: "fffacd",
709 | lightblue: "add8e6",
710 | lightcoral: "f08080",
711 | lightcyan: "e0ffff",
712 | lightgoldenrodyellow: "fafad2",
713 | lightgray: "d3d3d3",
714 | lightgreen: "90ee90",
715 | lightgrey: "d3d3d3",
716 | lightpink: "ffb6c1",
717 | lightsalmon: "ffa07a",
718 | lightseagreen: "20b2aa",
719 | lightskyblue: "87cefa",
720 | lightslategray: "789",
721 | lightslategrey: "789",
722 | lightsteelblue: "b0c4de",
723 | lightyellow: "ffffe0",
724 | lime: "0f0",
725 | limegreen: "32cd32",
726 | linen: "faf0e6",
727 | magenta: "f0f",
728 | maroon: "800000",
729 | mediumaquamarine: "66cdaa",
730 | mediumblue: "0000cd",
731 | mediumorchid: "ba55d3",
732 | mediumpurple: "9370db",
733 | mediumseagreen: "3cb371",
734 | mediumslateblue: "7b68ee",
735 | mediumspringgreen: "00fa9a",
736 | mediumturquoise: "48d1cc",
737 | mediumvioletred: "c71585",
738 | midnightblue: "191970",
739 | mintcream: "f5fffa",
740 | mistyrose: "ffe4e1",
741 | moccasin: "ffe4b5",
742 | navajowhite: "ffdead",
743 | navy: "000080",
744 | oldlace: "fdf5e6",
745 | olive: "808000",
746 | olivedrab: "6b8e23",
747 | orange: "ffa500",
748 | orangered: "ff4500",
749 | orchid: "da70d6",
750 | palegoldenrod: "eee8aa",
751 | palegreen: "98fb98",
752 | paleturquoise: "afeeee",
753 | palevioletred: "db7093",
754 | papayawhip: "ffefd5",
755 | peachpuff: "ffdab9",
756 | peru: "cd853f",
757 | pink: "ffc0cb",
758 | plum: "dda0dd",
759 | powderblue: "b0e0e6",
760 | purple: "800080",
761 | rebeccapurple: "663399",
762 | red: "f00",
763 | rosybrown: "bc8f8f",
764 | royalblue: "4169e1",
765 | saddlebrown: "8b4513",
766 | salmon: "fa8072",
767 | sandybrown: "f4a460",
768 | seagreen: "2e8b57",
769 | seashell: "fff5ee",
770 | sienna: "a0522d",
771 | silver: "c0c0c0",
772 | skyblue: "87ceeb",
773 | slateblue: "6a5acd",
774 | slategray: "708090",
775 | slategrey: "708090",
776 | snow: "fffafa",
777 | springgreen: "00ff7f",
778 | steelblue: "4682b4",
779 | tan: "d2b48c",
780 | teal: "008080",
781 | thistle: "d8bfd8",
782 | tomato: "ff6347",
783 | turquoise: "40e0d0",
784 | violet: "ee82ee",
785 | wheat: "f5deb3",
786 | white: "fff",
787 | whitesmoke: "f5f5f5",
788 | yellow: "ff0",
789 | yellowgreen: "9acd32",
790 | }),
791 | R = (f.hexNames = (function (t) {
792 | var e = {};
793 | for (var r in t) t.hasOwnProperty(r) && (e[t[r]] = r);
794 | return e;
795 | })(H));
796 | function F(t) {
797 | return (t = parseFloat(t)), (isNaN(t) || t < 0 || t > 1) && (t = 1), t;
798 | }
799 | function C(e, r) {
800 | (function (t) {
801 | return (
802 | "string" == typeof t && -1 != t.indexOf(".") && 1 === parseFloat(t)
803 | );
804 | })(e) && (e = "100%");
805 | var n = (function (t) {
806 | return "string" == typeof t && -1 != t.indexOf("%");
807 | })(e);
808 | return (
809 | (e = i(r, s(0, parseFloat(e)))),
810 | n && (e = parseInt(e * r, 10) / 100),
811 | t.abs(e - r) < 1e-6 ? 1 : (e % r) / parseFloat(r)
812 | );
813 | }
814 | function q(t) {
815 | return i(1, s(0, t));
816 | }
817 | function M(t) {
818 | return parseInt(t, 16);
819 | }
820 | function I(t) {
821 | return 1 == t.length ? "0" + t : "" + t;
822 | }
823 | function L(t) {
824 | return t <= 1 && (t = 100 * t + "%"), t;
825 | }
826 | function N(e) {
827 | return t.round(255 * parseFloat(e)).toString(16);
828 | }
829 | function z(t) {
830 | return M(t) / 255;
831 | }
832 | var E,
833 | T,
834 | j,
835 | O =
836 | ((T =
837 | "[\\s|\\(]+(" +
838 | (E = "(?:[-\\+]?\\d*\\.\\d+%?)|(?:[-\\+]?\\d+%?)") +
839 | ")[,|\\s]+(" +
840 | E +
841 | ")[,|\\s]+(" +
842 | E +
843 | ")\\s*\\)?"),
844 | (j =
845 | "[\\s|\\(]+(" +
846 | E +
847 | ")[,|\\s]+(" +
848 | E +
849 | ")[,|\\s]+(" +
850 | E +
851 | ")[,|\\s]+(" +
852 | E +
853 | ")\\s*\\)?"),
854 | {
855 | CSS_UNIT: new RegExp(E),
856 | rgb: new RegExp("rgb" + T),
857 | rgba: new RegExp("rgba" + j),
858 | hsl: new RegExp("hsl" + T),
859 | hsla: new RegExp("hsla" + j),
860 | hsv: new RegExp("hsv" + T),
861 | hsva: new RegExp("hsva" + j),
862 | hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
863 | hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
864 | hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
865 | hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
866 | });
867 | function P(t) {
868 | return !!O.CSS_UNIT.exec(t);
869 | }
870 | "undefined" != typeof module && module.exports
871 | ? (module.exports = f)
872 | : "function" == typeof define && define.amd
873 | ? define(function () {
874 | return f;
875 | })
876 | : (window.tinycolor = f);
877 | })(Math);
878 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GPL-3.0 License GPL-3.0 License GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/webview/main.js:
--------------------------------------------------------------------------------
1 | // This script will be run within the webview itself
2 | // It cannot access the main VS Code APIs directly
3 | // @ts-nocheck
4 |
5 | (function () {
6 | //====================================================
7 | // Global
8 | //====================================================
9 | const vscode = acquireVsCodeApi();
10 |
11 | // Handle messages sent from the extension to the webview
12 | window.addEventListener("message", (event) => {
13 | const message = event.data;
14 |
15 | // Message sent when the extension activates and sends settings
16 | if (message.type === "allConfig") {
17 | setBoxBackgroundColor();
18 |
19 | // Put words list and settings into a state
20 | vscode.setState({
21 | allWords: message.words,
22 | allCodes: message.codes,
23 | language: message.language,
24 | codeLanguage: message.codeLanguage,
25 | count: message.count,
26 | mode: message.mode,
27 | punctuation: message.punctuation,
28 | colorBlindMode: message.colorBlindMode,
29 | });
30 | extensionState = vscode.getState();
31 |
32 | // Change words list and settings
33 | allWords = message.words;
34 | allCodes = message.codes;
35 | setLanguage(message.language);
36 | setCodeLanguage(message.codeLanguage);
37 | setWordCount(message.count);
38 | setTimeCount(message.count);
39 | setTypingMode(message.mode);
40 | setColorBlindMode(message.colorBlindMode);
41 | setPunctuation(message.punctuation);
42 |
43 | // Start typing test
44 | if (extensionState.mode === "code snippets") {
45 | setCodeText();
46 | showCodeText();
47 | } else {
48 | setText();
49 | showText();
50 | }
51 | } else if (message.type === "practiceWithSelection") {
52 | setBoxBackgroundColor();
53 |
54 | // Put words list and settings into a state
55 | vscode.setState({
56 | allWords: message.words,
57 | allCodes: message.codes,
58 | language: message.language,
59 | codeLanguage: message.codeLanguage,
60 | count: message.count,
61 | mode: message.mode,
62 | punctuation: message.punctuation,
63 | colorBlindMode: message.colorBlindMode,
64 | });
65 | extensionState = vscode.getState();
66 |
67 | // Change words list and settings
68 | allWords = message.words;
69 | allCodes = message.codes;
70 | setLanguage(message.language);
71 | setCodeLanguage(message.codeLanguage);
72 | setWordCount(message.count);
73 | setTimeCount(message.count);
74 | setTypingMode(message.mode);
75 | setColorBlindMode(message.colorBlindMode);
76 | setPunctuation(message.punctuation);
77 |
78 | // Start typing test
79 | setSelectedCodeText(message.selectedCode, message.selectedCodeLanguage);
80 |
81 | showCodeText(message.selectedCodeLanguage);
82 | } else {
83 | // Message to change a single setting
84 | switch (message.config) {
85 | case "switchNaturalLanguage":
86 | if (message.value) {
87 | setLanguage(message.value);
88 |
89 | vscode.setState({ ...extensionState, language: message.value });
90 | extensionState = vscode.getState();
91 |
92 | if (extensionState.mode !== "code snippets") {
93 | setText();
94 | showText();
95 | }
96 | }
97 | break;
98 |
99 | case "switchProgrammingLanguage":
100 | if (message.value) {
101 | setCodeLanguage(message.value);
102 |
103 | vscode.setState({
104 | ...extensionState,
105 | codeLanguage: message.value,
106 | });
107 | extensionState = vscode.getState();
108 |
109 | if (extensionState.mode === "code snippets") {
110 | setCodeText();
111 | showCodeText();
112 | }
113 | }
114 | break;
115 |
116 | case "changeTypingMode":
117 | if (message.value) {
118 | vscode.setState({
119 | ...extensionState,
120 | mode: message.value,
121 | });
122 | extensionState = vscode.getState();
123 |
124 | gameOver = true;
125 | setTypingMode(message.value);
126 | }
127 | break;
128 |
129 | case "togglePunctuation":
130 | if (message.value) {
131 | setPunctuation(message.value);
132 |
133 | vscode.setState({
134 | ...extensionState,
135 | punctuation: message.value,
136 | });
137 | extensionState = vscode.getState();
138 |
139 | if (extensionState.mode !== "code snippets") {
140 | setText();
141 | showText();
142 | }
143 | }
144 | break;
145 |
146 | case "toggleColorBlindMode":
147 | setColorBlindMode(message.value);
148 |
149 | vscode.setState({ ...extensionState, colorBlindMode: message.value });
150 | extensionState = vscode.getState();
151 | break;
152 |
153 | case "changeCount":
154 | if (message.value) {
155 | setWordCount(message.value);
156 | setTimeCount(message.value);
157 |
158 | vscode.setState({
159 | ...extensionState,
160 | count: message.value,
161 | });
162 | extensionState = vscode.getState();
163 |
164 | if (extensionState.mode !== "code snippets") {
165 | setText();
166 | showText();
167 | }
168 | }
169 | break;
170 |
171 | default:
172 | break;
173 | }
174 | }
175 | });
176 |
177 | // Get document elements
178 | const textDisplay = document.querySelector("#text-display");
179 | const inputField = document.querySelector("#input-field");
180 |
181 | const cursor = document.getElementById("cursor");
182 | const root = document.documentElement;
183 |
184 | // Initialize dynamic variables
185 | let selectedLanguageWords = [];
186 | let currentWordsList = [];
187 | let currentWord = 0;
188 | let correctKeys = 0;
189 | let punctuation = false;
190 | let wordCount;
191 | let timeCount;
192 | let startDate = 0;
193 | let timerActive = false;
194 | let timer;
195 |
196 | let allWords = [];
197 | let allCodes = [];
198 | let selectedLanguageCodes = [];
199 | let selectedLanguageName = "";
200 | let currentCode = "";
201 | let gameOver = true;
202 | let codeStartDate = 0;
203 | let codeState = {
204 | firstChar: null,
205 | lastChar: null,
206 | currentChar: null,
207 | currentCharNum: 0,
208 | cursorLeftOffset: 0,
209 | cursorTopOffset: 0,
210 | linesLastCursorPositions: [],
211 | };
212 |
213 | // Get all words and settings from the state if it exists
214 | let extensionState = vscode.getState();
215 | if (extensionState) {
216 | setLanguage(extensionState.language);
217 | setCodeLanguage(extensionState.codeLanguage);
218 | setWordCount(extensionState.count);
219 | setTimeCount(extensionState.count);
220 | setTypingMode(extensionState.mode);
221 | setColorBlindMode(extensionState.mode);
222 | setPunctuation(extensionState.punctuation);
223 | setText();
224 | showText();
225 | }
226 |
227 | // Restart if restart button hit
228 | document.querySelector("#restart-button").addEventListener("click", (e) => {
229 | setText(e);
230 | showText();
231 | });
232 | document.querySelector(".codeButton").addEventListener("click", (e) => {
233 | setCodeText(e);
234 | showCodeText();
235 | });
236 |
237 | // Restart if escape key hit
238 | document.addEventListener("keydown", (e) => {
239 | if (e.key === "Escape") {
240 | if (extensionState.mode === "code snippets") {
241 | setCodeText(e);
242 | showCodeText();
243 | } else {
244 | setText(e);
245 | showText();
246 | }
247 | }
248 | });
249 |
250 | // Function to change typing mode
251 | function setTypingMode(_mode) {
252 | const mode = _mode.toLowerCase();
253 |
254 | switch (mode) {
255 | case "words (fixed amount)":
256 | // Update ui
257 | document.querySelector("#coding-area").style.display = "none";
258 | document.querySelector("#time-count").style.display = "none";
259 | document.querySelector("#language-selected").style.display = "none";
260 | document.querySelector("#typing-area").style.display = "inline";
261 | document.querySelector("#word-count").style.display = "inline";
262 |
263 | // Start typing test
264 | setText();
265 | showText();
266 |
267 | break;
268 |
269 | case "words (against the clock)":
270 | // Update ui
271 | document.querySelector("#coding-area").style.display = "none";
272 | document.querySelector("#word-count").style.display = "none";
273 | document.querySelector("#language-selected").style.display = "none";
274 | document.querySelector("#typing-area").style.display = "inline";
275 | document.querySelector("#time-count").style.display = "inline";
276 |
277 | // Start typing test
278 | setText();
279 | showText();
280 |
281 | break;
282 |
283 | case "code snippets":
284 | // Update ui
285 | document.querySelector("#typing-area").style.display = "none";
286 | document.querySelector("#word-count").style.display = "none";
287 | document.querySelector("#time-count").style.display = "none";
288 | document.querySelector("#coding-area").style.display = "inline";
289 | document.querySelector("#language-selected").style.display = "inline";
290 |
291 | // Start typing test
292 | setCodeText();
293 | showCodeText();
294 |
295 | break;
296 |
297 | default:
298 | console.error(`Mode ${mode} is undefined`);
299 | }
300 | }
301 |
302 | // Function to change color blind mode
303 | function setColorBlindMode(_mode) {
304 | let body = document.querySelector("body");
305 | if (_mode === "true" && !body.classList.contains("colorblind")) {
306 | body.classList.add("colorblind");
307 | }
308 | if (_mode === "false" && body.classList.contains("colorblind")) {
309 | body.classList.remove("colorblind");
310 | }
311 | }
312 |
313 | // Function to set box backrgound color depending on editor background color
314 | function setBoxBackgroundColor() {
315 | let body = document.querySelector("body");
316 |
317 | // Get current editor background color
318 | let editorBackgroundColor = tinycolor(
319 | getComputedStyle(root).getPropertyValue("--editorBackgroundColor")
320 | );
321 | let boxBackgroundColor = "";
322 |
323 | if (editorBackgroundColor.isLight()) {
324 | boxBackgroundColor = tinycolor(editorBackgroundColor)
325 | .darken(4)
326 | .toString();
327 | } else {
328 | boxBackgroundColor = tinycolor(editorBackgroundColor)
329 | .lighten(4)
330 | .toString();
331 | }
332 |
333 | root.style.setProperty("--boxBackgroundColor", boxBackgroundColor);
334 | }
335 |
336 | //====================================================
337 | // Words mode
338 | //====================================================
339 | // Function to generate a new list of words
340 | function setText(e) {
341 | e = e || window.event;
342 | var keepWordList = e && e.shiftKey;
343 |
344 | // Reset
345 | if (!keepWordList) {
346 | currentWordsList = [];
347 | }
348 | currentWord = 0;
349 | correctKeys = 0;
350 | inputField.value = "";
351 | timerActive = false;
352 | clearTimeout(timer);
353 | textDisplay.style.display = "block";
354 | inputField.className = "";
355 |
356 | switch (extensionState.mode) {
357 | case "words (fixed amount)":
358 | textDisplay.style.height = "auto";
359 |
360 | textDisplay.innerHTML = "";
361 | if (!keepWordList) {
362 | currentWordsList = [];
363 |
364 | while (currentWordsList.length < wordCount) {
365 | const randomWord =
366 | selectedLanguageWords[
367 | Math.floor(Math.random() * selectedLanguageWords.length)
368 | ];
369 | if (
370 | currentWordsList[currentWordsList.length - 1] !== randomWord ||
371 | currentWordsList[currentWordsList.length - 1] === undefined
372 | ) {
373 | currentWordsList.push(randomWord);
374 | }
375 | }
376 | }
377 | break;
378 |
379 | case "words (against the clock)":
380 | textDisplay.style.height = "3.2rem";
381 |
382 | document.querySelector(`#tc-${timeCount}`).innerHTML = timeCount;
383 |
384 | textDisplay.innerHTML = "";
385 | if (!keepWordList) {
386 | currentWordsList = [];
387 |
388 | for (let i = 0; i < 500; i++) {
389 | let n = Math.floor(Math.random() * selectedLanguageWords.length);
390 |
391 | currentWordsList.push(selectedLanguageWords[n]);
392 | }
393 | }
394 | }
395 |
396 | if (punctuation) addPunctuations();
397 |
398 | inputField.focus();
399 | }
400 |
401 | // Function to display a list of words
402 | function showText() {
403 | currentWordsList.forEach((word) => {
404 | let span = document.createElement("span");
405 | span.innerHTML = word + " ";
406 |
407 | textDisplay.appendChild(span);
408 | });
409 |
410 | textDisplay.firstChild.classList.add("highlight");
411 | }
412 |
413 | // Function to calculate and display result
414 | function showResult() {
415 | let words, minute, acc;
416 | switch (extensionState.mode) {
417 | case "words (fixed amount)":
418 | words = correctKeys / 5;
419 | minute = (Date.now() - startDate) / 1000 / 60;
420 | let totalKeys = -1;
421 |
422 | currentWordsList.forEach((e) => (totalKeys += e.length + 1));
423 | acc = Math.floor((correctKeys / totalKeys) * 100);
424 | break;
425 |
426 | case "words (against the clock)":
427 | words = correctKeys / 5;
428 |
429 | minute = timeCount / 60;
430 | let sumKeys = -1;
431 |
432 | for (let i = 0; i < currentWord; i++) {
433 | sumKeys += currentWordsList[i].length + 1;
434 | }
435 | acc = acc = Math.min(Math.floor((correctKeys / sumKeys) * 100), 100);
436 | }
437 |
438 | let wpm = Math.floor(words / minute);
439 |
440 | document.querySelector(
441 | "#right-wing"
442 | ).innerHTML = `WPM: ${wpm} / ACC: ${acc}`;
443 | }
444 |
445 | // Key is pressed in input field (game logic)
446 | inputField.addEventListener("keydown", (e) => {
447 | // Add wrong class to input field
448 | switch (extensionState.mode) {
449 | case "words (fixed amount)":
450 | if (currentWord < currentWordsList.length) inputFieldClass();
451 | case "words (against the clock)":
452 | if (timerActive) inputFieldClass();
453 | }
454 | function inputFieldClass() {
455 | if (
456 | (e.key >= "a" && e.key <= "z") ||
457 | e.key === `'` ||
458 | e.key === "," ||
459 | e.key === "." ||
460 | e.key === ";"
461 | ) {
462 | let inputWordSlice = inputField.value + e.key;
463 |
464 | let currentWordSlice = currentWordsList[currentWord].slice(
465 | 0,
466 | inputWordSlice.length
467 | );
468 |
469 | inputField.className =
470 | inputWordSlice === currentWordSlice ? "" : "wrong";
471 | } else if (e.key === "Backspace") {
472 | let inputWordSlice = e.ctrlKey
473 | ? ""
474 | : inputField.value.slice(0, inputField.value.length - 1);
475 |
476 | let currentWordSlice = currentWordsList[currentWord].slice(
477 | 0,
478 | inputWordSlice.length
479 | );
480 |
481 | inputField.className =
482 | inputWordSlice === currentWordSlice ? "" : "wrong";
483 | } else if (e.key === " ") {
484 | inputField.className = "";
485 | }
486 | }
487 |
488 | // If it is the first character entered
489 | if (currentWord === 0 && inputField.value === "") {
490 | switch (extensionState.mode) {
491 | case "words (fixed amount)":
492 | startDate = Date.now();
493 | break;
494 |
495 | case "words (against the clock)":
496 | if (!timerActive) {
497 | startTimer(timeCount);
498 | timerActive = true;
499 | }
500 |
501 | function startTimer(time) {
502 | if (time > 0) {
503 | document.querySelector(`#tc-${timeCount}`).innerHTML = time;
504 | timer = setTimeout(() => {
505 | time--;
506 | startTimer(time);
507 | }, 1000);
508 | } else {
509 | timerActive = false;
510 |
511 | textDisplay.style.display = "none";
512 |
513 | inputField.className = "";
514 |
515 | document.querySelector(`#tc-${timeCount}`).innerHTML = timeCount;
516 | showResult();
517 | }
518 | }
519 | }
520 | }
521 |
522 | // If it is the space key check the word and add correct/wrong class
523 | if (e.key === " ") {
524 | e.preventDefault();
525 |
526 | if (inputField.value !== "") {
527 | // Scroll down text when reach new line
528 | if (extensionState.mode === "words (against the clock)") {
529 | const currentWordPosition =
530 | textDisplay.childNodes[currentWord].getBoundingClientRect();
531 | const nextWordPosition =
532 | textDisplay.childNodes[currentWord + 1].getBoundingClientRect();
533 | if (currentWordPosition.top < nextWordPosition.top) {
534 | for (let i = 0; i < currentWord + 1; i++)
535 | textDisplay.childNodes[i].style.display = "none";
536 | }
537 | }
538 |
539 | // If it is not the last word increment currentWord,
540 | if (currentWord < currentWordsList.length - 1) {
541 | if (inputField.value === currentWordsList[currentWord]) {
542 | textDisplay.childNodes[currentWord].classList.add("correct");
543 |
544 | correctKeys += currentWordsList[currentWord].length + 1;
545 | } else {
546 | textDisplay.childNodes[currentWord].classList.add("wrong");
547 | }
548 |
549 | textDisplay.childNodes[currentWord + 1].classList.add("highlight");
550 | } else if (currentWord === currentWordsList.length - 1) {
551 | textDisplay.childNodes[currentWord].classList.add("wrong");
552 | showResult();
553 | }
554 |
555 | inputField.value = "";
556 | currentWord++;
557 | }
558 |
559 | // Else if it is the last word and input word is correct show the result
560 | } else if (currentWord === currentWordsList.length - 1) {
561 | if (inputField.value + e.key === currentWordsList[currentWord]) {
562 | textDisplay.childNodes[currentWord].classList.add("correct");
563 |
564 | correctKeys += currentWordsList[currentWord].length;
565 | currentWord++;
566 | showResult();
567 | }
568 | }
569 | });
570 |
571 | // Command center actions
572 | document.querySelector("#wc-15").addEventListener("click", () => {
573 | setWordCount(15);
574 | setText();
575 | showText();
576 | });
577 | document.querySelector("#wc-30").addEventListener("click", () => {
578 | setWordCount(30);
579 | setText();
580 | showText();
581 | });
582 | document.querySelector("#wc-60").addEventListener("click", () => {
583 | setWordCount(60);
584 | setText();
585 | showText();
586 | });
587 | document.querySelector("#wc-120").addEventListener("click", () => {
588 | setWordCount(120);
589 | setText();
590 | showText();
591 | });
592 | document.querySelector("#wc-240").addEventListener("click", () => {
593 | setWordCount(240);
594 | setText();
595 | showText();
596 | });
597 | document.querySelector("#tc-15").addEventListener("click", () => {
598 | setTimeCount(15);
599 | setText();
600 | showText();
601 | });
602 | document.querySelector("#tc-30").addEventListener("click", () => {
603 | setTimeCount(30);
604 | setText();
605 | showText();
606 | });
607 | document.querySelector("#tc-60").addEventListener("click", () => {
608 | setTimeCount(60);
609 | setText();
610 | showText();
611 | });
612 | document.querySelector("#tc-120").addEventListener("click", () => {
613 | setTimeCount(120);
614 | setText();
615 | showText();
616 | });
617 | document.querySelector("#tc-240").addEventListener("click", () => {
618 | setTimeCount(240);
619 | setText();
620 | showText();
621 | });
622 |
623 | // Function to add punctuation to a list of words
624 | function addPunctuations() {
625 | if (currentWordsList[0] !== undefined) {
626 | // Capitalize first word
627 | currentWordsList[0] =
628 | currentWordsList[0][0].toUpperCase() + currentWordsList[0].slice(1);
629 |
630 | // Add comma, fullstop, question mark, exclamation mark, semicolon. Capitalize the next word
631 | for (let i = 0; i < currentWordsList.length; i++) {
632 | const ran = Math.random();
633 |
634 | if (i < currentWordsList.length - 1) {
635 | if (ran < 0.03) {
636 | currentWordsList[i] += ",";
637 | } else if (ran < 0.05) {
638 | currentWordsList[i] += ".";
639 |
640 | currentWordsList[i + 1] =
641 | currentWordsList[i + 1][0].toUpperCase() +
642 | currentWordsList[i + 1].slice(1);
643 | } else if (ran < 0.06) {
644 | currentWordsList[i] += "?";
645 |
646 | currentWordsList[i + 1] =
647 | currentWordsList[i + 1][0].toUpperCase() +
648 | currentWordsList[i + 1].slice(1);
649 | } else if (ran < 0.07) {
650 | currentWordsList[i] += "!";
651 |
652 | currentWordsList[i + 1] =
653 | currentWordsList[i + 1][0].toUpperCase() +
654 | currentWordsList[i + 1].slice(1);
655 | } else if (ran < 0.08) {
656 | currentWordsList[i] += ";";
657 | }
658 | }
659 | }
660 | currentWordsList[currentWordsList.length - 1] += ".";
661 | }
662 | }
663 |
664 | // Functions to change language setting
665 | function setLanguage(lang) {
666 | selectedLanguageWords = extensionState.allWords[lang];
667 | }
668 |
669 | // Function to change punctuation setting
670 | function setPunctuation(punct) {
671 | const punc = punct.toLowerCase();
672 | if (punc === "true") {
673 | punctuation = true;
674 | } else if (punc === "false") {
675 | punctuation = false;
676 | }
677 | }
678 |
679 | // Function to change word count setting
680 | function setWordCount(wc) {
681 | wordCount = wc;
682 | document
683 | .querySelectorAll("#word-count > span")
684 | .forEach((e) => (e.style.borderBottom = ""));
685 | document.querySelector(`#wc-${wordCount}`).style.borderBottom = "2px solid";
686 |
687 | // Change state
688 | vscode.setState({ ...extensionState, count: wordCount });
689 | extensionState = vscode.getState();
690 |
691 | // Send message to extension to update setting
692 | vscode.postMessage({
693 | command: "changeCount",
694 | count: wordCount,
695 | });
696 | }
697 |
698 | // Function to change time count setting
699 | function setTimeCount(tc) {
700 | timeCount = tc;
701 | document.querySelectorAll("#time-count > span").forEach((e) => {
702 | e.style.borderBottom = "";
703 | e.innerHTML = e.id.substring(3, 6);
704 | });
705 | document.querySelector(`#tc-${timeCount}`).style.borderBottom = "2px solid";
706 |
707 | // Change state
708 | vscode.setState({ ...extensionState, count: timeCount });
709 | extensionState = vscode.getState();
710 |
711 | // Send message to extension to update setting
712 | vscode.postMessage({
713 | command: "changeCount",
714 | count: timeCount,
715 | });
716 | }
717 |
718 | //====================================================
719 | // Code mode
720 | //====================================================
721 | // Function to set new code snippet and reset states
722 | function setCodeText(e) {
723 | document.querySelector("#language-selected").innerHTML =
724 | extensionState.codeLanguage.charAt(0).toUpperCase() +
725 | extensionState.codeLanguage.slice(1);
726 |
727 | e = e || window.event;
728 | var keepWordList = e && e.shiftKey;
729 |
730 | // Change code snippet if shift key is not hit
731 | if (!keepWordList) {
732 | currentCode =
733 | selectedLanguageCodes[
734 | Math.floor(Math.random() * selectedLanguageCodes.length)
735 | ];
736 | }
737 |
738 | // Reset progress state
739 | clearTimeout(timer);
740 | gameOver = false;
741 | codeState = {
742 | firstChar: null,
743 | lastChar: null,
744 | currentChar: null,
745 | currentCharNum: 0,
746 | cursorLeftOffset: 0,
747 | cursorTopOffset: 0,
748 | linesLastCursorPositions: [],
749 | };
750 |
751 | // Reset cursor position
752 | cursor.classList.remove("hidden");
753 | updateCursorPosition(0, 0);
754 |
755 | return;
756 | }
757 |
758 | // Function to set selection as new code snippet and reset states
759 | function setSelectedCodeText(selectedCode, selectedLanguage) {
760 | // Change code snippet
761 | currentCode = selectedCode;
762 | document.querySelector("#language-selected").innerHTML =
763 | selectedLanguage.charAt(0).toUpperCase() + selectedLanguage.slice(1);
764 |
765 | // Reset progress state
766 | clearTimeout(timer);
767 | gameOver = false;
768 | codeState = {
769 | firstChar: null,
770 | lastChar: null,
771 | currentChar: null,
772 | currentCharNum: 0,
773 | cursorLeftOffset: 0,
774 | cursorTopOffset: 0,
775 | linesLastCursorPositions: [],
776 | };
777 |
778 | // Reset cursor position
779 | cursor.classList.remove("hidden");
780 | updateCursorPosition(0, 0);
781 |
782 | return;
783 | }
784 |
785 | // Function to show the code snippet in the dom
786 | function showCodeText(userSelectedLanguage) {
787 | if (userSelectedLanguage) {
788 | highlightCode(currentCode, userSelectedLanguage);
789 | } else {
790 | highlightCode(currentCode, selectedLanguageName);
791 | }
792 |
793 | // Update state with the correct characters
794 | updateStateChars();
795 |
796 | // Focus into it
797 | document.getElementById("coding-area").focus();
798 | return;
799 | }
800 |
801 | // Function to show end results for code snippets mode
802 | function showCodeResults() {
803 | let numberOfCharacters = document.querySelectorAll(".char").length;
804 | let numberOfCorrectTypings = document.querySelectorAll(".passed").length;
805 |
806 | let time = (Date.now() - codeStartDate) / 1000 / 60;
807 | let words = numberOfCorrectTypings / 5;
808 |
809 | let wpm = Math.floor(words / time);
810 | let acc = Math.floor((numberOfCorrectTypings / numberOfCharacters) * 100);
811 |
812 | document.querySelector(
813 | "#right-wing"
814 | ).innerHTML = `WPM: ${wpm} / ACC: ${acc}`;
815 |
816 | return;
817 | }
818 |
819 | // Retrieve cursor dimensions from css
820 | let cursorWidth = 0;
821 | let cursorHeight = 0;
822 | setTimeout(() => {
823 | // Gets the actual rendered width of the char
824 | cursorWidth = document
825 | .querySelectorAll(".char")[0]
826 | .getBoundingClientRect().width;
827 | cursorHeight = document
828 | .querySelectorAll(".char")[0]
829 | .getBoundingClientRect().height;
830 | }, 100); // Delay ensures that the data is read after rendering
831 |
832 | // Add event listeners for key presses
833 | document.addEventListener("keydown", (e) => handleKeyDown(e));
834 | document.addEventListener("keypress", (e) => handleKeyPress(e));
835 |
836 | // Function to set code language
837 | function setCodeLanguage(lang) {
838 | selectedLanguageName = lang;
839 | selectedLanguageCodes = allCodes[lang];
840 | return;
841 | }
842 |
843 | // Function to update characters in the state
844 | function updateStateChars() {
845 | const toPassSymbols = document.getElementsByClassName("topass");
846 | codeState = {
847 | ...codeState,
848 | firstChar: toPassSymbols[0],
849 | currentChar: toPassSymbols[0],
850 | lastChar: toPassSymbols[toPassSymbols.length - 1],
851 | };
852 | }
853 |
854 | // Function that sets highlighted code in dom
855 | function highlightCode(codeSnippet, language) {
856 | let codeDiv = document.getElementById("code-code");
857 |
858 | document.getElementById("code-pre").className = "";
859 | document.getElementById("code-code").className = "";
860 |
861 | codeDiv.classList.add(`language-${language}`);
862 | codeDiv.innerHTML = codeSnippet;
863 |
864 | Prism.highlightElement(codeDiv);
865 |
866 | codeDiv.innerHTML = cutCodeIntoPieces(codeDiv.innerHTML);
867 | }
868 |
869 | // Function that cuts highlighted code into spans of characters
870 | function cutCodeIntoPieces(highlightedCode) {
871 | const regexpTag = /(<\/?span.*?>)/;
872 | const tagsAndTextArr = highlightedCode.split(regexpTag);
873 | const regexpSpecialChar = /&[a-z]*;/;
874 | let codeToRender = "";
875 |
876 | // Wrap code characters with
877 | for (let i = 0; i < tagsAndTextArr.length; i++) {
878 | // If text element, wrap each symbol with span
879 | if (tagsAndTextArr[i] !== "" && !regexpTag.test(tagsAndTextArr[i])) {
880 | let newHtml = "";
881 | if (regexpSpecialChar.test(tagsAndTextArr[i])) {
882 | // Special characters
883 | const specialCharsArr = tagsAndTextArr[i].match(/&[a-z]*;/g);
884 | // If we have one special character without other symbols
885 | if (
886 | specialCharsArr.length === 1 &&
887 | specialCharsArr[0] === tagsAndTextArr[i]
888 | ) {
889 | newHtml += `${tagsAndTextArr[i]}`;
890 | // If we have a special character with other symbols
891 | } else {
892 | const otherCharsArr = tagsAndTextArr[i].split(regexpSpecialChar);
893 | for (let j = 0; j < otherCharsArr.length; j++) {
894 | if (otherCharsArr[j] === "" && j < specialCharsArr.length) {
895 | newHtml += `${specialCharsArr[0]}`;
896 | continue;
897 | }
898 | for (let k = 0; k < otherCharsArr[j].length; k++) {
899 | newHtml += `${otherCharsArr[j][k]}`;
900 | }
901 | if (j !== otherCharsArr.length - 1) {
902 | newHtml += `${specialCharsArr[0]}`;
903 | }
904 | }
905 | }
906 | } else {
907 | // Simple words and symbols
908 | for (let j = 0; j < tagsAndTextArr[i].length; j++) {
909 | newHtml += `${tagsAndTextArr[i][j]}`;
910 | }
911 | }
912 | tagsAndTextArr[i] = newHtml;
913 | }
914 | codeToRender += tagsAndTextArr[i];
915 | }
916 |
917 | return codeToRender;
918 | }
919 |
920 | // Function that returns the next character to the cursor
921 | function getNextChar() {
922 | return document.getElementsByClassName("char")[
923 | codeState.currentCharNum + 1
924 | ];
925 | }
926 |
927 | // Function that returns the previous character to the cursor
928 | function getPrevChar() {
929 | return document.getElementsByClassName("char")[
930 | codeState.currentCharNum - 1
931 | ];
932 | }
933 |
934 | // Function that handles "tab" and "backspace" key presses
935 | function handleKeyDown(e) {
936 | if (gameOver) {
937 | e.submit();
938 | }
939 |
940 | // If it's the first character, start timer
941 | if (codeState.currentCharNum === 0) {
942 | codeStartDate = Date.now();
943 | }
944 |
945 | // Tab: move cursor further
946 | if (e.which === 9) {
947 | e.preventDefault();
948 | const currentChar = codeState.currentChar;
949 | const currentCharCode = currentChar.innerText.charCodeAt(0);
950 |
951 | // If the current symbol is a tab character
952 | if (currentCharCode === 9) {
953 | handleKeyPress(e);
954 | }
955 |
956 | // If the current symbol is a tab consisting of spaces
957 | if (currentCharCode === 32) {
958 | // Count all next spaces
959 | let counter = 0;
960 | let summToAdd = 0;
961 | let currentEl = currentChar;
962 |
963 | // Calculate the distance to move the cursor and change classes of passed characters
964 | while (currentEl.innerText.charCodeAt(0) === 32) {
965 | summToAdd += cursorWidth;
966 | currentEl.classList.remove("topass");
967 | currentEl.classList.add("passed");
968 | currentEl = currentEl.nextElementSibling;
969 | counter++;
970 | }
971 |
972 | // Change state depending on how much spaces we have passed
973 | if (counter === 1) {
974 | // Single space just for space
975 | flashCursor();
976 | } else {
977 | // Move cursor through spaces
978 | codeState = {
979 | ...codeState,
980 | currentCharNum: codeState.currentCharNum + (counter - 1),
981 | };
982 | codeState = {
983 | ...codeState,
984 | cursorLeftOffset: codeState.cursorLeftOffset + summToAdd,
985 | currentChar: getNextChar(),
986 | currentCharNum: codeState.currentCharNum + 1,
987 | };
988 | updateCursorPosition(
989 | codeState.cursorLeftOffset,
990 | codeState.cursorTopOffset
991 | );
992 | }
993 | }
994 | }
995 |
996 | // Backspace: move cursor back
997 | if (e.key === "Backspace") {
998 | // If first element is reached, ignore
999 | if (codeState.currentChar === codeState.firstChar) {
1000 | flashCursor();
1001 | return;
1002 | }
1003 |
1004 | // Else find out where we are and change state
1005 | const currentChar = getPrevChar();
1006 | const currentCharCode = currentChar.innerText.charCodeAt(0);
1007 |
1008 | codeState = { ...codeState, currentChar: currentChar };
1009 | currentChar.classList.remove("notpassed");
1010 | currentChar.classList.add("topass");
1011 |
1012 | // If we are at the beginning of the line, go to the previous line
1013 | if (currentCharCode === 10) {
1014 | const linesLastCursorPositions = codeState.linesLastCursorPositions;
1015 |
1016 | codeState = {
1017 | ...codeState,
1018 | cursorLeftOffset: linesLastCursorPositions.pop(),
1019 | cursorTopOffset: codeState.cursorTopOffset - cursorHeight,
1020 | linesLastCursorPositions,
1021 | currentCharNum: codeState.currentCharNum - 1,
1022 | };
1023 | updateCursorPosition(
1024 | codeState.cursorLeftOffset,
1025 | codeState.cursorTopOffset
1026 | );
1027 |
1028 | return;
1029 | }
1030 |
1031 | // If it's the same line, go one back
1032 | codeState = {
1033 | ...codeState,
1034 | cursorLeftOffset: codeState.cursorLeftOffset - cursorWidth,
1035 | currentCharNum: codeState.currentCharNum - 1,
1036 | };
1037 | updateCursorPosition(
1038 | codeState.cursorLeftOffset,
1039 | codeState.cursorTopOffset
1040 | );
1041 | }
1042 | }
1043 |
1044 | // Function that handles all the other key presses
1045 | function handleKeyPress(e) {
1046 | if (gameOver) {
1047 | e.submit();
1048 | }
1049 |
1050 | // Other keys: change state depending on the key pressed
1051 | e.preventDefault();
1052 |
1053 | const currentChar = codeState.currentChar;
1054 | const typedSymbolCode = e.which;
1055 | const currentCharCode = currentChar.innerText.charCodeAt(0);
1056 |
1057 | // If the current symbol is a new line, do nothing if 'enter' not hit
1058 | if (currentCharCode === 10 && typedSymbolCode !== 13) {
1059 | flashCursor();
1060 | return;
1061 | }
1062 |
1063 | // If the current symbol is not new line, do nothing if 'enter' pressed
1064 | if (currentCharCode !== 10 && typedSymbolCode === 13) {
1065 | flashCursor();
1066 | return;
1067 | }
1068 |
1069 | // Change classes of passed characters
1070 | currentChar.classList.remove("topass");
1071 |
1072 | // Change class depending if you typed correct or wrong
1073 | if (typedSymbolCode === currentCharCode) {
1074 | currentChar.classList.add("passed");
1075 | } else {
1076 | currentChar.classList.add("notpassed");
1077 | }
1078 |
1079 | // If last symbol reached, hide cursor and show stats
1080 | if (codeState.currentChar === codeState.lastChar) {
1081 | cursor.classList.add("hidden");
1082 |
1083 | showCodeResults();
1084 | gameOver = true;
1085 | return;
1086 | }
1087 |
1088 | // Else, get next symbol and set it as current
1089 | const next = getNextChar();
1090 | codeState = { ...codeState, currentChar: next };
1091 |
1092 | // Moving the cursor to the next position
1093 |
1094 | // If it's new line
1095 | if (currentCharCode === 10 && typedSymbolCode === 13) {
1096 | const linesLastCursorPositions = codeState.linesLastCursorPositions;
1097 | linesLastCursorPositions.push(codeState.cursorLeftOffset);
1098 | codeState = {
1099 | ...codeState,
1100 | cursorLeftOffset: 0,
1101 | cursorTopOffset: codeState.cursorTopOffset + cursorHeight,
1102 | linesLastCursorPositions,
1103 | currentCharNum: codeState.currentCharNum + 1,
1104 | };
1105 | updateCursorPosition(
1106 | codeState.cursorLeftOffset,
1107 | codeState.cursorTopOffset
1108 | );
1109 |
1110 | return;
1111 | }
1112 |
1113 | // If tab symbol is reached
1114 | if (currentCharCode === 9) {
1115 | codeState = {
1116 | ...codeState,
1117 | cursorLeftOffset: codeState.cursorLeftOffset + cursorWidth,
1118 | currentCharNum: codeState.currentCharNum + 1,
1119 | };
1120 | updateCursorPosition(
1121 | codeState.cursorLeftOffset,
1122 | codeState.cursorTopOffset
1123 | );
1124 |
1125 | return;
1126 | }
1127 |
1128 | // If it's the same line
1129 | codeState = {
1130 | ...codeState,
1131 | cursorLeftOffset: codeState.cursorLeftOffset + cursorWidth,
1132 | currentCharNum: codeState.currentCharNum + 1,
1133 | };
1134 | updateCursorPosition(codeState.cursorLeftOffset, codeState.cursorTopOffset);
1135 | }
1136 |
1137 | // Function to update cursor position in the dom
1138 | function updateCursorPosition(left, top) {
1139 | cursor.style.left = `${left}px `;
1140 | cursor.style.top = `${top}px`;
1141 | cursor.scrollIntoView({
1142 | behavior: "smooth",
1143 | block: "center",
1144 | inline: "center",
1145 | });
1146 | }
1147 |
1148 | // Function to visually flash the cursor
1149 | function flashCursor() {
1150 | cursor.style.background = "#e0556170";
1151 | setTimeout(() => {
1152 | cursor.style.background = "#5dbeff";
1153 | }, 100);
1154 | }
1155 | })();
1156 |
--------------------------------------------------------------------------------