2ch commited on
Commit
d9a4de4
·
verified ·
1 Parent(s): 46b0dd3

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +440 -68
index.html CHANGED
@@ -1,79 +1,451 @@
1
  <!DOCTYPE html>
2
- <html>
3
  <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 
5
  <title>Заменитель буков</title>
6
- <link rel="stylesheet" href="style.css" />
7
- </head>
8
- <body>
9
- <script type='text/javascript'>
10
- function simbols(text) {
11
- var str = document.getElementById('text').value;
12
- str = str.replace(/--/g, '—');
13
- str = str.replace('*', '*');
14
- str = str.replace(/ - /g, ' — ');
15
- str = str.replace(/- /g, '— ');
16
- str = str.replace(/Оу/g, 'Ѹ');
17
- str = str.replace(/оу/g, 'ѹ');
18
- str = str.replace(/(")(.+?)(")/g, '«$2»');
19
- document.getElementById(text).value = str;
20
- }
21
-
22
- function letter() {
23
- var text = document.getElementById('text').value;
24
- var transl = new Array();
25
- transl['А'] = 'A'; transl['а'] = 'a';
26
- transl['Б'] = 'Ƃ'; transl['б'] = 'б';
27
- transl['В'] = 'B'; transl['в'] = 'ʙ';
28
- transl['Г'] = 'Γ'; transl['г'] = 'ᴦ';
29
- transl['Е'] = 'E'; transl['е'] = 'e';
30
- transl['Ё'] = 'Ë'; transl['ё'] = 'ë';
31
- transl['З'] = 'З'; transl['з'] = 'з';
32
- transl['И'] = 'Ͷ'; transl['и'] = 'ᴎ';
33
- transl['Й'] = 'Ѝ'; transl['й'] = 'ѝ';
34
- transl['К'] = 'K'; transl['к'] = 'ĸ';
35
- transl['Л'] = 'Ʌ'; transl['л'] = 'ᴫ';
36
- transl['М'] = 'M'; transl['м'] = 'ᴍ';
37
- transl['Н'] = 'Н'; transl['н'] = 'ʜ';
38
- transl['О'] = 'O'; transl['о'] = 'o';
39
- transl['П'] = 'Ⲡ'; transl['п'] = 'ⲡ';
40
- transl['Р'] = 'P'; transl['р'] = 'p';
41
- transl['С'] = 'C'; transl['с'] = 'c';
42
- transl['Т'] = 'T'; transl['т'] = 'ⲧ';
43
- transl['У'] = 'Ꭹ'; transl['у'] = 'y';
44
- transl['Ф'] = 'Ⲫ'; transl['ф'] = 'ⲫ';
45
- transl['Х'] = 'X'; transl['х'] = 'x';
46
- transl['Ч'] = 'Ч'; transl['ч'] = 'ч';
47
- transl['Ш'] = 'Ɯ'; transl['ш'] = 'ꟺ';
48
- transl['Щ'] = 'Щ'; transl['щ'] = 'ɰ';
49
- transl['Ъ'] = 'Ѣ'; transl['ъ'] = 'Ꙏ';
50
- transl['Ь'] = 'Ⱃ'; transl['ь'] = 'ь';
51
- transl['Я'] = 'Я'; transl['я'] = 'ᴙ';
52
-
53
- var result = '';
54
- for (i = 0; i < text.length; i++) {
55
- if (transl[text[i]] != undefined) {
56
- result += transl[text[i]];
57
- } else {
58
- result += text[i];
59
- }
60
- }
61
- document.getElementById('text').value = result;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  }
63
 
64
- function copy() {
65
- var copyText = document.getElementById("text");
66
- copyText.select();
67
- document.execCommand("copy");
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  }
69
- </script>
70
- <div id="page">
71
- <div>
72
- <textarea id="text" placeholder="Вставь сюда свою копипасту и жми на кнопку. Готовый текст добавится в буфер обмена сразу."></textarea>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  </div>
74
- <div id="go">
75
- <input id="button" type="button" value="сделать" onclick="simbols('text'),letter(),copy()">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  </div>
77
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  </body>
79
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="ru">
3
  <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Заменитель буков</title>
