js_lis 1.0.5 → 1.0.7

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.
@@ -0,0 +1,437 @@
1
+ import { layouts } from './layouts.js';
2
+
3
+ export class VirtualKeyboard {
4
+ constructor() {
5
+ this.currentLayout = 'en';
6
+ this.isVisible = false;
7
+ this.container = document.getElementById('keyboard-container');
8
+ this.currentInput = null;
9
+ this.layouts = layouts;
10
+ this.isDragging = false;
11
+ this.offsetX = 0;
12
+ this.offsetY = 0;
13
+ this.shiftActive = false;
14
+ this.capsLockActive = false;
15
+
16
+ this.render();
17
+ this.initializeInputListeners();
18
+ }
19
+
20
+ getLayoutName(layout) {
21
+ switch (layout) {
22
+ case 'en': return 'English Keyboard';
23
+ case 'enSc': return 'English scrambled';
24
+ case 'th': return 'Thai keyboard';
25
+ case 'thSc': return 'Thai scrambled';
26
+ case 'numpad': return 'Numpad Keyboard';
27
+ case 'scNum': return 'Scrambled Keyboard';
28
+ }
29
+ }
30
+
31
+ initializeInputListeners() {
32
+ // เพิ่ม event listener สำหรับทุก input และ textarea
33
+ document.addEventListener('click', (e) => {
34
+ const target = e.target;
35
+ if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
36
+ this.setCurrentInput(target);
37
+ }
38
+ });
39
+
40
+ // เพิ่ม event listener สำหรับ focus
41
+ document.addEventListener('focus', (e) => {
42
+ const target = e.target;
43
+ if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
44
+ this.setCurrentInput(target);
45
+ }
46
+ }, true);
47
+ }
48
+
49
+ setCurrentInput(inputElement) {
50
+ // ถ้ามี input เก่า ให้ลบ class active
51
+ if (this.currentInput) {
52
+ this.currentInput.classList.remove('keyboard-active');
53
+ }
54
+
55
+ // เซ็ต input ใหม่และเพิ่ม class active
56
+ this.currentInput = inputElement;
57
+ this.currentInput.classList.add('keyboard-active');
58
+ }
59
+
60
+ render() {
61
+ const keyboard = document.createElement('div');
62
+ keyboard.className = 'virtual-keyboard';
63
+ keyboard.style.display = this.isVisible ? 'block' : 'none';
64
+
65
+ // Add the ID for easy reference in dragging
66
+ keyboard.id = 'keyboard';
67
+
68
+ // New: Add Layout selector at the top inside the keyboard div
69
+ const controlsContainer = document.createElement('div');
70
+ controlsContainer.className = 'controls';
71
+ controlsContainer.style.display = 'flex';
72
+ controlsContainer.style.justifyContent = 'center';
73
+ controlsContainer.style.alignItems = 'center';
74
+ controlsContainer.style.marginBottom = '10px'; // Optional: Add some space below the selector
75
+
76
+ const layoutSelector = document.createElement('select');
77
+ layoutSelector.id = 'layout-selector';
78
+ layoutSelector.onchange = (e) => this.changeLayout(e.target.value);
79
+
80
+ const layouts = ['en', 'enSc', 'th', 'thSc', 'numpad', 'scNum'];
81
+ layouts.forEach(layout => {
82
+ const option = document.createElement('option');
83
+ option.value = layout;
84
+ option.innerText = this.getLayoutName(layout);
85
+ layoutSelector.appendChild(option);
86
+ });
87
+ layoutSelector.value = this.currentLayout;
88
+ controlsContainer.appendChild(layoutSelector);
89
+
90
+ keyboard.appendChild(controlsContainer);
91
+
92
+ const layout = this.layouts[this.currentLayout];
93
+
94
+ layout.forEach(row => {
95
+ const rowElement = document.createElement('div');
96
+ rowElement.className = 'keyboard-row';
97
+
98
+ row.forEach(key => {
99
+ const keyElement = document.createElement('button');
100
+ keyElement.className = 'keyboard-key key'; // Add key class
101
+ keyElement.textContent = key;
102
+ keyElement.type = 'button';
103
+
104
+ // Add data-key to each key for reference
105
+ keyElement.dataset.key = key;
106
+
107
+ if (key === 'Space') {
108
+ keyElement.className += ' space';
109
+ }
110
+
111
+ if (key === 'backspace') {
112
+ keyElement.className += ' backspacew';
113
+ }
114
+
115
+ keyElement.onclick = (e) => {
116
+ e.preventDefault();
117
+ const keyPressed = keyElement.dataset.key || keyElement.textContent;
118
+ if (keyPressed) {
119
+ this.handleKeyPress(keyPressed);
120
+ } else {
121
+ console.error("The key element does not have a valid key value.");
122
+ }
123
+ };
124
+
125
+ rowElement.appendChild(keyElement);
126
+ });
127
+
128
+ keyboard.appendChild(rowElement);
129
+ });
130
+
131
+ this.container.innerHTML = '';
132
+ this.container.appendChild(keyboard);
133
+
134
+ if (this.currentLayout === "scNum") {
135
+ this.scrambleKeyboard();
136
+ }
137
+
138
+ if (this.currentLayout === "enSc") {
139
+ this.scrambleEnglishKeys();
140
+ }
141
+
142
+ if (this.currentLayout === "thSc") {
143
+ this.scrambleThaiKeys();
144
+ }
145
+
146
+ // Add drag functionality to the keyboard
147
+ keyboard.addEventListener('mousedown', (event) => this.startDrag(event));
148
+ }
149
+
150
+
151
+
152
+ handleKeyPress(keyPressed) {
153
+ if (!this.currentInput) return;
154
+
155
+ const start = this.currentInput.selectionStart;
156
+ const end = this.currentInput.selectionEnd;
157
+ const value = this.currentInput.value;
158
+
159
+ const isCapsActive = this.capsLockActive;
160
+ const isShiftActive = this.shiftActive;
161
+
162
+ const convertToCorrectCase = (char) => {
163
+ if (isCapsActive || isShiftActive) {
164
+ return char.toUpperCase();
165
+ }
166
+ return char.toLowerCase();
167
+ };
168
+
169
+ // ตรวจสอบค่า keyPressed
170
+ if (!keyPressed) {
171
+ console.error("Invalid key pressed.");
172
+ return;
173
+ }
174
+
175
+ switch(keyPressed) {
176
+ case 'Backspace':
177
+ case 'backspace':
178
+ if (start === end && start > 0) {
179
+ this.currentInput.value = value.slice(0, start - 1) + value.slice(end);
180
+ this.currentInput.selectionStart = this.currentInput.selectionEnd = start - 1;
181
+ } else {
182
+ this.currentInput.value = value.slice(0, start) + value.slice(end);
183
+ this.currentInput.selectionStart = this.currentInput.selectionEnd = start;
184
+ }
185
+ break;
186
+
187
+ case 'Space':
188
+ this.insertText(' ');
189
+ break;
190
+
191
+ case 'Tab':
192
+ this.insertText('\t');
193
+ break;
194
+
195
+ case 'Enter':
196
+ if (this.currentInput.tagName === 'TEXTAREA') {
197
+ this.insertText('\n');
198
+ }
199
+ break;
200
+
201
+ case 'Caps':
202
+ this.toggleCapsLock();
203
+ break;
204
+
205
+ case 'Shift':
206
+ this.toggleShift();
207
+ break;
208
+
209
+ default:
210
+ this.insertText(convertToCorrectCase(keyPressed));
211
+
212
+ }
213
+
214
+ if (isShiftActive && !isCapsActive) {
215
+ this.toggleShift();
216
+ }
217
+
218
+ this.currentInput.focus();
219
+ const event = new Event('input', { bubbles: true });
220
+ this.currentInput.dispatchEvent(event);
221
+ }
222
+
223
+ toggleCapsLock() {
224
+ this.capsLockActive = !this.capsLockActive;
225
+ const capsKey = document.querySelector('.key[data-key="Caps"]');
226
+ capsKey.classList.toggle("active", this.capsLockActive);
227
+ capsKey.classList.toggle("bg-gray-400", this.capsLockActive);
228
+
229
+ document.querySelectorAll(".key").forEach((key) => {
230
+ if (key.dataset.key.length === 1 && /[a-zA-Zก-๙]/.test(key.dataset.key)) {
231
+ key.textContent = this.capsLockActive
232
+ ? key.dataset.key.toUpperCase()
233
+ : key.dataset.key.toLowerCase();
234
+ }
235
+ });
236
+
237
+ const keyboardKeys = document.querySelectorAll(".key:not([data-key='Shift'])");
238
+ keyboardKeys.forEach((key) => {
239
+ const currentChar = key.textContent.trim();
240
+ if (
241
+ this.capsLockActive &&
242
+ this.currentLayout === "th" &&
243
+ this.ThaiAlphabetShift[currentChar]
244
+ ) {
245
+ key.textContent = this.ThaiAlphabetShift[currentChar];
246
+ key.dataset.key = this.ThaiAlphabetShift[currentChar];
247
+ } else if (
248
+ !this.capsLockActive &&
249
+ this.currentLayout === "th" &&
250
+ Object.values(this.ThaiAlphabetShift).includes(currentChar)
251
+ ) {
252
+ const originalKey = Object.keys(this.ThaiAlphabetShift).find(
253
+ (key) => this.ThaiAlphabetShift[key] === currentChar
254
+ );
255
+ if (originalKey) {
256
+ key.textContent = originalKey;
257
+ key.dataset.key = originalKey;
258
+ }
259
+ }
260
+ });
261
+ }
262
+
263
+ toggleShift() {
264
+ this.shiftActive = !this.shiftActive;
265
+ const shiftKey = document.querySelector('.key[data-key="Shift"]');
266
+ shiftKey.classList.toggle("active", this.shiftActive);
267
+ shiftKey.classList.toggle("bg-gray-400", this.shiftActive);
268
+
269
+ document.querySelectorAll(".key").forEach((key) => {
270
+ if (key.dataset.key.length === 1 && /[a-zA-Zก-๙]/.test(key.dataset.key)) {
271
+ // แสดงผลตัวพิมพ์ใหญ่หรือเล็กตามค่า Shift
272
+ key.textContent = this.shiftActive
273
+ ? key.dataset.key.toUpperCase()
274
+ : key.dataset.key.toLowerCase();
275
+ }
276
+ });
277
+
278
+ const keyboardKeys = document.querySelectorAll(".key:not([data-key='Shift'])");
279
+ keyboardKeys.forEach((key) => {
280
+ const currentChar = key.textContent.trim();
281
+ if (
282
+ this.shiftActive &&
283
+ this.currentLayout === "th" && // ตรวจสอบว่ากำลังใช้เลย์เอาต์ภาษาไทย
284
+ this.ThaiAlphabetShift[currentChar]
285
+ ) {
286
+ key.textContent = this.ThaiAlphabetShift[currentChar]; // แสดงอักษรจาก shift
287
+ key.dataset.key = this.ThaiAlphabetShift[currentChar]; // อัปเดต dataset.key
288
+ } else if (
289
+ !this.shiftActive &&
290
+ this.currentLayout === "th" &&
291
+ Object.values(this.ThaiAlphabetShift).includes(currentChar)
292
+ ) {
293
+ const originalKey = Object.keys(this.ThaiAlphabetShift).find(
294
+ (key) => this.ThaiAlphabetShift[key] === currentChar
295
+ );
296
+ if (originalKey) {
297
+ key.textContent = originalKey;
298
+ key.dataset.key = originalKey;
299
+ }
300
+ }
301
+ });
302
+ }
303
+
304
+ ThaiAlphabetShift = {
305
+ _: "%",
306
+ ๅ: "+",
307
+ "/": "๑",
308
+ "-": "๒",
309
+ ภ: "๓",
310
+ ถ: "๔",
311
+ "ุ": "ู",
312
+ "ึ": "฿",
313
+ ค: "๕",
314
+ ต: "๖",
315
+ จ: "๗",
316
+ ข: "๘",
317
+ ช: "๙",
318
+ ๆ: "๐",
319
+ ไ: '"',
320
+ ำ: "ฎ",
321
+ พ: "ฑ",
322
+ ะ: "ธ",
323
+ "ั": "ํ",
324
+ "ี": "๋",
325
+ ร: "ณ",
326
+ น: "ฯ",
327
+ ย: "ญ",
328
+ บ: "ฐ",
329
+ ล: ",",
330
+ ฃ: "ฅ",
331
+ ฟ: "ฤ",
332
+ ห: "ฆ",
333
+ ก: "ฏ",
334
+ ด: "โ",
335
+ เ: "ฌ",
336
+ "้": "็",
337
+ "่": "๋",
338
+ า: "ษ",
339
+ ส: "ศ",
340
+ ว: "ซ",
341
+ ง: ".",
342
+ ผ: "(",
343
+ ป: ")",
344
+ แ: "ฉ",
345
+ อ: "ฮ",
346
+ "ิ": "ฺ",
347
+ "ื": "์",
348
+ ท: "?",
349
+ ม: "ฒ",
350
+ ใ: "ฬ",
351
+ ฝ: "ฦ",
352
+ };
353
+
354
+ insertText(text) {
355
+ const start = this.currentInput.selectionStart;
356
+ const end = this.currentInput.selectionEnd;
357
+ this.currentInput.value = this.currentInput.value.slice(0, start) + text + this.currentInput.value.slice(end);
358
+ this.currentInput.selectionStart = this.currentInput.selectionEnd = start + text.length;
359
+ }
360
+
361
+ toggle() {
362
+ this.isVisible = !this.isVisible;
363
+ this.render();
364
+ }
365
+
366
+ changeLayout(layout) {
367
+ this.currentLayout = layout;
368
+ this.render();
369
+ }
370
+
371
+ shuffleArray(array) {
372
+ for (let i = array.length - 1; i > 0; i--) {
373
+ const j = Math.floor(Math.random() * (i + 1));
374
+ [array[i], array[j]] = [array[j], array[i]];
375
+ }
376
+ }
377
+
378
+ scrambleKeyboard() {
379
+ const keys = document.querySelectorAll(
380
+ ".key:not([data-key=backspace]):not([data-key='+']):not([data-key='-']):not([data-key='*']):not([data-key='/']):not([data-key='%']):not([data-key='=']):not([data-key='.']):not([data-key='00'])"
381
+ );
382
+ const numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
383
+ this.shuffleArray(numbers);
384
+ keys.forEach((key, index) => {
385
+ key.textContent = numbers[index];
386
+ key.dataset.key = numbers[index];
387
+ });
388
+ }
389
+
390
+ scrambleEnglishKeys() {
391
+ const keys = document.querySelectorAll(
392
+ ".key:not([data-key='Space']):not([data-key='Backspace']):not([data-key='Caps']):not([data-key='Shift']):not([data-key='Enter']):not([data-key='Tab']):not([data-key='`']):not([data-key='1']):not([data-key='2']):not([data-key='3']):not([data-key='4']):not([data-key='5']):not([data-key='6']):not([data-key='7']):not([data-key='8']):not([data-key='9']):not([data-key='0']):not([data-key='-']):not([data-key='+']):not([data-key='='])"
393
+ );
394
+ const englishAlphabet = "abcdefghijklmnopqrstuvwxyz".split("");
395
+ this.shuffleArray(englishAlphabet);
396
+ keys.forEach((key, index) => {
397
+ key.textContent = englishAlphabet[index];
398
+ key.dataset.key = englishAlphabet[index];
399
+ });
400
+ }
401
+
402
+ scrambleThaiKeys() {
403
+ const keys = document.querySelectorAll(
404
+ ".key:not([data-key='Backspace']):not([data-key='Caps']):not([data-key='Shift']):not([data-key='Enter']):not([data-key='Space'])"
405
+ );
406
+ const ThaiAlphabet = "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ".split("");
407
+ this.shuffleArray(ThaiAlphabet); // สับเปลี่ยนลำดับของตัวอักษร
408
+
409
+ keys.forEach((key, index) => {
410
+ // อัพเดต textContent และ dataset.key ให้ตรงกับค่าใหม่ที่สับเปลี่ยน
411
+ key.textContent = ThaiAlphabet[index];
412
+ key.dataset.key = ThaiAlphabet[index]; // อัพเดต dataset.key ด้วยค่าใหม่
413
+ });
414
+ }
415
+
416
+
417
+ // จัดการการลากคีย์บอร์ด
418
+ startDrag(event) {
419
+ this.isDragging = true;
420
+ this.offsetX = event.clientX - document.getElementById("keyboard").offsetLeft;
421
+ this.offsetY = event.clientY - document.getElementById("keyboard").offsetTop;
422
+
423
+ document.addEventListener("mousemove", this.drag.bind(this)); // Use bind to preserve `this` context
424
+ document.addEventListener("mouseup", () => {
425
+ this.isDragging = false;
426
+ document.removeEventListener("mousemove", this.drag.bind(this));
427
+ });
428
+ }
429
+
430
+ drag(event) {
431
+ if (this.isDragging) {
432
+ const keyboard = document.getElementById("keyboard");
433
+ keyboard.style.left = `${event.clientX - this.offsetX}px`;
434
+ keyboard.style.top = `${event.clientY - this.offsetY}px`;
435
+ }
436
+ }
437
+ }