CVNSS commited on
Commit
e4ba47a
·
verified ·
1 Parent(s): c8768b2

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +155 -225
index.html CHANGED
@@ -1,225 +1,155 @@
1
- <!DOCTYPE html>
2
- <html lang="vi">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Bộ chuyển đổi CQN ⇄ CVNSS 4.0 (offline) - Tối ưu</title>
7
- <style>
8
- body {
9
- font-family: Segoe UI, Arial, Helvetica, sans-serif; /* Cập nhật font chữ hiện đại hơn */
10
- background: #f4f6f8; /* Màu nền nhẹ nhàng hơn */
11
- margin: 30px;
12
- text-align: center;
13
- color: #333;
14
- }
15
- h1 {
16
- margin-bottom: 24px;
17
- font-size: 28px; /* Tăng kích thước tiêu đề */
18
- color: #2c3e50; /* Màu tiêu đề đậm hơn */
19
- }
20
- .wrap {
21
- display: flex;
22
- gap: 25px;
23
- flex-wrap: wrap;
24
- justify-content: center;
25
- margin-bottom: 20px; /* Thêm khoảng cách dưới */
26
- }
27
- textarea {
28
- width: 420px; /* Điều chỉnh kích thước */
29
- height: 280px;
30
- padding: 15px;
31
- font-size: 16px;
32
- border: 1px solid #dcdcdc; /* Viền nhẹ hơn */
33
- border-radius: 8px; /* Bo góc ít hơn một chút */
34
- resize: vertical; /* Cho phép thay đổi chiều cao */
35
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
36
- box-sizing: border-box;
37
- transition: border-color 0.2s, box-shadow 0.2s; /* Thêm hiệu ứng chuyển tiếp */
38
- }
39
- textarea:focus {
40
- border-color: #3498db; /* Màu viền khi focus nổi bật hơn */
41
- box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2); /* Thêm hiệu ứng bóng mờ khi focus */
42
- outline: none;
43
- }
44
- .controls { /* Đổi tên từ btn-row cho rõ ràng hơn */
45
- display: flex;
46
- gap: 15px;
47
- justify-content: center;
48
- align-items: center; /* Căn giữa các item theo chiều dọc */
49
- margin-top: 15px;
50
- }
51
- button, .mode-select {
52
- padding: 10px 20px; /* Đồng nhất padding */
53
- font-size: 15px;
54
- border-radius: 6px;
55
- cursor: pointer;
56
- transition: transform 0.1s, background-color 0.2s, box-shadow 0.2s; /* Cập nhật transition */
57
- }
58
- button {
59
- min-width: 120px; /* Tăng chiều rộng tối thiểu */
60
- border: none;
61
- color: #fff;
62
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
63
- }
64
- button:hover {
65
- filter: brightness(1.1);
66
- box-shadow: 0 4px 8px rgba(0,0,0,0.15);
67
- }
68
- button:active {
69
- transform: scale(0.97);
70
- filter: brightness(1);
71
- }
72
- .copy {
73
- background-color: #3498db; /* Xanh dương */
74
- }
75
- .reset {
76
- background-color: #e74c3c; /* Đỏ */
77
- }
78
- .mode-select {
79
- border: 1px solid #dcdcdc;
80
- background-color: #fff;
81
- min-width: 180px; /* Cho select rộng hơn một chút */
82
- }
83
- .mode-select:focus {
84
- border-color: #3498db;
85
- box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
86
- outline: none;
87
- }
88
- .status-message { /* Kiểu cho thông báo lỗi/thành công */
89
- margin-top: 15px;
90
- font-size: 14px;
91
- min-height: 20px; /* Giữ không gian cho thông báo */
92
- }
93
- .status-success { color: #27ae60; }
94
- .status-error { color: #c0392b; }
95
- </style>
96
- </head>
97
- <body>
98
- <h1>Công cụ CVNSS4.0 ⇄ Chữ Quốc ngữ</h1>
99
-
100
- <div class="wrap">
101
- <textarea id="inputText" placeholder="Nhập văn bản (CQN, CVN hoặc CVSS)" aria-label="Văn bản đầu vào"></textarea>
102
- <textarea id="outputText" placeholder="Kết quả chuyển đổi" readonly aria-label="Văn bản kết quả"></textarea>
103
- </div>
104
-
105
- <div class="controls">
106
- <select id="mode" class="mode-select" aria-label="Chọn chế độ chuyển đổi">
107
- <option value="cqn">CQN → CVN/CVSS</option>
108
- <option value="cvn">CVN → CQN/CVSS</option>
109
- <option value="cvss">CVSS → CQN/CVN</option>
110
- </select>
111
- <button class="copy" onclick="copyText()">Sao chép</button>
112
- <button class="reset" onclick="resetAll()">Đặt lại</button>
113
- </div>
114
- <div id="statusMessage" class="status-message"></div>
115
-
116
- <script src="cvnss4.0-converter.js"></script>
117
- <script>
118
- const inputBox = document.getElementById("inputText");
119
- const outputBox = document.getElementById("outputText");
120
- const modeSelect = document.getElementById("mode");
121
- const statusMessageEl = document.getElementById("statusMessage");
122
- let convertTimeout; // Di chuyển khai báo timeout ra ngoài debounce
123
- let guard = false;
124
-
125
- // Hàm debounce để tối ưu hóa sự kiện input và select
126
- function debounce(fn, delay) {
127
- return function (...args) {
128
- clearTimeout(convertTimeout);
129
- convertTimeout = setTimeout(() => fn.apply(this, args), delay); // Sử dụng apply để giữ context
130
- };
131
- }
132
-
133
- // Hàm hiển thị thông báo
134
- function showStatus(message, type = "success") {
135
- statusMessageEl.textContent = message;
136
- statusMessageEl.className = `status-message status-${type}`;
137
- setTimeout(() => { statusMessageEl.textContent = ""; statusMessageEl.className = "status-message";}, 3000);
138
- }
139
-
140
- // Hàm chuyển đổi văn bản
141
- function convertText() {
142
- if (guard) return;
143
- guard = true; // Bảo vệ chống gọi lại liên tục (dù debounce đã giảm thiểu)
144
-
145
- const input = inputBox.value;
146
- const mode = modeSelect.value;
147
-
148
- try {
149
- if (!input.trim()) { // Nếu input rỗng hoặc chỉ có khoảng trắng
150
- outputBox.value = "";
151
- guard = false;
152
- return;
153
- }
154
- const result = CVNSSConverter.convert(input, mode);
155
-
156
- // Logic hiển thị kết quả dựa trên mode
157
- // Các lựa chọn trong select của bạn là:
158
- // CQN → CVN/CVSS: Hiển thị CVN là chính, có thể cung cấp thêm CVSS
159
- // CVN → CQN/CVSS: Hiển thị CQN là chính, có thể cung cấp thêm CVSS
160
- // CVSS → CQN/CVN: Hiển thị CQN là chính, có thể cung cấp thêm CVN
161
- // Hiện tại, ta sẽ hiển thị kết quả đầu tiên được gợi ý.
162
- if (mode === "cqn") {
163
- outputBox.value = result.cvn;
164
- // Để hiển thị cả hai: outputBox.value = `CVN: ${result.cvn}\n\nCVSS: ${result.cvss}`;
165
- } else if (mode === "cvn") {
166
- outputBox.value = result.cqn;
167
- // Để hiển thị cả hai: outputBox.value = `CQN: ${result.cqn}\n\nCVSS: ${result.cvss}`;
168
- } else if (mode === "cvss") {
169
- outputBox.value = result.cqn;
170
- // Để hiển thị cả hai: outputBox.value = `CQN: ${result.cqn}\n\nCVN: ${result.cvn}`;
171
- }
172
- } catch (error) {
173
- console.error("Lỗi chuyển đổi:", error);
174
- outputBox.value = ""; // Xóa output nếu có lỗi
175
- showStatus("Lỗi: " + error.message, "error");
176
- } finally {
177
- guard = false;
178
- }
179
- }
180
-
181
- // Gắn sự kiện input và change với debounce
182
- const debouncedConvert = debounce(convertText, 250); // Giảm nhẹ delay
183
- inputBox.addEventListener("input", debouncedConvert);
184
- modeSelect.addEventListener("change", debouncedConvert); // Áp dụng debounce cho cả select
185
-
186
- // Hàm sao chép nâng cao với Clipboard API và fallback
187
- async function copyText() {
188
- if (!outputBox.value.trim()) {
189
- showStatus("Không có nội dung để sao chép.", "error");
190
- return;
191
- }
192
- try {
193
- if (navigator.clipboard && navigator.clipboard.writeText) {
194
- await navigator.clipboard.writeText(outputBox.value);
195
- showStatus("Đã sao chép vào clipboard!");
196
- } else {
197
- // Fallback cho trình duyệt cũ hơn hoặc môi trường không an toàn (non-HTTPS)
198
- outputBox.select(); // Chọn văn bản trong textarea
199
- outputBox.setSelectionRange(0, 99999); // Đối với mobile
200
- document.execCommand("copy");
201
- showStatus("Đã sao chép (sử dụng phương pháp cũ)!");
202
- }
203
- } catch (err) {
204
- console.error("Lỗi khi sao chép: ", err);
205
- showStatus("Lỗi khi sao chép: " + err.message, "error");
206
- }
207
- }
208
-
209
- // Hàm đặt lại
210
- function resetAll() {
211
- inputBox.value = "";
212
- outputBox.value = "";
213
- modeSelect.value = "cqn"; // Giữ lại giá trị mặc định
214
- statusMessageEl.textContent = "";
215
- statusMessageEl.className = "status-message";
216
- inputBox.focus();
217
- }
218
-
219
- // Khởi tạo chuyển đổi nếu có sẵn giá trị trong input khi tải trang (tùy chọn)
220
- // if (inputBox.value.trim()) {
221
- // convertText();
222
- // }
223
- </script>
224
- </body>
225
- </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="vi">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>CVNSS 4.0 ⇄ Chữ Quốc ngữ</title>
6
+ <style>
7
+ body {
8
+ font-family: Segoe UI, Arial, Helvetica, sans-serif;
9
+ background: #f4f6f8;
10
+ margin: 30px;
11
+ text-align: center;
12
+ color: #333;
13
+ }
14
+ h1 {
15
+ margin-bottom: 18px;
16
+ font-size: 26px;
17
+ color: #2c3e50;
18
+ }
19
+ .wrap {
20
+ display: flex;
21
+ gap: 24px;
22
+ flex-wrap: wrap;
23
+ justify-content: center;
24
+ margin-bottom: 14px;
25
+ }
26
+ textarea {
27
+ width: 400px;
28
+ height: 210px;
29
+ padding: 12px;
30
+ font-size: 16px;
31
+ border: 1px solid #dcdcdc;
32
+ border-radius: 8px;
33
+ resize: vertical;
34
+ box-shadow: 0 1px 3px rgba(0,0,0,0.06);
35
+ box-sizing: border-box;
36
+ }
37
+ textarea:focus {
38
+ border-color: #3498db;
39
+ box-shadow: 0 0 0 2px rgba(52,152,219,0.12);
40
+ outline: none;
41
+ }
42
+ .controls {
43
+ display: flex;
44
+ gap: 12px;
45
+ justify-content: center;
46
+ margin-top: 6px;
47
+ }
48
+ button, .mode-select {
49
+ padding: 8px 18px;
50
+ font-size: 15px;
51
+ border-radius: 6px;
52
+ cursor: pointer;
53
+ border: 1px solid #dcdcdc;
54
+ }
55
+ button {
56
+ color: #fff;
57
+ background-color: #3498db;
58
+ border: none;
59
+ min-width: 90px;
60
+ transition: filter 0.15s;
61
+ }
62
+ button:hover { filter: brightness(1.1);}
63
+ button:active { filter: brightness(0.98);}
64
+ .reset { background: #e74c3c;}
65
+ .mode-select { background: #fff; min-width: 180px;}
66
+ .mode-select:focus {
67
+ border-color: #3498db;
68
+ box-shadow: 0 0 0 2px rgba(52,152,219,0.09);
69
+ outline: none;
70
+ }
71
+ .status-message {
72
+ margin-top: 12px;
73
+ font-size: 13px;
74
+ min-height: 20px;
75
+ }
76
+ .status-success { color: #27ae60;}
77
+ .status-error { color: #c0392b;}
78
+ </style>
79
+ </head>
80
+ <body>
81
+ <h1>CVNSS 4.0 Chữ Quốc ngữ</h1>
82
+
83
+ <div class="wrap">
84
+ <textarea id="inputText" placeholder="Nhập văn bản (CVNSS hoặc CQN)"></textarea>
85
+ <textarea id="outputText" placeholder="Kết quả chuyển đổi" readonly></textarea>
86
+ </div>
87
+
88
+ <div class="controls">
89
+ <select id="mode" class="mode-select">
90
+ <option value="cvn">CVNSS 4.0 → Chữ Quốc ngữ</option>
91
+ <option value="cqn">Chữ Quốc ngữ CVNSS 4.0</option>
92
+ </select>
93
+ <button onclick="copyText()">Sao chép</button>
94
+ <button class="reset" onclick="resetAll()">Đặt lại</button>
95
+ </div>
96
+ <div id="statusMessage" class="status-message"></div>
97
+
98
+ <script src="cvnss4.0-converter.js"></script>
99
+ <script>
100
+ const inputBox = document.getElementById("inputText");
101
+ const outputBox = document.getElementById("outputText");
102
+ const modeSelect = document.getElementById("mode");
103
+ const statusMessageEl = document.getElementById("statusMessage");
104
+
105
+ function convertText() {
106
+ const input = inputBox.value;
107
+ const mode = modeSelect.value;
108
+ if (!input.trim()) {
109
+ outputBox.value = "";
110
+ return;
111
+ }
112
+ try {
113
+ const result = CVNSSConverter.convert(input, mode);
114
+ // Hiển thị kết quả tương ứng với hướng chuyển đổi
115
+ outputBox.value = mode === "cvn" ? result.cqn : result.cvn;
116
+ } catch (e) {
117
+ outputBox.value = "";
118
+ showStatus("Lỗi chuyển đổi: " + e.message, "error");
119
+ }
120
+ }
121
+
122
+ inputBox.addEventListener("input", convertText);
123
+ modeSelect.addEventListener("change", convertText);
124
+
125
+ async function copyText() {
126
+ if (!outputBox.value.trim()) {
127
+ showStatus("Không nội dung để sao chép.", "error");
128
+ return;
129
+ }
130
+ try {
131
+ await navigator.clipboard.writeText(outputBox.value);
132
+ showStatus("Đã sao chép vào clipboard!", "success");
133
+ } catch {
134
+ outputBox.select();
135
+ document.execCommand("copy");
136
+ showStatus("Đã sao chép (cách cũ)!", "success");
137
+ }
138
+ }
139
+
140
+ function resetAll() {
141
+ inputBox.value = "";
142
+ outputBox.value = "";
143
+ modeSelect.value = "cvn";
144
+ statusMessageEl.textContent = "";
145
+ inputBox.focus();
146
+ }
147
+
148
+ function showStatus(message, type="success") {
149
+ statusMessageEl.textContent = message;
150
+ statusMessageEl.className = `status-message status-${type}`;
151
+ setTimeout(() => { statusMessageEl.textContent = ""; }, 2000);
152
+ }
153
+ </script>
154
+ </body>
155
+ </html>