js_lis 1.0.24 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VirtualKeyboard.js +293 -303
- package/layouts.js +23 -35
- package/package.json +1 -1
package/VirtualKeyboard.js
CHANGED
|
@@ -2,7 +2,7 @@ import { layouts } from "./layouts.js";
|
|
|
2
2
|
|
|
3
3
|
export class VirtualKeyboard {
|
|
4
4
|
constructor() {
|
|
5
|
-
this.currentLayout = "
|
|
5
|
+
this.currentLayout = "full";
|
|
6
6
|
this.isVisible = false;
|
|
7
7
|
this.container = document.getElementById("keyboard-container");
|
|
8
8
|
this.currentInput = null;
|
|
@@ -12,10 +12,7 @@ export class VirtualKeyboard {
|
|
|
12
12
|
this.offsetY = 0;
|
|
13
13
|
this.shiftActive = false;
|
|
14
14
|
this.capsLockActive = false;
|
|
15
|
-
|
|
16
|
-
// กำหนดคีย์และ IV สำหรับการเข้ารหัส
|
|
17
|
-
this.secretKey = "1234567890abcdef1234567890abcdef"; // คีย์ความยาว 32 bytes
|
|
18
|
-
this.iv = CryptoJS.lib.WordArray.random(16); // สร้าง IV ความยาว 16 bytes
|
|
15
|
+
this.isScrambled = false;
|
|
19
16
|
|
|
20
17
|
this.initialize();
|
|
21
18
|
}
|
|
@@ -30,62 +27,16 @@ export class VirtualKeyboard {
|
|
|
30
27
|
}
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
encodeText(text) {
|
|
34
|
-
const encrypted = CryptoJS.AES.encrypt(
|
|
35
|
-
text,
|
|
36
|
-
CryptoJS.enc.Hex.parse(this.secretKey),
|
|
37
|
-
{
|
|
38
|
-
iv: this.iv,
|
|
39
|
-
mode: CryptoJS.mode.CBC,
|
|
40
|
-
padding: CryptoJS.pad.Pkcs7,
|
|
41
|
-
}
|
|
42
|
-
);
|
|
43
|
-
return {
|
|
44
|
-
encrypted: encrypted.ciphertext.toString(CryptoJS.enc.Base64),
|
|
45
|
-
iv: this.iv.toString(CryptoJS.enc.Base64),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
decodeText(encryptedData) {
|
|
50
|
-
if (!encryptedData || !encryptedData.encrypted || !encryptedData.iv) {
|
|
51
|
-
if (encryptedData === "\t") {
|
|
52
|
-
return "\t";
|
|
53
|
-
} else {
|
|
54
|
-
return " ";
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
const decrypted = CryptoJS.AES.decrypt(
|
|
60
|
-
{ ciphertext: CryptoJS.enc.Base64.parse(encryptedData.encrypted) },
|
|
61
|
-
CryptoJS.enc.Hex.parse(this.secretKey),
|
|
62
|
-
{
|
|
63
|
-
iv: CryptoJS.enc.Base64.parse(encryptedData.iv),
|
|
64
|
-
mode: CryptoJS.mode.CBC,
|
|
65
|
-
padding: CryptoJS.pad.Pkcs7,
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
return decrypted.toString(CryptoJS.enc.Utf8);
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.error("Error decoding text:", error);
|
|
71
|
-
return " ";
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
30
|
getLayoutName(layout) {
|
|
76
31
|
switch (layout) {
|
|
32
|
+
case "full":
|
|
33
|
+
return "Full Keyboard";
|
|
77
34
|
case "en":
|
|
78
35
|
return "English Keyboard";
|
|
79
|
-
case "enSc":
|
|
80
|
-
return "English scrambled";
|
|
81
36
|
case "th":
|
|
82
37
|
return "Thai keyboard";
|
|
83
|
-
case "thSc":
|
|
84
|
-
return "Thai scrambled";
|
|
85
38
|
case "numpad":
|
|
86
39
|
return "Numpad Keyboard";
|
|
87
|
-
case "scNum":
|
|
88
|
-
return "Scrambled Keyboard";
|
|
89
40
|
default:
|
|
90
41
|
return "Unknown Layout";
|
|
91
42
|
}
|
|
@@ -125,9 +76,10 @@ export class VirtualKeyboard {
|
|
|
125
76
|
this.currentInput.classList.add("keyboard-active");
|
|
126
77
|
}
|
|
127
78
|
|
|
79
|
+
// [1]
|
|
128
80
|
render() {
|
|
129
81
|
const keyboard = document.createElement("div");
|
|
130
|
-
keyboard.className =
|
|
82
|
+
keyboard.className = `virtual-keyboard ${this.currentLayout}`;
|
|
131
83
|
keyboard.style.display = this.isVisible ? "block" : "none";
|
|
132
84
|
keyboard.id = "keyboard";
|
|
133
85
|
|
|
@@ -142,7 +94,7 @@ export class VirtualKeyboard {
|
|
|
142
94
|
layoutSelector.id = "layout-selector";
|
|
143
95
|
layoutSelector.onchange = (e) => this.changeLayout(e.target.value);
|
|
144
96
|
|
|
145
|
-
const layouts = ["
|
|
97
|
+
const layouts = ["full", "en", "th", "numpad"];
|
|
146
98
|
layouts.forEach((layout) => {
|
|
147
99
|
const option = document.createElement("option");
|
|
148
100
|
option.value = layout;
|
|
@@ -160,7 +112,7 @@ export class VirtualKeyboard {
|
|
|
160
112
|
const rowElement = document.createElement("div");
|
|
161
113
|
rowElement.className = "keyboard-row";
|
|
162
114
|
|
|
163
|
-
row.forEach((key) => {
|
|
115
|
+
row.forEach((key, index) => {
|
|
164
116
|
const keyElement = document.createElement("button");
|
|
165
117
|
keyElement.className = "keyboard-key key";
|
|
166
118
|
keyElement.textContent = key;
|
|
@@ -168,6 +120,10 @@ export class VirtualKeyboard {
|
|
|
168
120
|
|
|
169
121
|
keyElement.dataset.key = key;
|
|
170
122
|
|
|
123
|
+
if (index >= row.length - 4) {
|
|
124
|
+
keyElement.classList.add("concat-keys");
|
|
125
|
+
}
|
|
126
|
+
|
|
171
127
|
if (key === "Space") {
|
|
172
128
|
keyElement.className += " space";
|
|
173
129
|
}
|
|
@@ -196,24 +152,12 @@ export class VirtualKeyboard {
|
|
|
196
152
|
this.container.innerHTML = "";
|
|
197
153
|
this.container.appendChild(keyboard);
|
|
198
154
|
|
|
199
|
-
if (this.currentLayout === "scNum") {
|
|
200
|
-
this.scrambleKeyboard();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (this.currentLayout === "enSc") {
|
|
204
|
-
this.scrambleEnglishKeys();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (this.currentLayout === "thSc") {
|
|
208
|
-
this.scrambleThaiKeys();
|
|
209
|
-
}
|
|
210
|
-
|
|
211
155
|
keyboard.addEventListener("mousedown", (event) => this.startDrag(event));
|
|
212
156
|
}
|
|
213
157
|
|
|
158
|
+
// [2]
|
|
214
159
|
async handleKeyPress(keyPressed) {
|
|
215
160
|
if (!this.currentInput) return;
|
|
216
|
-
|
|
217
161
|
const start = this.currentInput.selectionStart;
|
|
218
162
|
const end = this.currentInput.selectionEnd;
|
|
219
163
|
const value = this.currentInput.value;
|
|
@@ -228,23 +172,158 @@ export class VirtualKeyboard {
|
|
|
228
172
|
return char.toLowerCase();
|
|
229
173
|
};
|
|
230
174
|
|
|
231
|
-
if (!keyPressed)
|
|
232
|
-
|
|
175
|
+
if (!keyPressed) return console.error("Invalid key pressed.");
|
|
176
|
+
|
|
177
|
+
if (keyPressed === "scr" || keyPressed === "scrambled") {
|
|
178
|
+
if (this.currentLayout === "en") {
|
|
179
|
+
if (this.isScrambled) {
|
|
180
|
+
this.unscrambleEnglishKeys();
|
|
181
|
+
} else {
|
|
182
|
+
this.scrambleEnglishKeys();
|
|
183
|
+
}
|
|
184
|
+
this.isScrambled = !this.isScrambled;
|
|
185
|
+
document
|
|
186
|
+
.querySelectorAll('.key[data-key="scrambled"]')
|
|
187
|
+
.forEach((key) => {
|
|
188
|
+
key.classList.toggle("active", this.isScrambled);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (this.currentLayout === "th") {
|
|
193
|
+
if (this.isScrambled) {
|
|
194
|
+
this.unscrambleEnglishKeys();
|
|
195
|
+
} else {
|
|
196
|
+
this.scrambleThaiKeys();
|
|
197
|
+
}
|
|
198
|
+
this.isScrambled = !this.isScrambled;
|
|
199
|
+
document
|
|
200
|
+
.querySelectorAll('.key[data-key="scrambled"]')
|
|
201
|
+
.forEach((key) => {
|
|
202
|
+
key.classList.toggle("active", this.isScrambled);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (this.currentLayout === "numpad") {
|
|
207
|
+
if (this.isScrambled) {
|
|
208
|
+
this.unscrambleEnglishKeys();
|
|
209
|
+
} else {
|
|
210
|
+
this.scrambleKeyboard();
|
|
211
|
+
}
|
|
212
|
+
this.isScrambled = !this.isScrambled;
|
|
213
|
+
document.querySelectorAll('.key[data-key="scr"]').forEach((key) => {
|
|
214
|
+
key.classList.toggle("active", this.isScrambled);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
233
217
|
return;
|
|
234
218
|
}
|
|
235
219
|
|
|
236
220
|
switch (keyPressed) {
|
|
221
|
+
case "Esc":
|
|
222
|
+
const modals = document.querySelectorAll(".modal");
|
|
223
|
+
if (modals.length === 0) {
|
|
224
|
+
document.exitFullscreen();
|
|
225
|
+
console.warn("No modals found to close.");
|
|
226
|
+
}
|
|
227
|
+
modals.forEach((modal) => {
|
|
228
|
+
modal.classList.add("hidden");
|
|
229
|
+
});
|
|
230
|
+
break;
|
|
231
|
+
|
|
232
|
+
case "F1":
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
case "F2":
|
|
236
|
+
// เปิดโหมดแก้ไขสำหรับ element ที่เลือก
|
|
237
|
+
const activeElement = document.activeElement;
|
|
238
|
+
if (activeElement && activeElement.contentEditable !== undefined) {
|
|
239
|
+
activeElement.contentEditable = true;
|
|
240
|
+
activeElement.focus();
|
|
241
|
+
console.log(activeElement.contentEditable);
|
|
242
|
+
} else {
|
|
243
|
+
console.warn("No editable element found.");
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case "F3":
|
|
248
|
+
// เปิดการค้นหา
|
|
249
|
+
event.preventDefault();
|
|
250
|
+
this.option.openSearch();
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
case "F4":
|
|
254
|
+
// เปิดเมนูการตั้งค่า
|
|
255
|
+
event.preventDefault();
|
|
256
|
+
this.option.openSettings();
|
|
257
|
+
break;
|
|
258
|
+
|
|
259
|
+
case "F5":
|
|
260
|
+
// รีโหลดหน้าเว็บ (คงเดิม)
|
|
261
|
+
window.location.reload();
|
|
262
|
+
break;
|
|
263
|
+
|
|
264
|
+
case "F6":
|
|
265
|
+
// สลับระหว่างโหมดกลางวัน/กลางคืน
|
|
266
|
+
document.body.classList.toggle("dark-mode");
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
case "F7":
|
|
270
|
+
break;
|
|
271
|
+
|
|
272
|
+
case "F8":
|
|
273
|
+
break;
|
|
274
|
+
|
|
275
|
+
case "F9":
|
|
276
|
+
break;
|
|
277
|
+
|
|
278
|
+
case "F10":
|
|
279
|
+
break;
|
|
280
|
+
|
|
281
|
+
case "F11":
|
|
282
|
+
if (!document.fullscreenElement) {
|
|
283
|
+
document.documentElement
|
|
284
|
+
.requestFullscreen()
|
|
285
|
+
.catch((err) =>
|
|
286
|
+
console.error("Error attempting to enable fullscreen:", err)
|
|
287
|
+
);
|
|
288
|
+
} else {
|
|
289
|
+
document
|
|
290
|
+
.exitFullscreen()
|
|
291
|
+
.catch((err) =>
|
|
292
|
+
console.error("Error attempting to exit fullscreen:", err)
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
|
|
297
|
+
case "F12":
|
|
298
|
+
break;
|
|
299
|
+
|
|
300
|
+
case "HOME":
|
|
301
|
+
this.currentInput.setSelectionRange(Math.max(0, 0), Math.max(0, 0));
|
|
302
|
+
break;
|
|
303
|
+
|
|
304
|
+
case "END":
|
|
305
|
+
const length = this.currentInput.value.length;
|
|
306
|
+
this.currentInput.setSelectionRange(length, length);
|
|
307
|
+
break;
|
|
308
|
+
|
|
237
309
|
case "Backspace":
|
|
238
310
|
case "backspace":
|
|
239
311
|
if (start === end && start > 0) {
|
|
240
|
-
this.currentInput.value =
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
312
|
+
this.currentInput.value = value.slice(0, start - 1) + value.slice(end);
|
|
313
|
+
this.currentInput.selectionStart = this.currentInput.selectionEnd = start - 1;
|
|
314
|
+
} else {
|
|
315
|
+
this.currentInput.value = value.slice(0, start) + value.slice(end);
|
|
316
|
+
this.currentInput.selectionStart = this.currentInput.selectionEnd = start;
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case "DEL⌦":
|
|
321
|
+
if (start === end && start < value.length) {
|
|
322
|
+
this.currentInput.value = value.slice(0, start) + value.slice(end + 1);
|
|
323
|
+
this.currentInput.selectionStart = this.currentInput.selectionEnd = start;
|
|
244
324
|
} else {
|
|
245
325
|
this.currentInput.value = value.slice(0, start) + value.slice(end);
|
|
246
|
-
this.currentInput.selectionStart = this.currentInput.selectionEnd =
|
|
247
|
-
start;
|
|
326
|
+
this.currentInput.selectionStart = this.currentInput.selectionEnd = start;
|
|
248
327
|
}
|
|
249
328
|
break;
|
|
250
329
|
|
|
@@ -252,290 +331,203 @@ export class VirtualKeyboard {
|
|
|
252
331
|
await this.insertText(" ");
|
|
253
332
|
break;
|
|
254
333
|
|
|
255
|
-
case "Tab":
|
|
334
|
+
case "Tab ↹":
|
|
256
335
|
await this.insertText("\t");
|
|
257
336
|
break;
|
|
258
337
|
|
|
259
|
-
case
|
|
338
|
+
case "Enter":
|
|
260
339
|
if (this.currentInput.tagName === "TEXTAREA") {
|
|
261
|
-
const start = this.currentInput.selectionStart;
|
|
262
|
-
const end = this.currentInput.selectionEnd;
|
|
263
|
-
const value = this.currentInput.value;
|
|
264
340
|
this.currentInput.value = value.slice(0, start) + "\n" + value.slice(end);
|
|
265
341
|
this.currentInput.setSelectionRange(start + 1, start + 1);
|
|
266
|
-
} else if (this.currentInput.tagName === "INPUT" || this.currentInput.type === "password" || this.currentInput.type === "text") {
|
|
342
|
+
} else if (this.currentInput.tagName === "INPUT" || this.currentInput.type === "password" || this.currentInput.type === "text" ) {
|
|
267
343
|
if (this.currentInput.form) {
|
|
268
|
-
const submitButton = this.currentInput.form.querySelector(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
this.currentInput.form.submit();
|
|
273
|
-
}
|
|
344
|
+
const submitButton = this.currentInput.form.querySelector(
|
|
345
|
+
'input[type="submit"], button[type="submit"], button[type="button"], button[onclick]'
|
|
346
|
+
);
|
|
347
|
+
if (submitButton) submitButton.click(); else this.currentInput.form.submit();
|
|
274
348
|
} else {
|
|
275
|
-
this.currentInput.value +=
|
|
349
|
+
this.currentInput.value += "\n";
|
|
276
350
|
}
|
|
277
351
|
}
|
|
278
352
|
break;
|
|
279
353
|
|
|
280
|
-
case "Caps":
|
|
354
|
+
case "Caps 🄰":
|
|
281
355
|
this.toggleCapsLock();
|
|
282
356
|
break;
|
|
283
357
|
|
|
284
|
-
case "Shift":
|
|
358
|
+
case "Shift ⇧":
|
|
285
359
|
this.toggleShift();
|
|
286
360
|
break;
|
|
287
361
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
362
|
+
case "←":
|
|
363
|
+
this.currentInput.setSelectionRange(
|
|
364
|
+
Math.max(0, start - 1),
|
|
365
|
+
Math.max(0, start - 1)
|
|
291
366
|
);
|
|
292
|
-
|
|
293
|
-
}
|
|
367
|
+
break;
|
|
294
368
|
|
|
295
|
-
|
|
296
|
-
|
|
369
|
+
case "→":
|
|
370
|
+
this.currentInput.setSelectionRange(start + 1, start + 1);
|
|
371
|
+
break;
|
|
372
|
+
|
|
373
|
+
case "↑":
|
|
374
|
+
case "↓":
|
|
375
|
+
const text = this.currentInput.value;
|
|
376
|
+
const lines = text.substring(0, start).split("\n");
|
|
377
|
+
const currentLineIndex = lines.length - 1;
|
|
378
|
+
const currentLine = lines[currentLineIndex];
|
|
379
|
+
const columnIndex = start - text.lastIndexOf("\n", start - 1) - 1;
|
|
380
|
+
|
|
381
|
+
if (keyPressed === "↑" && currentLineIndex > 0) {
|
|
382
|
+
const prevLineLength = lines[currentLineIndex - 1].length;
|
|
383
|
+
const newPos = start - currentLine.length - 1 - Math.min(columnIndex, prevLineLength);
|
|
384
|
+
this.currentInput.setSelectionRange(newPos, newPos);
|
|
385
|
+
} else if (keyPressed === "↓" && currentLineIndex < lines.length - 1) {
|
|
386
|
+
const nextLine = lines[currentLineIndex + 1];
|
|
387
|
+
const newPos = start + currentLine.length + 1 + Math.min(columnIndex, nextLine.length);
|
|
388
|
+
this.currentInput.setSelectionRange(newPos, newPos);
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
|
|
392
|
+
default:
|
|
393
|
+
const textvalue = await convertToCorrectCase(keyPressed);
|
|
394
|
+
await this.insertText(textvalue);
|
|
297
395
|
}
|
|
298
396
|
|
|
397
|
+
if (isShiftActive && !isCapsActive) this.toggleShift();
|
|
398
|
+
|
|
299
399
|
this.currentInput.focus();
|
|
300
400
|
const event = new Event("input", { bubbles: true });
|
|
301
401
|
this.currentInput.dispatchEvent(event);
|
|
302
|
-
// console.log(this.encodeText(convertToCorrectCase(keyPressed)));
|
|
303
|
-
// console.log(keyPressed);
|
|
304
402
|
}
|
|
305
403
|
|
|
404
|
+
// [3]
|
|
306
405
|
async insertText(text) {
|
|
307
406
|
const start = this.currentInput.selectionStart;
|
|
308
407
|
const end = this.currentInput.selectionEnd;
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
this.currentInput.value =
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
408
|
+
const textvalue = text;
|
|
409
|
+
|
|
410
|
+
this.currentInput.value = this.currentInput.value.slice(0, start) + textvalue + this.currentInput.value.slice(end);
|
|
411
|
+
this.currentInput.selectionStart = this.currentInput.selectionEnd = start + textvalue.length;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// [4]
|
|
415
|
+
unscrambleEnglishKeys() {
|
|
416
|
+
this.keys = this.originalKeys;
|
|
417
|
+
this.render();
|
|
317
418
|
}
|
|
318
419
|
|
|
420
|
+
// [5]
|
|
319
421
|
toggleCapsLock() {
|
|
320
422
|
this.capsLockActive = !this.capsLockActive;
|
|
321
|
-
|
|
423
|
+
|
|
424
|
+
document.querySelectorAll('.key[data-key="Caps 🄰"]').forEach((key) => {
|
|
322
425
|
key.classList.toggle("active", this.capsLockActive);
|
|
323
426
|
key.classList.toggle("bg-gray-400", this.capsLockActive);
|
|
324
427
|
});
|
|
325
428
|
|
|
326
429
|
document.querySelectorAll(".key").forEach((key) => {
|
|
327
|
-
|
|
430
|
+
const isLetter = key.dataset.key.length === 1 && /[a-zA-Zก-๙]/.test(key.dataset.key);
|
|
431
|
+
|
|
432
|
+
if (isLetter) {
|
|
328
433
|
key.textContent = this.capsLockActive
|
|
329
434
|
? key.dataset.key.toUpperCase()
|
|
330
435
|
: key.dataset.key.toLowerCase();
|
|
331
436
|
}
|
|
437
|
+
this.updateKeyContent(key, this.capsLockActive);
|
|
332
438
|
});
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if (originalKey) {
|
|
356
|
-
key.textContent = originalKey;
|
|
357
|
-
key.dataset.key = originalKey;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (
|
|
362
|
-
this.capsLockActive &&
|
|
363
|
-
this.currentLayout === "en" &&
|
|
364
|
-
this.EngAlphabetShift[currentChar]
|
|
365
|
-
) {
|
|
366
|
-
key.textContent = this.EngAlphabetShift[currentChar];
|
|
367
|
-
key.dataset.key = this.EngAlphabetShift[currentChar];
|
|
368
|
-
} else if (
|
|
369
|
-
!this.capsLockActive &&
|
|
370
|
-
this.currentLayout === "en" &&
|
|
371
|
-
Object.values(this.EngAlphabetShift).includes(currentChar)
|
|
372
|
-
) {
|
|
373
|
-
// เปลี่ยนกลับเมื่อปิด Shift
|
|
374
|
-
const originalKey = Object.keys(this.EngAlphabetShift).find(
|
|
375
|
-
(key) => this.EngAlphabetShift[key] === currentChar
|
|
376
|
-
);
|
|
377
|
-
if (originalKey) {
|
|
378
|
-
key.textContent = originalKey;
|
|
379
|
-
key.dataset.key = originalKey;
|
|
380
|
-
}
|
|
439
|
+
}
|
|
440
|
+
updateKeyContent(key, capsLockActive) {
|
|
441
|
+
const currentChar = key.textContent.trim();
|
|
442
|
+
|
|
443
|
+
const layouts = {
|
|
444
|
+
th: this.ThaiAlphabetShift,
|
|
445
|
+
en: this.EngAlphabetShift,
|
|
446
|
+
enSc: this.EngAlphabetShift,
|
|
447
|
+
full: this.FullAlphabetShift,
|
|
448
|
+
};
|
|
449
|
+
const layout = layouts[this.currentLayout];
|
|
450
|
+
|
|
451
|
+
if (!layout) return;
|
|
452
|
+
|
|
453
|
+
if (capsLockActive && layout[currentChar]) {
|
|
454
|
+
key.textContent = layout[currentChar];
|
|
455
|
+
key.dataset.key = layout[currentChar];
|
|
456
|
+
} else if (!capsLockActive && Object.values(layout).includes(currentChar)) {
|
|
457
|
+
const originalKey = Object.keys(layout).find((k) => layout[k] === currentChar);
|
|
458
|
+
if (originalKey) {
|
|
459
|
+
key.textContent = originalKey;
|
|
460
|
+
key.dataset.key = originalKey;
|
|
381
461
|
}
|
|
382
|
-
}
|
|
462
|
+
}
|
|
383
463
|
}
|
|
384
464
|
|
|
465
|
+
// [6]
|
|
385
466
|
toggleShift() {
|
|
386
467
|
if (this.capsLockActive) {
|
|
387
468
|
return this.toggleCapsLock();
|
|
388
469
|
}
|
|
389
470
|
|
|
390
471
|
this.shiftActive = !this.shiftActive;
|
|
391
|
-
document.querySelectorAll('.key[data-key="Shift"]').forEach((key) => {
|
|
472
|
+
document.querySelectorAll('.key[data-key="Shift ⇧"]').forEach((key) => {
|
|
392
473
|
key.classList.toggle("active", this.shiftActive);
|
|
393
474
|
key.classList.toggle("bg-gray-400", this.shiftActive);
|
|
394
475
|
});
|
|
395
|
-
|
|
476
|
+
|
|
396
477
|
document.querySelectorAll(".key").forEach((key) => {
|
|
397
|
-
|
|
478
|
+
const isLetter = key.dataset.key.length === 1 && /[a-zA-Zก-๙]/.test(key.dataset.key);
|
|
479
|
+
|
|
480
|
+
if (isLetter) {
|
|
398
481
|
key.textContent = this.shiftActive
|
|
399
482
|
? key.dataset.key.toUpperCase()
|
|
400
483
|
: key.dataset.key.toLowerCase();
|
|
401
484
|
}
|
|
485
|
+
this.updateKeyContent(key, this.shiftActive);
|
|
402
486
|
});
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if (originalKey) {
|
|
426
|
-
key.textContent = originalKey;
|
|
427
|
-
key.dataset.key = originalKey;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (
|
|
432
|
-
this.shiftActive &&
|
|
433
|
-
this.currentLayout === "en" &&
|
|
434
|
-
this.EngAlphabetShift[currentChar]
|
|
435
|
-
) {
|
|
436
|
-
key.textContent = this.EngAlphabetShift[currentChar];
|
|
437
|
-
key.dataset.key = this.EngAlphabetShift[currentChar];
|
|
438
|
-
} else if (
|
|
439
|
-
!this.shiftActive &&
|
|
440
|
-
this.currentLayout === "en" &&
|
|
441
|
-
Object.values(this.EngAlphabetShift).includes(currentChar)
|
|
442
|
-
) {
|
|
443
|
-
// เปลี่ยนกลับเมื่อปิด Shift
|
|
444
|
-
const originalKey = Object.keys(this.EngAlphabetShift).find(
|
|
445
|
-
(key) => this.EngAlphabetShift[key] === currentChar
|
|
446
|
-
);
|
|
447
|
-
if (originalKey) {
|
|
448
|
-
key.textContent = originalKey;
|
|
449
|
-
key.dataset.key = originalKey;
|
|
450
|
-
}
|
|
487
|
+
}
|
|
488
|
+
updateKeyContent(key, shiftActive) {
|
|
489
|
+
const currentChar = key.textContent.trim();
|
|
490
|
+
|
|
491
|
+
const layouts = {
|
|
492
|
+
th: this.ThaiAlphabetShift,
|
|
493
|
+
en: this.EngAlphabetShift,
|
|
494
|
+
enSc: this.EngAlphabetShift,
|
|
495
|
+
full: this.FullAlphabetShift,
|
|
496
|
+
};
|
|
497
|
+
const layout = layouts[this.currentLayout];
|
|
498
|
+
|
|
499
|
+
if (!layout) return;
|
|
500
|
+
|
|
501
|
+
if (shiftActive && layout[currentChar]) {
|
|
502
|
+
key.textContent = layout[currentChar];
|
|
503
|
+
key.dataset.key = layout[currentChar];
|
|
504
|
+
} else if (!shiftActive && Object.values(layout).includes(currentChar)) {
|
|
505
|
+
const originalKey = Object.keys(layout).find((k) => layout[k] === currentChar);
|
|
506
|
+
if (originalKey) {
|
|
507
|
+
key.textContent = originalKey;
|
|
508
|
+
key.dataset.key = originalKey;
|
|
451
509
|
}
|
|
452
|
-
}
|
|
510
|
+
}
|
|
453
511
|
}
|
|
454
512
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
3: "#",
|
|
460
|
-
4: "$",
|
|
461
|
-
5: "%",
|
|
462
|
-
6: "^",
|
|
463
|
-
7: "&",
|
|
464
|
-
8: "*",
|
|
465
|
-
9: "(",
|
|
466
|
-
0: ")",
|
|
467
|
-
"-": "_",
|
|
468
|
-
"=": "+",
|
|
469
|
-
"[": "{",
|
|
470
|
-
"]": "}",
|
|
471
|
-
"\\": "|",
|
|
472
|
-
";": ":",
|
|
473
|
-
"'": '"',
|
|
474
|
-
",": "<",
|
|
475
|
-
".": ">",
|
|
476
|
-
"/": "?",
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
ThaiAlphabetShift = {
|
|
480
|
-
_: "%",
|
|
481
|
-
ๅ: "+",
|
|
482
|
-
"/": "๑",
|
|
483
|
-
"-": "๒",
|
|
484
|
-
ภ: "๓",
|
|
485
|
-
ถ: "๔",
|
|
486
|
-
"ุ": "ู",
|
|
487
|
-
"ึ": "฿",
|
|
488
|
-
ค: "๕",
|
|
489
|
-
ต: "๖",
|
|
490
|
-
จ: "๗",
|
|
491
|
-
ข: "๘",
|
|
492
|
-
ช: "๙",
|
|
493
|
-
ๆ: "๐",
|
|
494
|
-
ไ: '"',
|
|
495
|
-
ำ: "ฎ",
|
|
496
|
-
พ: "ฑ",
|
|
497
|
-
ะ: "ธ",
|
|
498
|
-
"ั": "ํ",
|
|
499
|
-
"ี": "๋",
|
|
500
|
-
ร: "ณ",
|
|
501
|
-
น: "ฯ",
|
|
502
|
-
ย: "ญ",
|
|
503
|
-
บ: "ฐ",
|
|
504
|
-
ล: ",",
|
|
505
|
-
ฃ: "ฅ",
|
|
506
|
-
ฟ: "ฤ",
|
|
507
|
-
ห: "ฆ",
|
|
508
|
-
ก: "ฏ",
|
|
509
|
-
ด: "โ",
|
|
510
|
-
เ: "ฌ",
|
|
511
|
-
"้": "็",
|
|
512
|
-
"่": "๋",
|
|
513
|
-
า: "ษ",
|
|
514
|
-
ส: "ศ",
|
|
515
|
-
ว: "ซ",
|
|
516
|
-
ง: ".",
|
|
517
|
-
ผ: "(",
|
|
518
|
-
ป: ")",
|
|
519
|
-
แ: "ฉ",
|
|
520
|
-
อ: "ฮ",
|
|
521
|
-
"ิ": "ฺ",
|
|
522
|
-
"ื": "์",
|
|
523
|
-
ท: "?",
|
|
524
|
-
ม: "ฒ",
|
|
525
|
-
ใ: "ฬ",
|
|
526
|
-
ฝ: "ฦ",
|
|
527
|
-
};
|
|
513
|
+
// [7]
|
|
514
|
+
EngAlphabetShift = { "`": "~", 1: "!", 2: "@", 3: "#", 4: "$", 5: "%", 6: "^", 7: "&", 8: "*", 9: "(", 0: ")", "-": "_", "=": "+", "[": "{", "]": "}", "\\": "|", ";": ":", "'": '"', ",": "<", ".": ">", "/": "?", };
|
|
515
|
+
ThaiAlphabetShift = { _: "%", ๅ: "+", "/": "๑", "-": "๒", ภ: "๓", ถ: "๔", "ุ": "ู", "ึ": "฿", ค: "๕", ต: "๖", จ: "๗", ข: "๘", ช: "๙", ๆ: "๐", ไ: '"', ำ: "ฎ", พ: "ฑ", ะ: "ธ", "ั": "ํ", "ี": "๋", ร: "ณ", น: "ฯ", ย: "ญ", บ: "ฐ", ล: ",", ฃ: "ฅ", ฟ: "ฤ", ห: "ฆ", ก: "ฏ", ด: "โ", เ: "ฌ", "้": "็", "่": "๋", า: "ษ", ส: "ศ", ว: "ซ", ง: ".", ผ: "(", ป: ")", แ: "ฉ", อ: "ฮ", "ิ": "ฺ", "ื": "์", ท: "?", ม: "ฒ", ใ: "ฬ", ฝ: "ฦ"};
|
|
516
|
+
FullAlphabetShift = { "[": "{", "]": "}", "\\": "|", ";": ":", "'": '"', ",": "<", ".": ">", "/": "?", };
|
|
528
517
|
|
|
518
|
+
// [8]
|
|
529
519
|
toggle() {
|
|
530
520
|
this.isVisible = !this.isVisible;
|
|
531
521
|
this.render();
|
|
532
522
|
}
|
|
533
523
|
|
|
524
|
+
// [9]
|
|
534
525
|
changeLayout(layout) {
|
|
535
526
|
this.currentLayout = layout;
|
|
536
527
|
this.render();
|
|
537
528
|
}
|
|
538
529
|
|
|
530
|
+
// [10]
|
|
539
531
|
shuffleArray(array) {
|
|
540
532
|
for (let i = array.length - 1; i > 0; i--) {
|
|
541
533
|
const j = Math.floor(Math.random() * (i + 1));
|
|
@@ -543,11 +535,12 @@ export class VirtualKeyboard {
|
|
|
543
535
|
}
|
|
544
536
|
}
|
|
545
537
|
|
|
538
|
+
// [11]
|
|
546
539
|
scrambleKeyboard() {
|
|
547
540
|
const keys = document.querySelectorAll(
|
|
548
|
-
"
|
|
541
|
+
":not([data-key='std']):not([data-key='scr']).key:not([data-key=backspace]"
|
|
549
542
|
);
|
|
550
|
-
const numbers =
|
|
543
|
+
const numbers = "1234567890-=+%/*.()".split("");
|
|
551
544
|
this.shuffleArray(numbers);
|
|
552
545
|
keys.forEach((key, index) => {
|
|
553
546
|
key.textContent = numbers[index];
|
|
@@ -555,11 +548,12 @@ export class VirtualKeyboard {
|
|
|
555
548
|
});
|
|
556
549
|
}
|
|
557
550
|
|
|
551
|
+
// [12]
|
|
558
552
|
scrambleEnglishKeys() {
|
|
559
553
|
const keys = document.querySelectorAll(
|
|
560
|
-
".key:not([data-key='
|
|
554
|
+
".key:not([data-key='scrambled']):not([data-key='std']):not([data-key='scr']):not([data-key='Space']):not([data-key='Backspace']):not([data-key='Caps 🄰']):not([data-key='Shift ⇧']):not([data-key='Enter']):not([data-key='Tab ↹'])"
|
|
561
555
|
);
|
|
562
|
-
const englishAlphabet = "abcdefghijklmnopqrstuvwxyz".split("");
|
|
556
|
+
const englishAlphabet = "abcdefghijklmnopqrstuvwxyz/.,';\\][`1234567890-=".split("");
|
|
563
557
|
this.shuffleArray(englishAlphabet);
|
|
564
558
|
keys.forEach((key, index) => {
|
|
565
559
|
key.textContent = englishAlphabet[index];
|
|
@@ -567,37 +561,33 @@ export class VirtualKeyboard {
|
|
|
567
561
|
});
|
|
568
562
|
}
|
|
569
563
|
|
|
564
|
+
// [13]
|
|
570
565
|
scrambleThaiKeys() {
|
|
571
566
|
const keys = document.querySelectorAll(
|
|
572
|
-
".key:not([data-key='Backspace']):not([data-key='Caps']):not([data-key='Shift']):not([data-key='Enter']):not([data-key='Space'])"
|
|
573
|
-
);
|
|
574
|
-
const ThaiAlphabet = "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ".split(
|
|
575
|
-
""
|
|
567
|
+
".key:not([data-key='scrambled']):not([data-key='scr']):not([data-key='Backspace']):not([data-key='Caps 🄰']):not([data-key='Shift ⇧']):not([data-key='Enter']):not([data-key='Space']):not([data-key='Tab ↹'])"
|
|
576
568
|
);
|
|
577
|
-
|
|
578
|
-
|
|
569
|
+
const ThaiAlphabet = '_ๅ/-ภถุึคตจขชๆไำพะัีรนยบลฃฟหกดเ้่าสวงผปแอิืทมใฝ%+๑๒๓๔ู฿๕๖๗๘๙๐"ฎฑธํ๊ณฯญฐ,ฅฤฆฏโฌ็๋ษศซ.ฦฬฒ?์ฺฮฉ)('.split("");
|
|
570
|
+
this.shuffleArray(ThaiAlphabet);
|
|
579
571
|
keys.forEach((key, index) => {
|
|
580
|
-
// อัพเดต textContent และ dataset.key ให้ตรงกับค่าใหม่ที่สับเปลี่ยน
|
|
581
572
|
key.textContent = ThaiAlphabet[index];
|
|
582
|
-
key.dataset.key = ThaiAlphabet[index];
|
|
573
|
+
key.dataset.key = ThaiAlphabet[index];
|
|
583
574
|
});
|
|
584
575
|
}
|
|
585
576
|
|
|
586
|
-
// จัดการการลากคีย์บอร์ด
|
|
577
|
+
// [14] จัดการการลากคีย์บอร์ด
|
|
587
578
|
startDrag(event) {
|
|
588
579
|
this.isDragging = true;
|
|
589
|
-
this.offsetX =
|
|
590
|
-
|
|
591
|
-
this.offsetY =
|
|
592
|
-
event.clientY - document.getElementById("keyboard").offsetTop;
|
|
580
|
+
this.offsetX = event.clientX - document.getElementById("keyboard").offsetLeft;
|
|
581
|
+
this.offsetY = event.clientY - document.getElementById("keyboard").offsetTop;
|
|
593
582
|
|
|
594
|
-
document.addEventListener("mousemove", this.drag.bind(this));
|
|
583
|
+
document.addEventListener("mousemove", this.drag.bind(this));
|
|
595
584
|
document.addEventListener("mouseup", () => {
|
|
596
585
|
this.isDragging = false;
|
|
597
586
|
document.removeEventListener("mousemove", this.drag.bind(this));
|
|
598
587
|
});
|
|
599
588
|
}
|
|
600
589
|
|
|
590
|
+
// [15]
|
|
601
591
|
drag(event) {
|
|
602
592
|
if (this.isDragging) {
|
|
603
593
|
const keyboard = document.getElementById("keyboard");
|
package/layouts.js
CHANGED
|
@@ -1,44 +1,32 @@
|
|
|
1
1
|
export const layouts = {
|
|
2
|
+
full: [
|
|
3
|
+
["Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "DEL⌦", "HOME", "END"],
|
|
4
|
+
["`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Backspace"].concat(["+", "-", "*","/"]),
|
|
5
|
+
["Tab ↹", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\"].concat(["7", "8", "9", "%"]),
|
|
6
|
+
["Caps 🄰", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Enter"].concat(["4", "5", "6", "_"]),
|
|
7
|
+
["Shift ⇧", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift ⇧", "↑"].concat(["1", "2", "3", "="]),
|
|
8
|
+
["Space", "←", "↓", "→"].concat(["0", "."]),
|
|
9
|
+
],
|
|
2
10
|
en: [
|
|
3
11
|
["`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Backspace"],
|
|
4
|
-
["Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\"],
|
|
5
|
-
["Caps", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Enter"],
|
|
6
|
-
["Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift"],
|
|
7
|
-
["Space"],
|
|
12
|
+
["Tab ↹", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\"],
|
|
13
|
+
["Caps 🄰", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Enter"],
|
|
14
|
+
["Shift ⇧", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift ⇧"],
|
|
15
|
+
["scrambled", "Space"],
|
|
8
16
|
],
|
|
9
17
|
th: [
|
|
10
18
|
["_", "ๅ", "/", "-", "ภ", "ถ", "ุ", "ึ", "ค", "ต", "จ", "ข", "ช", "Backspace"],
|
|
11
|
-
["Tab", "ๆ", "ไ", "ำ", "พ", "ะ", "ั", "ี", "ร", "น", "ย", "บ", "ล", "ฃ"],
|
|
12
|
-
["Caps", "ฟ", "ห", "ก", "ด", "เ", "้", "่", "า", "ส", "ว", "ง", "Enter"],
|
|
13
|
-
["Shift", "ผ", "ป", "แ", "อ", "ิ", "ื", "ท", "ม", "ใ", "ฝ", "Shift"],
|
|
14
|
-
["Space"],
|
|
19
|
+
["Tab ↹", "ๆ", "ไ", "ำ", "พ", "ะ", "ั", "ี", "ร", "น", "ย", "บ", "ล", "ฃ"],
|
|
20
|
+
["Caps 🄰", "ฟ", "ห", "ก", "ด", "เ", "้", "่", "า", "ส", "ว", "ง", "Enter"],
|
|
21
|
+
["Shift ⇧", "ผ", "ป", "แ", "อ", "ิ", "ื", "ท", "ม", "ใ", "ฝ", "Shift ⇧"],
|
|
22
|
+
["scrambled" ,"Space"],
|
|
15
23
|
],
|
|
16
24
|
numpad: [
|
|
17
|
-
["
|
|
18
|
-
["1", "2", "3", "
|
|
19
|
-
["4", "5", "6", "
|
|
20
|
-
["7", "8", "9", "
|
|
21
|
-
["
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
["+", "-", "*", "/"],
|
|
25
|
-
["1", "2", "3", "%"],
|
|
26
|
-
["4", "5", "6", "."],
|
|
27
|
-
["7", "8", "9", "="],
|
|
28
|
-
["00", "0", "backspace"],
|
|
29
|
-
],
|
|
30
|
-
thSc: [
|
|
31
|
-
["ก", "ข", "ฃ", "ค", "ฅ", "ฆ", "ง", "จ", "ฉ", "ช", "ซ", "ฌ", "Backspace"],
|
|
32
|
-
["ญ", "ฎ", "ฏ", "ฐ", "ฑ", "ฒ", "ณ", "ด", "ต", "ถ", "ท", "ธ", "น"],
|
|
33
|
-
["บ", "ป", "ผ", "ฝ", "พ", "ฟ", "ภ", "ม", "ย", "ร", "ฤ", "Enter"],
|
|
34
|
-
["ล", "ฦ", "ว", "ศ", "ษ", "ส", "ห", "ฬ", "อ", "ฮ"],
|
|
35
|
-
["Space"],
|
|
36
|
-
],
|
|
37
|
-
enSc: [
|
|
38
|
-
["`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "="],
|
|
39
|
-
["Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "Backspace"],
|
|
40
|
-
["Caps", "a", "s", "d", "f", "g", "h", "j", "k", "l", "Enter"],
|
|
41
|
-
["Shift", "z", "x", "c", "v", "b", "n", "m", "Shift"],
|
|
42
|
-
["Space"],
|
|
43
|
-
],
|
|
25
|
+
["scr", "+", "-", "*"],
|
|
26
|
+
["1", "2", "3", "/"],
|
|
27
|
+
["4", "5", "6", "%"],
|
|
28
|
+
["7", "8", "9", "."],
|
|
29
|
+
["(", "0", ")", "="],
|
|
30
|
+
["backspace"]
|
|
31
|
+
]
|
|
44
32
|
};
|