AnnLior commited on
Commit
3a2aaad
·
verified ·
1 Parent(s): f4215af

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +304 -19
index.html CHANGED
@@ -1,19 +1,304 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </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: #f4f5f7;
10
+ --panel: #ffffff;
11
+ --border: #d9dce1;
12
+ --text: #202124;
13
+ --muted: #6b7280;
14
+ --input: #ffffff;
15
+ --btn: #111827;
16
+ --btn-text: #ffffff;
17
+ --code: #f8f9fa;
18
+ }
19
+ .dark {
20
+ --bg: #1a1b1e;
21
+ --panel: #252629;
22
+ --border: #3a3b3e;
23
+ --text: #e4e4e7;
24
+ --muted: #9ca3af;
25
+ --input: #1a1b1e;
26
+ --btn: #3b82f6;
27
+ --btn-text: #ffffff;
28
+ --code: #1f2023;
29
+ }
30
+ * { box-sizing: border-box; }
31
+ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; background: var(--bg); color: var(--text); transition: background .2s, color .2s; }
32
+ .container { max-width: 1200px; margin: 0 auto; padding: 24px; }
33
+ .top { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; }
34
+ h1 { margin: 0 0 6px; font-size: 26px; }
35
+ .sub { color: var(--muted); margin: 0 0 16px; line-height: 1.5; }
36
+ textarea { width: 100%; min-height: 300px; resize: vertical; border: 1px solid var(--border); border-radius: 10px; padding: 12px; font: 14px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: var(--input); color: var(--text); }
37
+ .controls { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; margin: 14px 0; }
38
+ label { font-size: 13px; display: flex; align-items: center; gap: 6px; cursor: pointer; }
39
+ button { border: 0; border-radius: 8px; padding: 10px 16px; font-size: 14px; cursor: pointer; font-weight: 600; }
40
+ .primary { background: var(--btn); color: var(--btn-text); }
41
+ .ghost { background: var(--panel); color: var(--text); border: 1px solid var(--border); }
42
+ .theme { background: var(--panel); color: var(--text); border: 1px solid var(--border); }
43
+ .row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 8px; }
44
+ @media (max-width: 800px) { .row { grid-template-columns: 1fr; } }
45
+ section { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
46
+ section h2 { margin: 0 0 12px; font-size: 17px; }
47
+ .preview { min-height: 260px; max-height: 560px; overflow: auto; border: 1px solid var(--border); border-radius: 10px; padding: 14px; line-height: 1.75; font-size: 15px; background: var(--input); color: var(--text); }
48
+ .preview p { margin: 0 0 12px; }
49
+ .preview p:last-child { margin-bottom: 0; }
50
+ .preview em { font-style: italic; }
51
+ .preview mark { border-radius: 3px; padding: 0 2px; }
52
+ .codehead { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; gap: 10px; flex-wrap: wrap; }
53
+ #code { width: 100%; min-height: 260px; max-height: 560px; resize: vertical; font: 12.5px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: var(--code); border: 1px solid var(--border); border-radius: 10px; padding: 12px; color: var(--text); }
54
+ .stats { font-size: 13px; color: var(--muted); }
55
+ </style>
56
+ </head>
57
+ <body>
58
+ <div class="container">
59
+ <div class="top">
60
+ <h1>Обработка стихов</h1>
61
+ <button class="theme" id="theme" onclick="toggleTheme()">🌙 Тёмная</button>
62
+ </div>
63
+ <p class="sub">
64
+ Вставьте текст...
65
+ </p>
66
+
67
+ <textarea id="input" placeholder="Вставьте текст… Например:&#10;4 Не делай себе кумира и никакого изображения…&#10;7 Не произноси имени Господа… (Исх. 20:1-7)."></textarea>
68
+
69
+ <div class="controls">
70
+ <label><input type="checkbox" id="handleBr" checked> Воспринимать &lt;br&gt; как перенос строки</label>
71
+ <label><input type="checkbox" id="stripTags" checked> Убирать остальные HTML-теги</label>
72
+ <button class="primary" id="run">Обработать</button>
73
+ <button class="ghost" id="example">Пример</button>
74
+ <button class="ghost" id="clear">Очистить</button>
75
+ </div>
76
+
77
+ <div class="row">
78
+ <section>
79
+ <h2>Предпросмотр</h2>
80
+ <div class="preview" id="preview">Текст появится здесь.</div>
81
+ </section>
82
+ <section>
83
+ <h2>HTML-код</h2>
84
+ <div class="codehead">
85
+ <span class="stats" id="stats"></span>
86
+ <button class="ghost" id="copy">📋 Копировать</button>
87
+ </div>
88
+ <textarea id="code" readonly></textarea>
89
+ </section>
90
+ </div>
91
+ </div>
92
+
93
+ <script>
94
+ const $ = id => document.getElementById(id);
95
+
96
+ function toggleTheme() {
97
+ document.body.classList.toggle("dark");
98
+ const dark = document.body.classList.contains("dark");
99
+ try { localStorage.setItem("theme", dark ? "dark" : "light"); } catch (e) {}
100
+ $("theme").textContent = dark ? "☀️ Светлая" : "🌙 Тёмная";
101
+ }
102
+ window.toggleTheme = toggleTheme;
103
+ try {
104
+ if (localStorage.getItem("theme") === "dark") {
105
+ document.body.classList.add("dark");
106
+ $("theme").textContent = "☀️ Светлая";
107
+ }
108
+ } catch (e) {}
109
+
110
+ function escapeHtml(s) {
111
+ return String(s)
112
+ .replace(/&/g, "&amp;")
113
+ .replace(/</g, "&lt;")
114
+ .replace(/>/g, "&gt;")
115
+ .replace(/"/g, "&quot;");
116
+ }
117
+
118
+ function decodeEntities(s) {
119
+ const ta = document.createElement("textarea");
120
+ ta.innerHTML = s;
121
+ return ta.value;
122
+ }
123
+
124
+ /* Цвета выделения по номеру тега <a id="N" name="N"> */
125
+ const HL_COLORS = {
126
+ 1: "#ccffcc", // салатовый
127
+ 2: "#cce5ff", // голубой
128
+ 3: "#fff59a", // жёлтый
129
+ 4: "#ffd1dc", // розовый
130
+ 5: "#ffc4a3" // коралловый
131
+ };
132
+
133
+ /* Замена плейсхолдеров на цветные <mark> */
134
+ function applyHighlights(s) {
135
+ let out = s.replace(/@@HL(\d+)@@/g, (m, n) => {
136
+ const c = HL_COLORS[n];
137
+ return c ? `<mark style="background-color:${c}">` : "";
138
+ });
139
+ return out.split("@@/HL@@").join("</mark>");
140
+ }
141
+
142
+ /* Протаскивает открытое выделение через следующие строки,
143
+ пока не встретится закрывающий плейсхолдер @@/HL@@. */
144
+ function propagateHighlights(lines) {
145
+ const result = [];
146
+ let active = null;
147
+ for (let line of lines) {
148
+ if (!line.trim()) {
149
+ result.push(line);
150
+ continue;
151
+ }
152
+ let processed = line;
153
+ if (active !== null && !/^@@HL\d+@@/.test(processed)) {
154
+ processed = "@@HL" + active + "@@" + processed;
155
+ }
156
+ let state = active;
157
+ const re = /@@HL(\d+)@@|@@\/HL@@/g;
158
+ let m;
159
+ while ((m = re.exec(processed)) !== null) {
160
+ state = (m[0] === "@@/HL@@") ? null : Number(m[1]);
161
+ }
162
+ result.push(processed);
163
+ active = state;
164
+ }
165
+ return result;
166
+ }
167
+
168
+ /* Обработка одной строки */
169
+ function processLine(line) {
170
+ if (!line) return "";
171
+
172
+ // Сначала сохраняем все плейсхолдеры выделения
173
+ let lead = "";
174
+ let body = line;
175
+ const leadMatch = line.match(/^((?:@@HL\d+@@|@@\/HL@@)+)/);
176
+ if (leadMatch) {
177
+ lead = leadMatch[1];
178
+ body = line.slice(lead.length);
179
+ }
180
+ let tail = "";
181
+ const tailMatch = body.match(/((?:@@\/HL@@|@@HL\d+@@)+)$/);
182
+ if (tailMatch) {
183
+ tail = tailMatch[1];
184
+ body = body.slice(0, body.length - tail.length);
185
+ }
186
+
187
+ // Ищем строку, начинающуюся с цифры
188
+ const m = body.match(/^(\d+[.)]?)\s+(.*)$/);
189
+ let out;
190
+
191
+ if (!m) {
192
+ out = escapeHtml(lead + body + tail);
193
+ } else {
194
+ const number = m[1];
195
+ let rest = m[2];
196
+
197
+ // Ищем скобки в конце строки (включая точку после скобок)
198
+ const parenMatch = rest.match(/^(.*?)\s*(\([^)]*\)\.?)\s*$/);
199
+
200
+ if (parenMatch) {
201
+ const beforeParen = parenMatch[1].trim();
202
+ const paren = parenMatch[2];
203
+
204
+ if (beforeParen) {
205
+ // Текст до скобок — курсивом, скобки — обычным текстом
206
+ out = `${lead}<em>${escapeHtml(number)} ${escapeHtml(beforeParen)}</em> ${escapeHtml(paren)}${tail}`;
207
+ } else {
208
+ // Только скобки — всё обычным текстом
209
+ out = escapeHtml(lead + number + ' ' + rest + tail);
210
+ }
211
+ } else {
212
+ // Нет скобок — всё курсивом
213
+ out = `${lead}<em>${escapeHtml(number)} ${escapeHtml(rest)}</em>${tail}`;
214
+ }
215
+ }
216
+
217
+ // Применяем подсветку
218
+ return applyHighlights(out);
219
+ }
220
+
221
+ function process() {
222
+ let raw = $("input").value;
223
+ raw = decodeEntities(raw);
224
+
225
+ if ($("handleBr").checked) raw = raw.replace(/<br\s*\/?>/gi, "\n");
226
+
227
+ // Сохраняем теги выделения
228
+ raw = raw.replace(/<a\s+[^>]*id="?(\d+)"?[^>]*>/gi, "@@HL$1@@");
229
+ raw = raw.replace(/<\/a>/gi, "@@/HL@@");
230
+
231
+ if ($("stripTags").checked) {
232
+ raw = raw.replace(/<[^>]+>/g, "");
233
+ }
234
+
235
+ raw = raw.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
236
+
237
+ raw = propagateHighlights(raw.split("\n")).join("\n");
238
+
239
+ const blocks = raw.split(/\n\s*\n+/).map(b => b.trim()).filter(Boolean);
240
+ const paragraphs = blocks.map(block => {
241
+ const lines = block.split("\n").map(l => l.trim()).filter(Boolean);
242
+ return lines.map(processLine).join("<br />");
243
+ });
244
+
245
+ const htmlPreview = paragraphs.map(p => `<p>${p}</p>`).join("\n");
246
+ const htmlCode = paragraphs.join("<br />");
247
+
248
+ $("preview").innerHTML = htmlPreview;
249
+
250
+ // Восстанавливаем теги <a> для HTML-кода
251
+ let codeHtml = htmlCode;
252
+ codeHtml = codeHtml.replace(/<mark style="background-color:#[^"]*">/g, (match) => {
253
+ const colorMap = {
254
+ '#ccffcc': '1',
255
+ '#cce5ff': '2',
256
+ '#fff59a': '3',
257
+ '#ffd1dc': '4',
258
+ '#ffc4a3': '5'
259
+ };
260
+ const color = match.match(/#[^"]*/)[0];
261
+ const id = colorMap[color] || '1';
262
+ return `<a id="${id}" name="${id}">`;
263
+ });
264
+ codeHtml = codeHtml.replace(/<\/mark>/g, '</a>');
265
+
266
+ $("code").value = codeHtml;
267
+
268
+ const verseCount = (htmlPreview.match(/<em>/g) || []).length;
269
+ $("stats").textContent = `Стихов: ${verseCount} · Абзацев: ${paragraphs.length}`;
270
+ }
271
+ window.process = process;
272
+
273
+ $("run").addEventListener("click", process);
274
+ $("input").addEventListener("keydown", e => {
275
+ if ((e.ctrlKey || e.metaKey) && e.key === "Enter") process();
276
+ });
277
+
278
+ $("clear").addEventListener("click", () => {
279
+ $("input").value = "";
280
+ $("preview").textContent = "Текст появится здесь.";
281
+ $("code").value = "";
282
+ $("stats").textContent = "";
283
+ });
284
+
285
+ $("example").addEventListener("click", () => {
286
+ $("input").value = ""
287
+ process();
288
+ });
289
+
290
+ $("copy").addEventListener("click", async () => {
291
+ const code = $("code").value;
292
+ if (!code) return;
293
+ try {
294
+ await navigator.clipboard.writeText(code);
295
+ $("copy").textContent = "✅ Скопировано";
296
+ setTimeout(() => $("copy").textContent = "📋 Копировать", 1500);
297
+ } catch (e) {
298
+ $("code").select();
299
+ document.execCommand("copy");
300
+ }
301
+ });
302
+ </script>
303
+ </body>
304
+ </html>