MarkTheArtist commited on
Commit
f2bcf38
·
verified ·
1 Parent(s): 1231edd

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +410 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Json Validator
3
- emoji: 👁
4
- colorFrom: gray
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: json-validator
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,410 @@
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="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>JSON Formatter & Validator</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .json-key {
11
+ color: #9CDCFE;
12
+ }
13
+ .json-value {
14
+ color: #CE9178;
15
+ }
16
+ .json-string {
17
+ color: #CE9178;
18
+ }
19
+ .json-number {
20
+ color: #B5CEA8;
21
+ }
22
+ .json-boolean {
23
+ color: #569CD6;
24
+ }
25
+ .json-null {
26
+ color: #569CD6;
27
+ }
28
+ .json-punctuation {
29
+ color: #D4D4D4;
30
+ }
31
+ .error-line {
32
+ background-color: rgba(255, 0, 0, 0.1);
33
+ position: relative;
34
+ }
35
+ .error-line::after {
36
+ content: attr(data-error);
37
+ position: absolute;
38
+ left: 0;
39
+ bottom: -20px;
40
+ color: #ff6b6b;
41
+ font-size: 12px;
42
+ font-family: monospace;
43
+ }
44
+ #jsonInput {
45
+ min-height: 200px;
46
+ font-family: 'Courier New', Courier, monospace;
47
+ }
48
+ #formattedJson {
49
+ min-height: 200px;
50
+ font-family: 'Courier New', Courier, monospace;
51
+ white-space: pre-wrap;
52
+ }
53
+ .copy-btn {
54
+ transition: all 0.2s;
55
+ }
56
+ .copy-btn:hover {
57
+ transform: scale(1.1);
58
+ }
59
+ .copy-btn.copied {
60
+ color: #4CAF50;
61
+ }
62
+ </style>
63
+ </head>
64
+ <body class="bg-gray-900 text-gray-100 min-h-screen">
65
+ <div class="container mx-auto px-4 py-8">
66
+ <header class="mb-8 text-center">
67
+ <h1 class="text-4xl font-bold mb-2 bg-gradient-to-r from-purple-400 via-pink-500 to-red-500 bg-clip-text text-transparent">
68
+ JSON Formatter & Validator
69
+ </h1>
70
+ <p class="text-gray-400">Paste, format, validate and beautify your JSON data</p>
71
+ </header>
72
+
73
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
74
+ <!-- Input Section -->
75
+ <div class="bg-gray-800 rounded-lg shadow-lg overflow-hidden">
76
+ <div class="bg-gray-700 px-4 py-3 flex justify-between items-center">
77
+ <h2 class="font-semibold">
78
+ <i class="fas fa-code mr-2 text-purple-400"></i> Input JSON
79
+ </h2>
80
+ <div class="flex space-x-2">
81
+ <button id="clearInput" class="text-gray-400 hover:text-white transition-colors" title="Clear">
82
+ <i class="fas fa-trash-alt"></i>
83
+ </button>
84
+ <button id="minifyBtn" class="text-gray-400 hover:text-white transition-colors" title="Minify">
85
+ <i class="fas fa-compress-alt"></i>
86
+ </button>
87
+ <button id="beautifyBtn" class="text-gray-400 hover:text-white transition-colors" title="Beautify">
88
+ <i class="fas fa-expand-alt"></i>
89
+ </button>
90
+ <button id="validateBtn" class="text-green-400 hover:text-green-300 transition-colors" title="Validate">
91
+ <i class="fas fa-check-circle"></i>
92
+ </button>
93
+ </div>
94
+ </div>
95
+ <div class="p-4 relative">
96
+ <textarea id="jsonInput" class="w-full bg-gray-700 border border-gray-600 rounded p-3 text-gray-200 focus:outline-none focus:ring-2 focus:ring-purple-500 resize-none" placeholder='Paste your JSON here...'></textarea>
97
+ <div id="inputError" class="text-red-400 text-sm mt-2 hidden"></div>
98
+ </div>
99
+ </div>
100
+
101
+ <!-- Output Section -->
102
+ <div class="bg-gray-800 rounded-lg shadow-lg overflow-hidden">
103
+ <div class="bg-gray-700 px-4 py-3 flex justify-between items-center">
104
+ <h2 class="font-semibold">
105
+ <i class="fas fa-eye mr-2 text-blue-400"></i> Formatted JSON
106
+ </h2>
107
+ <div class="flex space-x-2">
108
+ <button id="copyBtn" class="copy-btn text-gray-400 hover:text-white transition-colors" title="Copy to Clipboard">
109
+ <i class="fas fa-copy"></i>
110
+ </button>
111
+ <button id="downloadBtn" class="text-gray-400 hover:text-white transition-colors" title="Download">
112
+ <i class="fas fa-download"></i>
113
+ </button>
114
+ </div>
115
+ </div>
116
+ <div class="p-4">
117
+ <div id="formattedJson" class="w-full bg-gray-700 border border-gray-600 rounded p-3 overflow-auto"></div>
118
+ <div id="validationResult" class="mt-4 p-3 rounded hidden">
119
+ <div class="flex items-start">
120
+ <div class="flex-shrink-0">
121
+ <i id="validationIcon" class="fas fa-check-circle text-2xl"></i>
122
+ </div>
123
+ <div class="ml-3">
124
+ <h3 id="validationTitle" class="text-lg font-medium"></h3>
125
+ <div id="validationDetails" class="mt-1 text-sm"></div>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </div>
132
+
133
+ <!-- Sample JSON Buttons -->
134
+ <div class="mt-8">
135
+ <h3 class="text-xl font-semibold mb-4 text-center">Try with sample JSON</h3>
136
+ <div class="flex flex-wrap justify-center gap-3">
137
+ <button data-sample='{"name":"John","age":30,"city":"New York"}' class="sample-btn px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded transition-colors">
138
+ Simple Object
139
+ </button>
140
+ <button data-sample='[{"id":1,"name":"Alice","active":true},{"id":2,"name":"Bob","active":false},{"id":3,"name":"Charlie","active":true}]' class="sample-btn px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded transition-colors">
141
+ Array of Objects
142
+ </button>
143
+ <button data-sample='{"products":[{"id":"A1","price":19.99,"inStock":true},{"id":"B2","price":29.99,"inStock":false}],"totalItems":2}' class="sample-btn px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded transition-colors">
144
+ Nested Objects
145
+ </button>
146
+ <button data-sample='{"error":"Invalid JSON","line":3,"position":12}' class="sample-btn px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded transition-colors">
147
+ Invalid JSON
148
+ </button>
149
+ </div>
150
+ </div>
151
+
152
+ <!-- Features Section -->
153
+ <div class="mt-12 grid grid-cols-1 md:grid-cols-3 gap-6">
154
+ <div class="bg-gray-800 p-6 rounded-lg shadow-lg">
155
+ <div class="text-blue-400 text-2xl mb-3">
156
+ <i class="fas fa-check-double"></i>
157
+ </div>
158
+ <h3 class="text-xl font-semibold mb-2">Real-time Validation</h3>
159
+ <p class="text-gray-400">Instantly validate your JSON syntax and highlight errors with detailed messages.</p>
160
+ </div>
161
+ <div class="bg-gray-800 p-6 rounded-lg shadow-lg">
162
+ <div class="text-purple-400 text-2xl mb-3">
163
+ <i class="fas fa-paint-brush"></i>
164
+ </div>
165
+ <h3 class="text-xl font-semibold mb-2">Beautiful Formatting</h3>
166
+ <p class="text-gray-400">Format your JSON with syntax highlighting, proper indentation and line breaks.</p>
167
+ </div>
168
+ <div class="bg-gray-800 p-6 rounded-lg shadow-lg">
169
+ <div class="text-green-400 text-2xl mb-3">
170
+ <i class="fas fa-exchange-alt"></i>
171
+ </div>
172
+ <h3 class="text-xl font-semibold mb-2">Convert & Minify</h3>
173
+ <p class="text-gray-400">Easily convert between formatted and minified JSON with a single click.</p>
174
+ </div>
175
+ </div>
176
+ </div>
177
+
178
+ <script>
179
+ document.addEventListener('DOMContentLoaded', function() {
180
+ const jsonInput = document.getElementById('jsonInput');
181
+ const formattedJson = document.getElementById('formattedJson');
182
+ const validateBtn = document.getElementById('validateBtn');
183
+ const beautifyBtn = document.getElementById('beautifyBtn');
184
+ const minifyBtn = document.getElementById('minifyBtn');
185
+ const clearInput = document.getElementById('clearInput');
186
+ const copyBtn = document.getElementById('copyBtn');
187
+ const downloadBtn = document.getElementById('downloadBtn');
188
+ const inputError = document.getElementById('inputError');
189
+ const validationResult = document.getElementById('validationResult');
190
+ const validationIcon = document.getElementById('validationIcon');
191
+ const validationTitle = document.getElementById('validationTitle');
192
+ const validationDetails = document.getElementById('validationDetails');
193
+ const sampleBtns = document.querySelectorAll('.sample-btn');
194
+
195
+ // Initialize with empty state
196
+ clearInput.click();
197
+
198
+ // Sample JSON buttons
199
+ sampleBtns.forEach(btn => {
200
+ btn.addEventListener('click', function() {
201
+ const sample = this.getAttribute('data-sample');
202
+ jsonInput.value = sample;
203
+ formatAndValidate();
204
+ });
205
+ });
206
+
207
+ // Clear input
208
+ clearInput.addEventListener('click', function() {
209
+ jsonInput.value = '';
210
+ formattedJson.innerHTML = '';
211
+ inputError.classList.add('hidden');
212
+ validationResult.classList.add('hidden');
213
+ });
214
+
215
+ // Beautify JSON
216
+ beautifyBtn.addEventListener('click', function() {
217
+ formatAndValidate(true);
218
+ });
219
+
220
+ // Minify JSON
221
+ minifyBtn.addEventListener('click', function() {
222
+ try {
223
+ const jsonObj = JSON.parse(jsonInput.value);
224
+ jsonInput.value = JSON.stringify(jsonObj);
225
+ formatAndValidate();
226
+ } catch (e) {
227
+ showError(e.message);
228
+ }
229
+ });
230
+
231
+ // Validate JSON
232
+ validateBtn.addEventListener('click', function() {
233
+ formatAndValidate();
234
+ });
235
+
236
+ // Copy to clipboard
237
+ copyBtn.addEventListener('click', function() {
238
+ if (!formattedJson.textContent.trim()) return;
239
+
240
+ navigator.clipboard.writeText(formattedJson.textContent).then(() => {
241
+ copyBtn.classList.add('copied');
242
+ copyBtn.innerHTML = '<i class="fas fa-check"></i>';
243
+ setTimeout(() => {
244
+ copyBtn.classList.remove('copied');
245
+ copyBtn.innerHTML = '<i class="fas fa-copy"></i>';
246
+ }, 2000);
247
+ });
248
+ });
249
+
250
+ // Download JSON
251
+ downloadBtn.addEventListener('click', function() {
252
+ if (!formattedJson.textContent.trim()) return;
253
+
254
+ const blob = new Blob([formattedJson.textContent], { type: 'application/json' });
255
+ const url = URL.createObjectURL(blob);
256
+ const a = document.createElement('a');
257
+ a.href = url;
258
+ a.download = 'formatted.json';
259
+ document.body.appendChild(a);
260
+ a.click();
261
+ document.body.removeChild(a);
262
+ URL.revokeObjectURL(url);
263
+ });
264
+
265
+ // Auto-format when typing (with debounce)
266
+ let typingTimer;
267
+ jsonInput.addEventListener('input', function() {
268
+ clearTimeout(typingTimer);
269
+ typingTimer = setTimeout(() => {
270
+ formatAndValidate();
271
+ }, 800);
272
+ });
273
+
274
+ // Main formatting and validation function
275
+ function formatAndValidate(beautify = true) {
276
+ inputError.classList.add('hidden');
277
+ validationResult.classList.add('hidden');
278
+
279
+ try {
280
+ if (!jsonInput.value.trim()) {
281
+ formattedJson.innerHTML = '';
282
+ return;
283
+ }
284
+
285
+ const jsonObj = JSON.parse(jsonInput.value);
286
+ const formatted = beautify ? JSON.stringify(jsonObj, null, 2) : JSON.stringify(jsonObj);
287
+
288
+ // Update input if we're minifying
289
+ if (!beautify) {
290
+ jsonInput.value = formatted;
291
+ }
292
+
293
+ // Syntax highlight
294
+ formattedJson.innerHTML = syntaxHighlight(formatted);
295
+
296
+ // Show validation success
297
+ showValidationSuccess(jsonObj);
298
+
299
+ } catch (e) {
300
+ showError(e.message);
301
+ }
302
+ }
303
+
304
+ // Show error message
305
+ function showError(message) {
306
+ inputError.textContent = message;
307
+ inputError.classList.remove('hidden');
308
+
309
+ // Try to extract line number from error message
310
+ const lineMatch = message.match(/position (\d+)/);
311
+ if (lineMatch) {
312
+ const position = parseInt(lineMatch[1]);
313
+ highlightErrorLine(position);
314
+ }
315
+
316
+ // Show validation error
317
+ validationResult.classList.remove('hidden');
318
+ validationResult.className = 'mt-4 p-3 rounded bg-red-900/30 border border-red-700';
319
+ validationIcon.className = 'fas fa-times-circle text-2xl text-red-400';
320
+ validationTitle.textContent = 'Invalid JSON';
321
+ validationDetails.innerHTML = `<p class="text-red-300">${message}</p>`;
322
+ }
323
+
324
+ // Show validation success
325
+ function showValidationSuccess(jsonObj) {
326
+ validationResult.classList.remove('hidden');
327
+ validationResult.className = 'mt-4 p-3 rounded bg-green-900/30 border border-green-700';
328
+ validationIcon.className = 'fas fa-check-circle text-2xl text-green-400';
329
+ validationTitle.textContent = 'Valid JSON';
330
+
331
+ // Count properties
332
+ let propertyCount = 0;
333
+ if (Array.isArray(jsonObj)) {
334
+ propertyCount = jsonObj.length;
335
+ validationDetails.innerHTML = `
336
+ <p class="text-green-300">✓ Valid JSON array with ${propertyCount} items</p>
337
+ <p class="text-green-300 mt-1">✓ ${typeof jsonObj} (${getTypeDetails(jsonObj)})</p>
338
+ `;
339
+ } else if (jsonObj && typeof jsonObj === 'object') {
340
+ propertyCount = Object.keys(jsonObj).length;
341
+ validationDetails.innerHTML = `
342
+ <p class="text-green-300">✓ Valid JSON object with ${propertyCount} properties</p>
343
+ <p class="text-green-300 mt-1">✓ ${typeof jsonObj} (${getTypeDetails(jsonObj)})</p>
344
+ `;
345
+ } else {
346
+ validationDetails.innerHTML = `
347
+ <p class="text-green-300">✓ Valid JSON value</p>
348
+ <p class="text-green-300 mt-1">✓ ${typeof jsonObj} (${getTypeDetails(jsonObj)})</p>
349
+ `;
350
+ }
351
+ }
352
+
353
+ // Get type details for validation message
354
+ function getTypeDetails(value) {
355
+ if (value === null) return 'null';
356
+ if (Array.isArray(value)) return 'array';
357
+ if (typeof value === 'object') return 'object';
358
+ return typeof value;
359
+ }
360
+
361
+ // Highlight error line in input
362
+ function highlightErrorLine(position) {
363
+ // This is a simplified approach - a more complete solution would need a proper textarea line calculator
364
+ const lines = jsonInput.value.substr(0, position).split('\n');
365
+ const lineNumber = lines.length;
366
+
367
+ // Scroll to the line
368
+ const lineHeight = 20; // Approximate line height
369
+ jsonInput.scrollTop = (lineNumber - 3) * lineHeight;
370
+
371
+ // Highlight the line (this is a basic approach)
372
+ inputError.textContent += ` (near line ${lineNumber})`;
373
+ }
374
+
375
+ // Syntax highlighting for JSON
376
+ function syntaxHighlight(json) {
377
+ if (!json) return '';
378
+
379
+ json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
380
+
381
+ return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(match) {
382
+ let cls = 'json-value';
383
+ let style = '';
384
+
385
+ if (/^"/.test(match)) {
386
+ if (/:$/.test(match)) {
387
+ cls = 'json-key';
388
+ match = match.replace(/:$/, '');
389
+ style = ' style="color: #9CDCFE;"';
390
+ } else {
391
+ cls = 'json-string';
392
+ }
393
+ } else if (/true|false/.test(match)) {
394
+ cls = 'json-boolean';
395
+ } else if (/null/.test(match)) {
396
+ cls = 'json-null';
397
+ } else if (/^-?\d+\.?\d*([eE][+\-]?\d+)?$/.test(match)) {
398
+ cls = 'json-number';
399
+ }
400
+
401
+ return '<span class="' + cls + '"' + style + '>' + match + '</span>';
402
+ })
403
+ .replace(/([{}[\],:])/g, function(match) {
404
+ return '<span class="json-punctuation">' + match + '</span>';
405
+ });
406
+ }
407
+ });
408
+ </script>
409
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=MarkTheArtist/json-validator" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
410
+ </html>