lots of progress

This commit is contained in:
2025-04-20 17:54:08 +03:00
parent de8775d0c3
commit bb3c9c82e0
12 changed files with 2293 additions and 29 deletions
+577
View File
@@ -0,0 +1,577 @@
@extends('layouts.app')
@section('title', 'JSON Lint - Free Online JSON Validator and Formatter')
@push('styles')
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/jsoneditor@9.10.2/dist/jsoneditor.min.css" rel="stylesheet">
<style>
:root {
--primary-color: #4a6bff;
--primary-hover: #3a5bef;
}
body {
background-color: var(--bs-body-bg);
transition: background-color 0.3s ease;
}
.json-input {
min-height: 300px;
font-family: 'Courier New', monospace;
background-color: var(--bs-body-bg);
border: 1px solid var(--bs-border-color);
transition: all 0.3s ease;
}
.json-input:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(74, 107, 255, 0.25);
}
.theme-switch {
cursor: pointer;
padding: 0.5rem;
border-radius: 50%;
transition: all 0.3s ease;
background-color: var(--bs-body-bg);
border: 1px solid var(--bs-border-color);
}
.theme-switch:hover {
background-color: var(--bs-border-color);
}
.result-box {
display: none;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.result-box.show {
display: block;
}
.tree-view-container {
height: 500px;
border: 1px solid var(--bs-border-color);
border-radius: 0.375rem;
margin-top: 1rem;
background-color: var(--bs-body-bg);
transition: all 0.3s ease;
overflow: auto;
}
/* Tree view animations */
.jsoneditor-tree {
transition: all 0.3s ease;
}
.jsoneditor-tree .jsoneditor-expandable {
transition: transform 0.3s ease;
}
.jsoneditor-tree .jsoneditor-expandable:hover {
transform: scale(1.02);
}
.jsoneditor-tree .jsoneditor-field {
transition: color 0.3s ease;
}
.jsoneditor-tree .jsoneditor-field:hover {
color: var(--primary-color);
}
.jsoneditor-tree .jsoneditor-value {
transition: color 0.3s ease;
}
.jsoneditor-tree .jsoneditor-value:hover {
color: var(--primary-color);
}
.jsoneditor-tree .jsoneditor-separator {
transition: opacity 0.3s ease;
}
.jsoneditor-tree .jsoneditor-separator:hover {
opacity: 0.8;
}
/* Collapsible node styles */
.jsoneditor-tree .jsoneditor-navigation-bar {
padding: 8px;
}
.jsoneditor-tree .jsoneditor-navigation-bar button {
transition: all 0.3s ease;
}
.jsoneditor-tree .jsoneditor-navigation-bar button:hover {
background-color: var(--bs-border-color);
}
/* Smooth collapse/expand animation */
.jsoneditor-tree .jsoneditor-expandable {
transition: all 0.3s ease;
}
.jsoneditor-tree .jsoneditor-expandable.collapsed {
opacity: 0.8;
}
.jsoneditor-tree .jsoneditor-expandable.expanded {
opacity: 1;
}
.nav-tabs {
margin-bottom: 1rem;
border-bottom: 1px solid var(--bs-border-color);
}
.nav-tabs .nav-link {
color: var(--bs-body-color);
border: none;
padding: 0.5rem 1rem;
transition: all 0.3s ease;
}
.nav-tabs .nav-link.active {
color: var(--primary-color);
border-bottom: 2px solid var(--primary-color);
background-color: transparent;
}
.nav-tabs .nav-link:hover {
color: var(--primary-color);
border-color: transparent;
}
.jsoneditor {
border: none !important;
}
.jsoneditor-menu {
background-color: var(--bs-body-bg) !important;
border-bottom: 1px solid var(--bs-border-color) !important;
}
.jsoneditor-navigation-bar {
background-color: var(--bs-body-bg) !important;
border-bottom: 1px solid var(--bs-border-color) !important;
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-primary:hover {
background-color: var(--primary-hover);
border-color: var(--primary-hover);
}
.card {
border: none;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.card:hover {
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.hero-section {
background: linear-gradient(135deg, var(--primary-color) 0%, #6a8eff 100%);
color: white;
padding: 3rem 0;
margin-bottom: 2rem;
border-radius: 0.5rem;
}
.feature-icon {
font-size: 2rem;
color: var(--primary-color);
margin-bottom: 1rem;
}
.feature-card {
text-align: center;
padding: 1.5rem;
border-radius: 0.5rem;
background-color: var(--bs-body-bg);
border: 1px solid var(--bs-border-color);
transition: all 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
}
</style>
@endpush
@section('content')
<div class="hero-section text-center">
<h1 class="display-4 fw-bold mb-3">JSON Lint Tool</h1>
<p class="lead mb-4">Validate, format, and beautify your JSON data with our powerful online tool</p>
</div>
<div class="row mb-4">
<div class="col-md-4 mb-3">
<div class="feature-card">
<i class="bi bi-check-circle feature-icon"></i>
<h3>Validate JSON</h3>
<p>Quickly check if your JSON data is valid and well-formed</p>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="feature-card">
<i class="bi bi-code-slash feature-icon"></i>
<h3>Format JSON</h3>
<p>Automatically format and beautify your JSON for better readability</p>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="feature-card">
<i class="bi bi-diagram-3 feature-icon"></i>
<h3>Tree View</h3>
<p>Visualize your JSON structure with an interactive tree view</p>
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2 class="h4 mb-0">JSON Editor</h2>
</div>
<div class="mb-3">
<label for="jsonInput" class="form-label">Enter your JSON:</label>
<textarea class="form-control json-input" id="jsonInput" rows="10" placeholder='{
"example": "json",
"format": "here"
}'></textarea>
</div>
<div class="d-flex gap-2">
<button class="btn btn-primary" id="validateBtn">
<i class="bi bi-check-circle me-2"></i>Validate JSON
</button>
<button class="btn btn-outline-secondary" id="formatBtn">
<i class="bi bi-code-slash me-2"></i>Format JSON
</button>
<button class="btn btn-outline-secondary" id="shareBtn">
<i class="bi bi-share me-2"></i>Share
</button>
<button class="btn btn-outline-secondary" id="clearBtn">
<i class="bi bi-trash me-2"></i>Clear
</button>
</div>
</div>
</div>
<!-- New section for formatted JSON with tabs -->
<div class="card shadow-sm mt-4" id="formattedJsonBox">
<div class="card-body">
<h3 class="h5 card-title mb-3">Formatted JSON</h3>
<ul class="nav nav-tabs" id="jsonTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="tree-tab" data-bs-toggle="tab" data-bs-target="#tree-view" type="button" role="tab">
<i class="bi bi-diagram-3 me-2"></i>Tree View
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="raw-tab" data-bs-toggle="tab" data-bs-target="#raw-view" type="button" role="tab">
<i class="bi bi-code me-2"></i>Raw JSON
</button>
</li>
</ul>
<div class="tab-content mt-3" id="jsonTabsContent">
<div class="tab-pane fade show active" id="tree-view" role="tabpanel">
<div class="tree-view-container" id="treeView"></div>
</div>
<div class="tab-pane fade" id="raw-view" role="tabpanel">
<pre class="tree-view-container" id="rawView"></pre>
</div>
</div>
</div>
</div>
<div class="mt-5">
<h2 class="h4 mb-3">About JSON Lint Tool</h2>
<div class="card">
<div class="card-body">
<p>Our JSON Lint Tool is a free online service that helps developers and data analysts work with JSON data more efficiently. Whether you're validating API responses, debugging JSON configurations, or just need to format your JSON data, our tool provides a simple and intuitive interface to get the job done.</p>
<h3 class="h5 mt-4">Key Features:</h3>
<ul>
<li>Real-time JSON validation</li>
<li>Automatic JSON formatting and beautification</li>
<li>Interactive tree view for easy navigation</li>
<li>Dark and light mode support</li>
<li>Mobile-friendly interface</li>
</ul>
<h3 class="h5 mt-4">What is JSON?</h3>
<p>JSON (JavaScript Object Notation) is a lightweight data-interchange format that is easy for humans to read and write and easy for machines to parse and generate. It is widely used for transmitting data between a server and web application, as well as for configuration files and data storage.</p>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jsoneditor@9.10.2/dist/jsoneditor.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const jsonInput = document.getElementById('jsonInput');
const validateBtn = document.getElementById('validateBtn');
const formatBtn = document.getElementById('formatBtn');
const shareBtn = document.getElementById('shareBtn');
const clearBtn = document.getElementById('clearBtn');
const treeView = document.getElementById('treeView');
const rawView = document.getElementById('rawView');
const formattedJsonBox = document.getElementById('formattedJsonBox');
let editor = null;
function showError(message) {
const errorAlert = document.createElement('div');
errorAlert.className = 'alert alert-danger alert-dismissible fade show';
errorAlert.innerHTML = `
<i class="bi bi-exclamation-circle-fill me-2"></i>
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
`;
formattedJsonBox.querySelector('.card-body').insertBefore(errorAlert, formattedJsonBox.querySelector('.nav-tabs'));
}
function showSuccess(message) {
const successAlert = document.createElement('div');
successAlert.className = 'alert alert-success alert-dismissible fade show';
successAlert.innerHTML = `
<i class="bi bi-check-circle-fill me-2"></i>
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
`;
formattedJsonBox.querySelector('.card-body').insertBefore(successAlert, formattedJsonBox.querySelector('.nav-tabs'));
}
function updateTreeView(json) {
// Clear previous editor
if (editor) {
editor.destroy();
}
// Create new editor with enhanced options
const options = {
mode: 'tree',
modes: ['tree', 'view', 'form', 'code', 'text'],
theme: document.documentElement.getAttribute('data-bs-theme') === 'dark' ? 'ace/theme/monokai' : 'ace/theme/github',
enableSort: false,
enableTransform: false,
search: true,
history: true,
navigationBar: true,
statusBar: true,
mainMenuBar: true,
onChange: function() {
try {
const updatedJson = editor.get();
jsonInput.value = JSON.stringify(updatedJson, null, 2);
rawView.textContent = JSON.stringify(updatedJson, null, 2);
} catch (e) {
console.error('Error updating JSON:', e);
}
},
onNodeName: function(node) {
// Safely handle node animation
if (node && node.dom && node.dom.expandable) {
const expandable = node.dom.expandable;
expandable.classList.add('expanded');
setTimeout(() => {
expandable.classList.remove('expanded');
}, 300);
}
},
onEditable: function(node) {
return false; // Make the tree view read-only
}
};
editor = new JSONEditor(treeView, options);
// Add initial animation
setTimeout(() => {
const container = treeView.querySelector('.jsoneditor-tree');
if (container) {
container.style.opacity = '0';
container.style.transform = 'translateY(20px)';
setTimeout(() => {
container.style.opacity = '1';
container.style.transform = 'translateY(0)';
}, 100);
}
}, 100);
// Set the JSON after a small delay to ensure DOM is ready
setTimeout(() => {
editor.set(json);
}, 150);
// Update raw view
rawView.textContent = JSON.stringify(json, null, 2);
// Show the formatted JSON box
document.getElementById('formattedJsonBox').style.display = 'block';
}
// Hide formatted JSON box initially
document.getElementById('formattedJsonBox').style.display = 'none';
// Load saved JSON from local storage
const savedJson = localStorage.getItem('jsonlint_history');
if (savedJson) {
try {
const parsedJson = JSON.parse(savedJson);
jsonInput.value = JSON.stringify(parsedJson, null, 2);
updateTreeView(parsedJson);
} catch (e) {
console.error('Error loading saved JSON:', e);
}
}
// Save JSON to local storage
function saveToHistory() {
try {
const json = JSON.parse(jsonInput.value);
if (json && Object.keys(json).length > 0) {
localStorage.setItem('jsonlint_history', JSON.stringify(json));
}
} catch (e) {
console.error('Error saving JSON to history:', e);
}
}
// Generate shareable link
function generateShareLink() {
try {
const json = JSON.parse(jsonInput.value);
const jsonString = JSON.stringify(json);
const encodedJson = encodeURIComponent(jsonString);
const url = new URL(window.location.href);
url.searchParams.set('json', encodedJson);
return url.toString();
} catch (e) {
console.error('Error generating share link:', e);
return null;
}
}
// Share button click handler
shareBtn.addEventListener('click', function() {
const shareLink = generateShareLink();
if (shareLink) {
// Copy to clipboard
navigator.clipboard.writeText(shareLink).then(function() {
// Show success message
const originalText = shareBtn.innerHTML;
shareBtn.innerHTML = '<i class="bi bi-check-circle me-2"></i>Copied!';
setTimeout(function() {
shareBtn.innerHTML = originalText;
}, 2000);
}).catch(function(err) {
console.error('Error copying to clipboard:', err);
alert('Failed to copy link to clipboard. Please copy the URL manually.');
});
} else {
alert('Failed to generate share link. Please make sure your JSON is valid.');
}
});
// Validate JSON
validateBtn.addEventListener('click', function() {
try {
const json = JSON.parse(jsonInput.value);
showSuccess('Valid JSON!');
updateTreeView(json);
} catch (e) {
showError(`Invalid JSON: ${e.message}`);
formattedJsonBox.style.display = 'block';
if (editor) {
editor.destroy();
editor = null;
}
treeView.innerHTML = '';
rawView.textContent = '';
}
});
// Format JSON
formatBtn.addEventListener('click', function() {
try {
const json = JSON.parse(jsonInput.value);
jsonInput.value = JSON.stringify(json, null, 2);
showSuccess('JSON formatted successfully!');
updateTreeView(json);
} catch (e) {
showError(`Cannot format invalid JSON: ${e.message}`);
formattedJsonBox.style.display = 'block';
if (editor) {
editor.destroy();
editor = null;
}
treeView.innerHTML = '';
rawView.textContent = '';
}
});
// Clear input
clearBtn.addEventListener('click', function() {
jsonInput.value = '';
formattedJsonBox.style.display = 'none';
if (editor) {
editor.destroy();
editor = null;
}
treeView.innerHTML = '';
rawView.textContent = '';
localStorage.removeItem('jsonlint_history');
});
// Auto-save when input changes
jsonInput.addEventListener('input', function() {
try {
const json = JSON.parse(jsonInput.value);
if (json && Object.keys(json).length > 0) {
saveToHistory();
}
} catch (e) {
// Don't save invalid JSON
}
});
// Load JSON from URL if present
const urlParams = new URLSearchParams(window.location.search);
const jsonParam = urlParams.get('json');
if (jsonParam) {
try {
const decodedJson = decodeURIComponent(jsonParam);
const json = JSON.parse(decodedJson);
jsonInput.value = JSON.stringify(json, null, 2);
updateTreeView(json);
} catch (e) {
console.error('Error loading JSON from URL:', e);
}
}
});
</script>
@endpush