7
+ <style>
8
+ :root {
9
+ --bg-primary: #0d1117;
10
+ --bg-secondary: #161b22;
11
+ --bg-tertiary: #21262d;
12
+ --border: #30363d;
13
+ --text-primary: #e6edf3;
14
+ --text-secondary: #8b949e;
15
+ --accent: #58a6ff;
16
+ --accent-hover: #79b8ff;
17
+ --accent-glow: rgba(88, 166, 255, 0.15);
18
+ --success: #3fb950;
19
+ --toggle-bg: #30363d;
20
+ --toggle-active: #58a6ff;
21
+ --radius: 12px;
22
+ --transition: 0.25s ease;
23
+ }
24
+
25
+ *, *::before, *::after {
26
+ margin: 0;
27
+ padding: 0;
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ body {
32
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
33
+ 'Helvetica Neue', Arial, sans-serif;
34
+ background: var(--bg-primary);
35
+ color: var(--text-primary);
36
+ min-height: 100vh;
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: center;
40
+ padding: 20px;
41
+ }
42
+
43
+ .container {
44
+ width: 100%;
45
+ max-width: 700px;
46
+ display: flex;
47
+ flex-direction: column;
48
+ gap: 20px;
49
+ }
50
+
51
+ .header {
52
+ text-align: center;
53
+ }
54
+
55
+ .header h1 {
56
+ font-size: 1.5rem;
57
+ font-weight: 600;
58
+ letter-spacing: -0.02em;
59
+ color: var(--text-primary);
60
+ }
61
+
62
+ .header p {
63
+ font-size: 0.85rem;
64
+ color: var(--text-secondary);
65
+ margin-top: 6px;
66
+ }
67
+
68
+ .controls {
69
+ display: flex;
70
+ justify-content: center;
71
+ gap: 32px;
72
+ flex-wrap: wrap;
73
+ }
74
+
75
+ .control-group {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 10px;
79
+ }
80
+
81
+ .control-label {
82
+ font-size: 0.8rem;
83
+ color: var(--text-secondary);
84
+ text-transform: uppercase;
85
+ letter-spacing: 0.05em;
86
+ font-weight: 500;
87
+ }
88
+
89
+ .toggle {
90
+ position: relative;
91
+ display: flex;
92
+ background: var(--toggle-bg);
93
+ border-radius: 8px;
94
+ padding: 3px;
95
+ gap: 2px;
96
+ }
97
+
98
+ .toggle input[type="radio"] {
99
+ position: absolute;
100
+ opacity: 0;
101
+ pointer-events: none;
102
+ }
103
+
104
+ .toggle label {
105
+ padding: 6px 14px;
106
+ font-size: 0.8rem;
107
+ font-weight: 500;
108
+ color: var(--text-secondary);
109
+ border-radius: 6px;
110
+ cursor: pointer;
111
+ transition: all var(--transition);
112
+ user-select: none;
113
+ white-space: nowrap;
114
+ }
115
+
116
+ .toggle input[type="radio"]:checked + label {
117
+ background: var(--accent);
118
+ color: #fff;
119
+ box-shadow: 0 2px 8px rgba(88, 166, 255, 0.3);
120
+ }
121
+
122
+ .toggle label:hover {
123
+ color: var(--text-primary);
124
+ }
125
+
126
+ .editor {
127
+ position: relative;
128
+ }
129
+
130
+ #text {
131
+ width: 100%;
132
+ min-height: 280px;
133
+ padding: 18px 20px;
134
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
135
+ font-size: 0.95rem;
136
+ line-height: 1.6;
137
+ color: var(--text-primary);
138
+ background: var(--bg-secondary);
139
+ border: 1px solid var(--border);
140
+ border-radius: var(--radius);
141
+ resize: vertical;
142
+ outline: none;
143
+ transition: border-color var(--transition), box-shadow var(--transition);
144
+ }
145
+
146
+ #text::placeholder {
147
+ color: var(--text-secondary);
148
+ opacity: 0.6;
149
+ }
150
+
151
+ #text:focus {
152
+ border-color: var(--accent);
153
+ box-shadow: 0 0 0 3px var(--accent-glow);
154
+ }
155
+
156
+ .editor-footer {
157
+ display: flex;
158
+ justify-content: space-between;
159
+ align-items: center;
160
+ margin-top: 8px;
161
+ padding: 0 4px;
162
+ }
163
+
164
+ .char-count {
165
+ font-size: 0.75rem;
166
+ color: var(--text-secondary);
167
+ font-variant-numeric: tabular-nums;
168
+ }
169
+
170
+ .actions {
171
+ display: flex;
172
+ justify-content: center;
173
+ }
174
+
175
+ .btn {
176
+ padding: 12px 48px;
177
+ font-size: 0.95rem;
178
+ font-weight: 600;
179
+ color: #fff;
180
+ background: var(--accent);
181
+ border: none;
182
+ border-radius: 10px;
183
+ cursor: pointer;
184
+ transition: all var(--transition);
185
+ letter-spacing: 0.01em;
186
+ position: relative;
187
+ overflow: hidden;
188
+ }
189
+
190
+ .btn:hover {
191
+ background: var(--accent-hover);
192
+ transform: translateY(-1px);
193
+ box-shadow: 0 4px 16px rgba(88, 166, 255, 0.3);
194
+ }
195
+
196
+ .btn:active {
197
+ transform: translateY(0);
198
+ }
199
+
200
+ .btn.success {
201
+ background: var(--success);
202
+ }
203
+
204
+ .toast {
205
+ position: fixed;
206
+ bottom: 30px;
207
+ left: 50%;
208
+ transform: translateX(-50%) translateY(80px);
209
+ background: var(--bg-tertiary);
210
+ color: var(--text-primary);
211
+ padding: 12px 24px;
212
+ border-radius: 10px;
213
+ font-size: 0.85rem;
214
+ font-weight: 500;
215
+ border: 1px solid var(--border);
216
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
217
+ opacity: 0;
218
+ transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
219
+ pointer-events: none;
220
+ z-index: 100;
221
+ }
222
+
223
+ .toast.visible {
224
+ opacity: 1;
225
+ transform: translateX(-50%) translateY(0);
226
  }
