577 lines
20 KiB
PHP
577 lines
20 KiB
PHP
@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
|