RobinsAIWorld commited on
Commit
ec39fd9
·
verified ·
1 Parent(s): b9fe4f1

🐳 10/02 - 13:55 - The buttons and menu bar items still donlt wokr

Browse files
Files changed (1) hide show
  1. script.js +775 -0
script.js ADDED
@@ -0,0 +1,775 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ // Sample JSON data
3
+ const sampleJSON = {
4
+ "name": "John Doe",
5
+ "age": 30,
6
+ "isStudent": false,
7
+ "address": {
8
+ "street": "123 Main St",
9
+ "city": "Anytown",
10
+ "zipcode": "12345"
11
+ },
12
+ "hobbies": [
13
+ "reading",
14
+ "swimming",
15
+ "coding"
16
+ ],
17
+ "contact": {
18
+ "email": "john@example.com",
19
+ "phone": "555-1234"
20
+ }
21
+ };
22
+
23
+ // DOM elements
24
+ const jsonEditor = document.getElementById('jsonEditor');
25
+ const jsonOutput = document.getElementById('jsonOutput');
26
+ const notification = document.getElementById('notification');
27
+ const copyBtn = document.getElementById('copyBtn');
28
+ const downloadBtn = document.getElementById('downloadBtn');
29
+ const applyCodeBtn = document.getElementById('applyCodeBtn');
30
+ const validationStatus = document.getElementById('validationStatus');
31
+
32
+ // Create saved indicator
33
+ const savedIndicator = document.createElement('div');
34
+ savedIndicator.className = 'saved-indicator';
35
+ savedIndicator.innerHTML = '<i class="fas fa-check-circle mr-2"></i>Saved';
36
+ document.body.appendChild(savedIndicator);
37
+
38
+ let isApplyingChanges = false;
39
+
40
+ // Current state
41
+ let jsonData = {};
42
+ let history = [];
43
+ let historyIndex = -1;
44
+ const MAX_HISTORY = 50;
45
+
46
+ // Track all editable fields for TAB navigation
47
+ let editableFields = [];
48
+ let currentFieldIndex = -1;
49
+
50
+ // Layer colors for visual distinction
51
+ const layerColors = [
52
+ '#ef4444', '#f97316', '#eab308', '#22c55e',
53
+ '#06b6d4', '#3b82f6', '#8b5cf6', '#ec4899'
54
+ ];
55
+
56
+ // Initialize with sample data
57
+ loadJSON(sampleJSON);
58
+
59
+ // Create hidden file input for opening files
60
+ const fileInput = document.createElement('input');
61
+ fileInput.type = 'file';
62
+ fileInput.accept = 'application/json,.json';
63
+ fileInput.style.display = 'none';
64
+ document.body.appendChild(fileInput);
65
+
66
+ // ==================== NEW JSON FUNCTION ====================
67
+ function newJSON() {
68
+ const newEmpty = {
69
+ "newField": "value"
70
+ };
71
+ loadJSON(newEmpty);
72
+ showNotification('New JSON created');
73
+ }
74
+
75
+ document.getElementById('newBtn').addEventListener('click', newJSON);
76
+ document.getElementById('newBtn2').addEventListener('click', newJSON);
77
+
78
+ // ==================== OPEN FILE FUNCTION ====================
79
+ function openFile() {
80
+ fileInput.click();
81
+ }
82
+
83
+ fileInput.addEventListener('change', (event) => {
84
+ const file = event.target.files[0];
85
+ if (file) {
86
+ const reader = new FileReader();
87
+ reader.onload = (e) => {
88
+ try {
89
+ const data = JSON.parse(e.target.result);
90
+ loadJSON(data);
91
+ showNotification('File loaded successfully!');
92
+ } catch (error) {
93
+ showNotification('Error loading file: Invalid JSON');
94
+ }
95
+ };
96
+ reader.readAsText(file);
97
+ }
98
+ });
99
+
100
+ document.getElementById('openBtn').addEventListener('click', openFile);
101
+ document.getElementById('openBtn2').addEventListener('click', openFile);
102
+
103
+ // ==================== SAVE FILE FUNCTION ====================
104
+ function saveFile() {
105
+ const blob = new Blob([jsonOutput.value], { type: 'application/json' });
106
+ const url = URL.createObjectURL(blob);
107
+ const a = document.createElement('a');
108
+ a.href = url;
109
+ a.download = 'data.json';
110
+ document.body.appendChild(a);
111
+ a.click();
112
+ document.body.removeChild(a);
113
+ URL.revokeObjectURL(url);
114
+ showNotification('JSON file saved!');
115
+ }
116
+
117
+ document.getElementById('saveBtn').addEventListener('click', saveFile);
118
+ document.getElementById('saveBtn2').addEventListener('click', saveFile);
119
+
120
+ // ==================== COPY FUNCTION ====================
121
+ copyBtn.addEventListener('click', () => {
122
+ jsonOutput.select();
123
+ document.execCommand('copy');
124
+ showNotification('JSON copied to clipboard!');
125
+ });
126
+
127
+ document.getElementById('copyBtnMenu').addEventListener('click', () => {
128
+ jsonOutput.select();
129
+ document.execCommand('copy');
130
+ showNotification('JSON copied to clipboard!');
131
+ });
132
+
133
+ // ==================== DOWNLOAD FUNCTION ====================
134
+ downloadBtn.addEventListener('click', () => {
135
+ saveFile();
136
+ });
137
+
138
+ // ==================== UNDO/REDO FUNCTIONS ====================
139
+ function undo() {
140
+ if (historyIndex > 0) {
141
+ historyIndex--;
142
+ jsonData = JSON.parse(JSON.stringify(history[historyIndex]));
143
+ renderEditor();
144
+ updateOutput();
145
+ showNotification('Undo successful');
146
+ }
147
+ }
148
+
149
+ function redo() {
150
+ if (historyIndex < history.length - 1) {
151
+ historyIndex++;
152
+ jsonData = JSON.parse(JSON.stringify(history[historyIndex]));
153
+ renderEditor();
154
+ updateOutput();
155
+ showNotification('Redo successful');
156
+ }
157
+ }
158
+
159
+ // Menu undo/redo
160
+ document.getElementById('undoBtn').addEventListener('click', undo);
161
+ document.getElementById('redoBtn').addEventListener('click', redo);
162
+
163
+ // Toolbar undo/redo
164
+ document.getElementById('undoBtn2').addEventListener('click', undo);
165
+ document.getElementById('redoBtn2').addEventListener('click', redo);
166
+
167
+ // ==================== FORMAT FUNCTION ====================
168
+ function formatJSON() {
169
+ updateOutput();
170
+ showNotification('JSON formatted');
171
+ }
172
+
173
+ document.getElementById('formatBtn').addEventListener('click', formatJSON);
174
+ document.getElementById('formatBtn2').addEventListener('click', formatJSON);
175
+
176
+ // ==================== VALIDATE FUNCTION ====================
177
+ function validateJSON() {
178
+ try {
179
+ JSON.parse(jsonOutput.value);
180
+ showNotification('JSON is valid!');
181
+ } catch (e) {
182
+ showNotification('Invalid JSON: ' + e.message);
183
+ }
184
+ }
185
+
186
+ document.getElementById('validateBtn').addEventListener('click', validateJSON);
187
+ document.getElementById('validateBtn2').addEventListener('click', validateJSON);
188
+
189
+ // ==================== SAMPLE JSON FUNCTION ====================
190
+ document.getElementById('sampleBtn').addEventListener('click', () => {
191
+ loadJSON(sampleJSON);
192
+ showNotification('Sample JSON loaded');
193
+ });
194
+
195
+ // ==================== INSTRUCTIONS FUNCTION ====================
196
+ document.getElementById('instructionsBtn').addEventListener('click', () => {
197
+ showNotification('Click on any key or value to edit. Use Tab to navigate, Shift+Enter to add new fields.');
198
+ });
199
+
200
+ // ==================== CUT/PASTE/DEFAULT FUNCTIONS ====================
201
+ document.getElementById('cutBtnMenu').addEventListener('click', () => {
202
+ jsonOutput.select();
203
+ document.execCommand('copy');
204
+ document.execCommand('delete');
205
+ showNotification('Cut to clipboard');
206
+ });
207
+
208
+ document.getElementById('pasteBtnMenu').addEventListener('click', () => {
209
+ navigator.clipboard.readText().then(text => {
210
+ try {
211
+ const data = JSON.parse(text);
212
+ loadJSON(data);
213
+ showNotification('JSON pasted and loaded');
214
+ } catch (e) {
215
+ jsonOutput.value += text;
216
+ showNotification('Text pasted');
217
+ }
218
+ }).catch(() => {
219
+ showNotification('Unable to access clipboard');
220
+ });
221
+ });
222
+
223
+ document.getElementById('preferencesBtn').addEventListener('click', () => {
224
+ showNotification('Preferences coming soon!');
225
+ });
226
+
227
+ // ==================== CORE EDITOR FUNCTIONS ====================
228
+
229
+ // Load JSON data into the editor
230
+ function loadJSON(data) {
231
+ jsonData = JSON.parse(JSON.stringify(data));
232
+ renderEditor();
233
+ updateOutput();
234
+ saveToHistory();
235
+ }
236
+
237
+ // Render the JSON editor
238
+ function renderEditor() {
239
+ jsonEditor.innerHTML = '';
240
+ editableFields = [];
241
+ currentFieldIndex = -1;
242
+ renderElement(jsonEditor, jsonData, 0, 'root');
243
+ }
244
+
245
+ // Render a single JSON element (COMPLETE VERSION)
246
+ function renderElement(container, data, depth, key, parentKey = 'root') {
247
+ const layerClass = `lcars-layer-${Math.min(depth, 7)}`;
248
+ const layerColor = layerColors[Math.min(depth, 7)];
249
+
250
+ if (typeof data === 'object' && data !== null) {
251
+ // Object or Array - render opening bracket and children
252
+ const wrapper = document.createElement('div');
253
+ wrapper.className = `json-item relative ${layerClass}`;
254
+ wrapper.dataset.key = key;
255
+ wrapper.dataset.parent = parentKey;
256
+ wrapper.dataset.depth = depth;
257
+ wrapper.dataset.layer = Math.min(depth, 7);
258
+
259
+ // Layer indicator line (LCARS style)
260
+ const layerIndicator = document.createElement('div');
261
+ layerIndicator.className = 'layer-indicator';
262
+ layerIndicator.style.left = `${depth * 24}px`;
263
+ layerIndicator.style.background = layerColor;
264
+ layerIndicator.style.boxShadow = `2px 0 8px ${layerColor}`;
265
+ wrapper.appendChild(layerIndicator);
266
+
267
+ const content = document.createElement('div');
268
+ content.className = 'json-item-content flex items-start py-1';
269
+ content.style.marginLeft = `${depth * 24 + 12}px`;
270
+
271
+ // Key (for objects, not arrays)
272
+ if (!Array.isArray(data) && key !== 'root') {
273
+ const keySpan = document.createElement('span');
274
+ keySpan.className = 'json-key';
275
+ keySpan.textContent = `"${key}"`;
276
+ keySpan.style.color = '#93c5fd';
277
+ content.appendChild(keySpan);
278
+
279
+ const colon = document.createElement('span');
280
+ colon.textContent = ': ';
281
+ colon.className = 'text-slate-500';
282
+ content.appendChild(colon);
283
+
284
+ // Make key editable
285
+ keySpan.style.cursor = 'pointer';
286
+ keySpan.addEventListener('click', (e) => {
287
+ e.stopPropagation();
288
+ makeEditable(keySpan, 'key', key, parentKey);
289
+ });
290
+ }
291
+
292
+ // Value or children
293
+ if (Array.isArray(data)) {
294
+ // Array
295
+ const bracket = document.createElement('span');
296
+ bracket.className = `json-bracket bracket-layer-${Math.min(depth, 7)}`;
297
+ bracket.textContent = '[';
298
+ content.appendChild(bracket);
299
+
300
+ wrapper.appendChild(content);
301
+ container.appendChild(wrapper);
302
+
303
+ // Render array items
304
+ data.forEach((item, index) => {
305
+ renderElement(container, item, depth + 1, index, key);
306
+ });
307
+
308
+ // Closing bracket
309
+ const closingWrapper = document.createElement('div');
310
+ closingWrapper.className = `json-item relative ${layerClass}`;
311
+ closingWrapper.dataset.key = 'closing';
312
+ closingWrapper.dataset.parent = key;
313
+ closingWrapper.dataset.depth = depth;
314
+ closingWrapper.dataset.layer = Math.min(depth, 7);
315
+
316
+ const closingLayerIndicator = document.createElement('div');
317
+ closingLayerIndicator.className = 'layer-indicator';
318
+ closingLayerIndicator.style.left = `${depth * 24}px`;
319
+ closingWrapper.appendChild(closingLayerIndicator);
320
+
321
+ const closingContent = document.createElement('div');
322
+ closingContent.className = 'json-item-content flex items-start py-1';
323
+ closingContent.style.marginLeft = `${depth * 24 + 12}px`;
324
+ const closingBracket = document.createElement('span');
325
+ closingBracket.className = `json-bracket bracket-layer-${Math.min(depth, 7)}`;
326
+ closingBracket.textContent = ']';
327
+ closingContent.appendChild(closingBracket);
328
+ closingWrapper.appendChild(closingContent);
329
+ container.appendChild(closingWrapper);
330
+ } else {
331
+ // Object
332
+ const bracket = document.createElement('span');
333
+ bracket.className = `json-bracket bracket-layer-${Math.min(depth, 7)}`;
334
+ bracket.textContent = '{';
335
+ content.appendChild(bracket);
336
+
337
+ wrapper.appendChild(content);
338
+ container.appendChild(wrapper);
339
+
340
+ // Render object properties
341
+ Object.keys(data).forEach(propKey => {
342
+ renderElement(container, data[propKey], depth + 1, propKey, key);
343
+ });
344
+
345
+ // Closing bracket
346
+ const closingWrapper = document.createElement('div');
347
+ closingWrapper.className = `json-item relative ${layerClass}`;
348
+ closingWrapper.dataset.key = 'closing';
349
+ closingWrapper.dataset.parent = key;
350
+ closingWrapper.dataset.depth = depth;
351
+ closingWrapper.dataset.layer = Math.min(depth, 7);
352
+
353
+ const closingLayerIndicator = document.createElement('div');
354
+ closingLayerIndicator.className = 'layer-indicator';
355
+ closingLayerIndicator.style.left = `${depth * 24}px`;
356
+ closingWrapper.appendChild(closingLayerIndicator);
357
+
358
+ const closingContent = document.createElement('div');
359
+ closingContent.className = 'json-item-content flex items-start py-1';
360
+ closingContent.style.marginLeft = `${depth * 24 + 12}px`;
361
+ const closingBracket = document.createElement('span');
362
+ closingBracket.className = `json-bracket bracket-layer-${Math.min(depth, 7)}`;
363
+ closingBracket.textContent = '}';
364
+ closingContent.appendChild(closingBracket);
365
+ closingWrapper.appendChild(closingContent);
366
+ container.appendChild(closingWrapper);
367
+ }
368
+ } else {
369
+ // Primitive value (string, number, boolean, null)
370
+ const wrapper = document.createElement('div');
371
+ wrapper.className = `json-item relative ${layerClass}`;
372
+ wrapper.dataset.key = key;
373
+ wrapper.dataset.parent = parentKey;
374
+ wrapper.dataset.depth = depth;
375
+ wrapper.dataset.layer = Math.min(depth, 7);
376
+
377
+ // Layer indicator line
378
+ const layerIndicator = document.createElement('div');
379
+ layerIndicator.className = 'layer-indicator';
380
+ layerIndicator.style.left = `${depth * 24}px`;
381
+ layerIndicator.style.background = layerColor;
382
+ layerIndicator.style.boxShadow = `2px 0 8px ${layerColor}`;
383
+ wrapper.appendChild(layerIndicator);
384
+
385
+ const content = document.createElement('div');
386
+ content.className = 'json-item-content flex items-start py-1';
387
+ content.style.marginLeft = `${depth * 24 + 12}px`;
388
+
389
+ // Key
390
+ if (key !== 'root') {
391
+ const keySpan = document.createElement('span');
392
+ keySpan.className = 'json-key';
393
+ keySpan.textContent = `"${key}"`;
394
+ keySpan.style.color = '#93c5fd';
395
+ content.appendChild(keySpan);
396
+
397
+ const colon = document.createElement('span');
398
+ colon.textContent = ': ';
399
+ colon.className = 'text-slate-500';
400
+ content.appendChild(colon);
401
+
402
+ // Make key editable
403
+ keySpan.style.cursor = 'pointer';
404
+ keySpan.addEventListener('click', (e) => {
405
+ e.stopPropagation();
406
+ makeEditable(keySpan, 'key', key, parentKey);
407
+ });
408
+ }
409
+
410
+ // Value
411
+ const valueSpan = document.createElement('span');
412
+ valueSpan.className = 'json-value';
413
+ if (typeof data === 'string') {
414
+ valueSpan.textContent = `"${data}"`;
415
+ valueSpan.style.color = '#6ee7b7';
416
+ } else if (typeof data === 'number') {
417
+ valueSpan.textContent = data;
418
+ valueSpan.style.color = '#f472b6';
419
+ } else if (typeof data === 'boolean') {
420
+ valueSpan.textContent = data;
421
+ valueSpan.style.color = '#fbbf24';
422
+ } else if (data === null) {
423
+ valueSpan.textContent = 'null';
424
+ valueSpan.style.color = '#94a3b8';
425
+ }
426
+ content.appendChild(valueSpan);
427
+
428
+ // Make value editable
429
+ valueSpan.style.cursor = 'pointer';
430
+ valueSpan.addEventListener('click', (e) => {
431
+ e.stopPropagation();
432
+ makeEditable(valueSpan, 'value', key, parentKey);
433
+ });
434
+
435
+ wrapper.appendChild(content);
436
+ container.appendChild(wrapper);
437
+ }
438
+ }
439
+
440
+ // Make an element editable
441
+ function makeEditable(span, type, key, parentKey) {
442
+ const currentValue = type === 'key' ? key : getValue(parentKey, key);
443
+ let displayValue = type === 'key' ? currentValue : JSON.stringify(currentValue);
444
+
445
+ // Remove quotes from display for editing
446
+ if (displayValue.startsWith('"') && displayValue.endsWith('"')) {
447
+ displayValue = displayValue.slice(1, -1);
448
+ }
449
+
450
+ const input = document.createElement('input');
451
+ input.type = 'text';
452
+ input.value = displayValue;
453
+ input.className = `editable-field ${type}-input`;
454
+
455
+ // Replace span with input
456
+ span.parentNode.replaceChild(input, span);
457
+ input.focus();
458
+ input.select();
459
+
460
+ // Save on blur or Enter
461
+ const saveEdit = () => {
462
+ const newValue = input.value.trim();
463
+ input.parentNode.replaceChild(span, span);
464
+
465
+ if (type === 'key') {
466
+ // Rename the key
467
+ if (newValue !== key) {
468
+ renameKey(parentKey, key, newValue);
469
+ }
470
+ } else {
471
+ // Update the value
472
+ const parsedValue = parseValue(newValue);
473
+ if (JSON.stringify(parsedValue) !== JSON.stringify(currentValue)) {
474
+ updateValue(parentKey, key, parsedValue);
475
+ }
476
+ }
477
+ };
478
+
479
+ input.addEventListener('blur', saveEdit);
480
+ input.addEventListener('keydown', (e) => {
481
+ if (e.key === 'Enter') {
482
+ e.preventDefault();
483
+ input.blur();
484
+ } else if (e.key === 'Tab') {
485
+ e.preventDefault();
486
+ const direction = e.shiftKey ? -1 : 1;
487
+ navigateFields(direction);
488
+ } else if (e.shiftKey && e.key === 'Enter') {
489
+ e.preventDefault();
490
+ saveEdit();
491
+ insertNewField({ key, parentKey });
492
+ }
493
+ });
494
+ }
495
+
496
+ // Navigate to next/previous editable field
497
+ function navigateFields(direction) {
498
+ // Find all editable spans
499
+ const keys = jsonEditor.querySelectorAll('.json-key');
500
+ const values = jsonEditor.querySelectorAll('.json-value');
501
+ const allFields = [];
502
+
503
+ keys.forEach(k => allFields.push({ element: k, type: 'key' }));
504
+ values.forEach(v => allFields.push({ element: v, type: 'value' }));
505
+
506
+ let currentIndex = -1;
507
+ for (let i = 0; i < allFields.length; i++) {
508
+ if (document.activeElement === allFields[i].element ||
509
+ document.activeElement.parentNode === allFields[i].element.parentNode) {
510
+ currentIndex = i;
511
+ break;
512
+ }
513
+ }
514
+
515
+ let nextIndex = currentIndex + direction;
516
+ if (nextIndex < 0) nextIndex = allFields.length - 1;
517
+ if (nextIndex >= allFields.length) nextIndex = 0;
518
+
519
+ if (allFields[nextIndex]) {
520
+ allFields[nextIndex].element.click();
521
+ }
522
+ }
523
+
524
+ // Get nested value
525
+ function getValue(parentKey, key) {
526
+ if (parentKey === 'root') {
527
+ return jsonData[key];
528
+ }
529
+ const parent = findElementByKey(jsonData, parentKey);
530
+ if (parent && typeof parent === 'object') {
531
+ return parent[key];
532
+ }
533
+ return undefined;
534
+ }
535
+
536
+ // Update a value
537
+ function updateValue(parentKey, key, newValue) {
538
+ if (parentKey === 'root') {
539
+ jsonData[key] = newValue;
540
+ } else {
541
+ const parent = findElementByKey(jsonData, parentKey);
542
+ if (parent && typeof parent === 'object') {
543
+ parent[key] = newValue;
544
+ }
545
+ }
546
+ renderEditor();
547
+ updateOutput();
548
+ saveToHistory();
549
+ showSavedIndicator();
550
+ }
551
+
552
+ // Rename a key
553
+ function renameKey(parentKey, oldKey, newKey) {
554
+ if (parentKey === 'root') {
555
+ jsonData[newKey] = jsonData[oldKey];
556
+ delete jsonData[oldKey];
557
+ } else {
558
+ const parent = findElementByKey(jsonData, parentKey);
559
+ if (parent && typeof parent === 'object' && !Array.isArray(parent)) {
560
+ parent[newKey] = parent[oldKey];
561
+ delete parent[oldKey];
562
+ }
563
+ }
564
+ renderEditor();
565
+ updateOutput();
566
+ saveToHistory();
567
+ showSavedIndicator();
568
+ }
569
+
570
+ // Insert a new field after the current one
571
+ function insertNewField(currentFieldData) {
572
+ const { key, parentKey } = currentFieldData;
573
+
574
+ if (parentKey === 'root') {
575
+ const keys = Object.keys(jsonData);
576
+ const index = keys.indexOf(key);
577
+ const newKey = 'newField';
578
+
579
+ const newData = {};
580
+ keys.forEach((k, i) => {
581
+ newData[k] = jsonData[k];
582
+ if (i === index) {
583
+ newData[newKey] = '';
584
+ }
585
+ });
586
+ if (index === -1 || index === keys.length - 1) {
587
+ newData[newKey] = '';
588
+ }
589
+
590
+ jsonData = newData;
591
+ } else {
592
+ const parent = findElementByKey(jsonData, parentKey);
593
+ if (parent && typeof parent === 'object' && !Array.isArray(parent)) {
594
+ const keys = Object.keys(parent);
595
+ const index = keys.indexOf(key);
596
+ const newKey = 'newField';
597
+
598
+ const newData = {};
599
+ keys.forEach((k, i) => {
600
+ newData[k] = parent[k];
601
+ if (i === index) {
602
+ newData[newKey] = '';
603
+ }
604
+ });
605
+ if (index === -1 || index === keys.length - 1) {
606
+ newData[newKey] = '';
607
+ }
608
+
609
+ Object.keys(parent).forEach(k => delete parent[k]);
610
+ Object.assign(parent, newData);
611
+ }
612
+ }
613
+
614
+ renderEditor();
615
+ updateOutput();
616
+ saveToHistory();
617
+ showSavedIndicator();
618
+ }
619
+
620
+ // Parse a value from string to appropriate type
621
+ function parseValue(value) {
622
+ if (value === 'true') return true;
623
+ if (value === 'false') return false;
624
+ if (value === 'null') return null;
625
+ if (!isNaN(value) && value.trim() !== '' && !isNaN(Number(value))) {
626
+ return Number(value);
627
+ }
628
+ return value;
629
+ }
630
+
631
+ // Show saved indicator
632
+ function showSavedIndicator() {
633
+ savedIndicator.classList.add('show');
634
+ setTimeout(() => {
635
+ savedIndicator.classList.remove('show');
636
+ }, 1500);
637
+ }
638
+
639
+ // Find an element by key in nested structure
640
+ function findElementByKey(obj, key) {
641
+ if (obj[key] !== undefined) return obj;
642
+
643
+ for (let prop in obj) {
644
+ if (typeof obj[prop] === 'object' && obj[prop] !== null) {
645
+ const result = findElementByKey(obj[prop], key);
646
+ if (result) return result;
647
+ }
648
+ }
649
+
650
+ return null;
651
+ }
652
+
653
+ // Update JSON output
654
+ function updateOutput() {
655
+ if (isApplyingChanges) return;
656
+ try {
657
+ const jsonString = JSON.stringify(jsonData, null, 2);
658
+ jsonOutput.value = jsonString;
659
+ jsonOutput.style.borderColor = '#10b981';
660
+ validationStatus.innerHTML = '<i class="fas fa-check-circle text-green-500"></i><span class="text-green-600">Valid JSON</span>';
661
+ } catch (e) {
662
+ jsonOutput.style.borderColor = '#ef4444';
663
+ validationStatus.innerHTML = '<i class="fas fa-exclamation-circle text-red-500"></i><span class="text-red-600">Invalid JSON</span>';
664
+ }
665
+ }
666
+
667
+ // Apply changes from code editor to visual editor
668
+ function applyCodeChanges() {
669
+ if (!jsonOutput.value.trim()) return false;
670
+ try {
671
+ const newData = JSON.parse(jsonOutput.value);
672
+ isApplyingChanges = true;
673
+ jsonData = JSON.parse(JSON.stringify(newData));
674
+ renderEditor();
675
+ updateOutput();
676
+ saveToHistory();
677
+ isApplyingChanges = false;
678
+ showNotification('Changes applied successfully!');
679
+ return true;
680
+ } catch (e) {
681
+ showNotification('Invalid JSON: ' + e.message);
682
+ jsonOutput.style.borderColor = '#ef4444';
683
+ validationStatus.innerHTML = '<i class="fas fa-exclamation-circle text-red-500"></i><span class="text-red-600">Invalid JSON</span>';
684
+ return false;
685
+ }
686
+ }
687
+
688
+ // Real-time validation of code editor
689
+ jsonOutput.addEventListener('input', () => {
690
+ try {
691
+ JSON.parse(jsonOutput.value);
692
+ jsonOutput.style.borderColor = '#10b981';
693
+ validationStatus.innerHTML = '<i class="fas fa-check-circle text-green-500"></i><span class="text-green-600">Valid JSON</span>';
694
+ } catch (e) {
695
+ jsonOutput.style.borderColor = '#ef4444';
696
+ validationStatus.innerHTML = '<i class="fas fa-exclamation-circle text-red-500"></i><span class="text-red-600">Invalid JSON</span>';
697
+ }
698
+ });
699
+
700
+ // Apply button click
701
+ applyCodeBtn.addEventListener('click', applyCodeChanges);
702
+
703
+ // Auto-apply on Ctrl+Enter or Cmd+Enter
704
+ jsonOutput.addEventListener('keydown', (e) => {
705
+ if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
706
+ e.preventDefault();
707
+ applyCodeChanges();
708
+ }
709
+ });
710
+
711
+ // Auto-apply on blur (when clicking away)
712
+ jsonOutput.addEventListener('blur', () => {
713
+ const autoSaveStatus = document.getElementById('autoSaveStatus');
714
+ if (applyCodeChanges()) {
715
+ autoSaveStatus.classList.remove('hidden');
716
+ setTimeout(() => {
717
+ autoSaveStatus.classList.add('hidden');
718
+ }, 2000);
719
+ }
720
+ }, true);
721
+
722
+ // Paste in JSON output pane
723
+ jsonOutput.addEventListener('paste', (e) => {
724
+ e.preventDefault();
725
+ const pasteHandler = (text) => {
726
+ try {
727
+ const data = JSON.parse(text);
728
+ loadJSON(data);
729
+ showNotification('JSON pasted and loaded successfully');
730
+ } catch (parseError) {
731
+ const start = jsonOutput.selectionStart;
732
+ const end = jsonOutput.selectionEnd;
733
+ const currentValue = jsonOutput.value;
734
+ jsonOutput.value = currentValue.substring(0, start) + text + currentValue.substring(end);
735
+ }
736
+ };
737
+
738
+ navigator.clipboard.readText().then(text => {
739
+ pasteHandler(text);
740
+ }).catch(err => {
741
+ const text = (e.originalEvent || e).clipboardData.getData('text/plain');
742
+ pasteHandler(text);
743
+ });
744
+ });
745
+
746
+ // Show notification
747
+ function showNotification(message) {
748
+ const notificationContent = notification.querySelector('p');
749
+ notificationContent.textContent = message;
750
+ notification.classList.add('show');
751
+
752
+ setTimeout(() => {
753
+ notification.classList.remove('show');
754
+ }, 3000);
755
+ }
756
+
757
+ // Save to history for undo/redo
758
+ function saveToHistory() {
759
+ if (historyIndex < history.length - 1) {
760
+ history = history.slice(0, historyIndex + 1);
761
+ }
762
+
763
+ if (history.length >= MAX_HISTORY) {
764
+ history.shift();
765
+ historyIndex--;
766
+ }
767
+
768
+ if (history.length > 0 && JSON.stringify(history[historyIndex]) === JSON.stringify(jsonData)) {
769
+ return;
770
+ }
771
+
772
+ history.push(JSON.parse(JSON.stringify(jsonData)));
773
+ historyIndex = history.length - 1;
774
+ }
775
+ });