227
 
228
+ .toast .icon {
229
+ margin-right: 8px;
230
+ }
231
+
232
+ @media (max-width: 480px) {
233
+ .controls {
234
+ gap: 16px;
235
+ }
236
+
237
+ .btn {
238
+ width: 100%;
239
+ }
240
+
241
+ #text {
242
+ min-height: 220px;
243
+ font-size: 0.9rem;
244
+ }
245
  }
246
+ </style>
247
+ </head>
248
+ <body>
249
+
250
+ <div class="container">
251
+
252
+ <div class="header">
253
+ <h1>заменитель буков</h1>
254
+ <p>заменяет символы на визуально идентичные юникод-аналоги</p>
255
+ </div>
256
+
257
+ <div class="controls">
258
+ <div class="control-group">
259
+ <span class="control-label">язык</span>
260
+ <div class="toggle">
261
+ <input type="radio" name="lang" id="lang-ru" value="ru" checked>
262
+ <label for="lang-ru">RU</label>
263
+ <input type="radio" name="lang" id="lang-en" value="en">
264
+ <label for="lang-en">EN</label>
265
+ </div>
266
  </div>
267
+
268
+ <div class="control-group">
269
+ <span class="control-label">режим</span>
270
+ <div class="toggle">
271
+ <input type="radio" name="mode" id="mode-light" value="light" checked>
272
+ <label for="mode-light">слабый</label>
273
+ <input type="radio" name="mode" id="mode-full" value="full">
274
+ <label for="mode-full">сильный</label>
275
+ </div>
276
+ </div>
277
+ </div>
278
+
279
+ <div class="editor">
280
+ <textarea id="text" placeholder="вставь текст и нажми кнопку или Ctrl+Enter / Cmd+Enter для быстрого запуска: результат скопируется в буфер обмена автоматически"></textarea>
281
+ <div class="editor-footer">
282
+ <span class="char-count" id="charCount">0 сим.</span>
283
  </div>
284
  </div>
285
+
286
+ <div class="actions">
287
+ <button class="btn" id="processBtn" type="button">сделать</button>
288
+ </div>
289
+
290
+ </div>
291
+
292
+ <div class="toast" id="toast"></div>
293
+
294
+ <script>
295
+ (() => {
296
+ 'use strict';
297
+
298
+ const maps = {
299
+ ruLight: {
300
+ 'А': 'A', 'а': 'a', 'В': 'B', 'Е': 'E', 'е': 'e',
301
+ 'З': '3', 'й': 'ѝ', 'К': 'K', 'Л': 'Ʌ', 'М': 'M',
302
+ 'Н': 'H', 'О': 'O', 'о': 'o', 'П': 'Ⲡ', 'п': 'ⲡ',
303
+ 'Р': 'P', 'р': 'p', 'С': 'C', 'с': 'c', 'Т': 'T',
304
+ 'у': 'y', 'Х': 'X', 'х': 'x', 'Я': 'Я', 'я': 'ᴙ'
305
+ },
306
+ ruFull: {
307
+ 'А': 'A', 'а': 'a', 'Б': 'Ƃ', 'б': 'б', 'В': 'B', 'в': 'ʙ',
308
+ 'Г': 'Γ', 'г': 'ᴦ', 'Е': 'E', 'е': 'e', 'Ё': 'Ë', 'ё': 'ë',
309
+ 'З': 'З', 'з': 'з', 'И': 'Ͷ', 'и': 'ᴎ', 'Й': 'Ѝ', 'й': 'ѝ',
310
+ 'К': 'K', 'к': 'ĸ', 'Л': 'Ʌ', 'л': 'ᴫ', 'М': 'M', 'м': 'ᴍ',
311
+ 'Н': 'Н', 'н': 'ʜ', 'О': 'O', 'о': 'o', 'П': 'Ⲡ', 'п': 'ⲡ',
312
+ 'Р': 'P', 'р': 'p', 'С': 'C', 'с': 'c', 'Т': 'T', 'т': 'ⲧ',
313
+ 'У': 'Ꭹ', 'у': 'y', 'Ф': 'Ⲫ', 'ф': 'ⲫ', 'Х': 'X', 'х': 'x',
314
+ 'Ч': 'Ч', 'ч': 'ч', 'Ш': 'Ɯ', 'ш': 'ꟺ', 'Щ': 'Щ', 'щ': 'ɰ',
315
+ 'Ъ': 'Ѣ', 'ъ': 'Ꙏ', 'Ь': 'Ⱃ', 'ь': 'ь', 'Я': 'Я', 'я': 'ᴙ'
316
+ },
317
+ enLight: {
318
+ 'A': 'А', 'a': 'а', 'B': 'В', 'C': 'С', 'c': 'с',
319
+ 'E': 'Е', 'e': 'е', 'g': 'ɡ', 'H': 'Н', 'I': 'І',
320
+ 'i': 'і', 'J': 'Ј', 'j': 'ј', 'K': 'К', 'M': 'М',
321
+ 'n': 'ո', 'O': 'О', 'o': 'о', 'P': 'Р', 'p': 'р',
322
+ 'S': 'Ѕ', 's': 'ѕ', 'T': 'Т', 'U': 'Ս', 'w': 'ᴡ',
323
+ 'X': 'Х', 'x': 'х', 'Y': 'У', 'y': 'у', 'Z': 'Ζ',
324
+ 'z': 'ⲍ'
325
+ },
326
+ enFull: {
327
+ 'A': 'А', 'a': 'а', 'B': 'В', 'b': 'ɓ', 'C': 'С', 'c': 'с',
328
+ 'D': 'D', 'd': 'ԁ', 'E': 'Е', 'e': 'е', 'F': 'Ϝ', 'f': 'ƒ',
329
+ 'G': 'Ꮆ', 'g': 'ɡ', 'H': 'Н', 'h': 'һ', 'I': 'І', 'i': 'і',
330
+ 'J': 'Ј', 'j': 'ј', 'K': 'К', 'k': 'κ', 'L': 'Ꝉ', 'l': 'ⅼ',
331
+ 'M': 'М', 'm': 'ᴍ', 'N': 'Ɲ', 'n': 'ո', 'O': 'О', 'o': 'о',
332
+ 'P': 'Р', 'p': 'р', 'Q': 'Ϙ', 'q': 'ɋ', 'R': 'Ɍ', 'r': 'ꭈ',
333
+ 'S': 'Ѕ', 's': 'ѕ', 'T': 'Т', 't': 'τ', 'U': 'Ս', 'u': 'υ',
334
+ 'V': 'Ѵ', 'v': 'ѵ', 'W': 'Ⱳ', 'w': 'ᴡ', 'X': 'Х', 'x': 'х',
335
+ 'Y': 'У', 'y': 'у', 'Z': 'Ζ', 'z': 'ⲍ'
336
+ },
337
+ };
338
+
339
+
340
+ function getSettings() {
341
+ const lang = document.querySelector('input[name="lang"]:checked').value;
342
+ const mode = document.querySelector('input[name="mode"]:checked').value;
343
+ return { lang, mode };
344
+ }
345
+
346
+ function getActiveMap({ lang, mode }) {
347
+ const key = `${lang}${mode.charAt(0).toUpperCase() + mode.slice(1)}`; // ruLight, enFull …
348
+ return maps[key] ?? {};
349
+ }
350
+
351
+ function fixTypography(str) {
352
+ return str
353
+ .replace(/--/g, '—')
354
+ .replace(/ - /g, ' — ')
355
+ .replace(/(?<=\S)- /g, '— ')
356
+ .replace(/"([^"]+)"/g, '«$1»');
357
+ }
358
+
359
+ function replaceByMap(str, map) {
360
+ if (!Object.keys(map).length) return str;
361
+
362
+ const keys = Object.keys(map).sort((a, b) => b.length - a.length);
363
+ const escaped = keys.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
364
+ const regex = new RegExp(escaped.join('|'), 'g');
365
+
366
+ return str.replace(regex, match => map[match] ?? match);
367
+ }
368
+
369
+ async function copyToClipboard(text) {
370
+ if (navigator.clipboard?.writeText) {
371
+ return navigator.clipboard.writeText(text);
372
+ }
373
+
374
+ const ta = Object.assign(document.createElement('textarea'), {
375
+ value: text,
376
+ style: 'position:fixed;opacity:0',
377
+ });
378
+ document.body.appendChild(ta);
379
+ ta.select();
380
+ document.execCommand('copy');
381
+ ta.remove();
382
+ }
383
+
384
+ function showToast(message, duration = 2000) {
385
+ const toast = document.getElementById('toast');
386
+ toast.innerHTML = message;
387
+ toast.classList.add('visible');
388
+ clearTimeout(toast._timer);
389
+ toast._timer = setTimeout(() => toast.classList.remove('visible'), duration);
390
+ }
391
+
392
+ function updateCharCount() {
393
+ const len = textEl.value.length;
394
+ const word = len === 1 ? 'символ' : (len > 1 && len < 5 ? 'символа' : 'символов');
395
+ charCountEl.textContent = `${len} ${word}`;
396
+ }
397
+
398
+
399
+ async function process() {
400
+ const settings = getSettings();
401
+ const map = getActiveMap(settings);
402
+
403
+ let text = textEl.value;
404
+
405
+ if (!text.trim()) {
406
+ showToast('⚠️ поле пустое');
407
+ return;
408
+ }
409
+
410
+ if (!Object.keys(map).length) {
411
+ showToast('⚠️ карта замен в коде пуста');
412
+ return;
413
+ }
414
+
415
+ text = fixTypography(text);
416
+
417
+ text = replaceByMap(text, map);
418
+
419
+ textEl.value = text;
420
+ updateCharCount();
421
+
422
+ try {
423
+ await copyToClipboard(text);
424
+ showToast('<span class="icon">✅</span>готово и скопировано');
425
+ btn.classList.add('success');
426
+ setTimeout(() => btn.classList.remove('success'), 1200);
427
+ } catch {
428
+ showToast('<span class="icon">⚠️</span>готово, но копирование не удалось');
429
+ }
430
+ }
431
+
432
+ const textEl = document.getElementById('text');
433
+ const btn = document.getElementById('processBtn');
434
+ const charCountEl = document.getElementById('charCount');
435
+
436
+ btn.addEventListener('click', process);
437
+ textEl.addEventListener('input', updateCharCount);
438
+
439
+ textEl.addEventListener('keydown', (e) => {
440
+ if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
441
+ e.preventDefault();
442
+ process();
443
+ }
444
+ });
445
+
446
+ updateCharCount();
447
+ })();
448
+ </script>
449
+
450
  </body>
451
  </html>