423 lines
No EOL
12 KiB
HTML
423 lines
No EOL
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Markdown/JSON Toolkit</title>
|
|
<style>
|
|
/* style.css */
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
margin: 20px;
|
|
background-color: #f4f4f8;
|
|
color: #333;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
header {
|
|
text-align: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
h1 {
|
|
color: #007bff;
|
|
margin-bottom: 0.5em;
|
|
}
|
|
|
|
nav {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
margin-top: 1em;
|
|
}
|
|
.tab-button {
|
|
padding: 0.75em 1.5em;
|
|
background-color: #f9f9f9;
|
|
border: 1px solid #ddd;
|
|
border-bottom: none;
|
|
border-top-left-radius: 6px;
|
|
border-top-right-radius: 6px;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s ease-in-out, transform 0.2s;
|
|
|
|
color: #333;
|
|
}
|
|
|
|
.tab-button.active {
|
|
background-color: #fff;
|
|
border-bottom: 2px solid #007bff;
|
|
}
|
|
|
|
.tab-button:hover {
|
|
background-color: #eee;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
main {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
background-color: #fff;
|
|
padding: 30px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.tab-content {
|
|
display: none;
|
|
}
|
|
|
|
.tab-content.active {
|
|
display: block;
|
|
}
|
|
|
|
h2 {
|
|
color: #333;
|
|
margin-bottom: 1em;
|
|
}
|
|
|
|
p {
|
|
color: #555;
|
|
margin-bottom: 1em;
|
|
}
|
|
|
|
textarea {
|
|
width: calc(100% - 16px);
|
|
min-height: 150px;
|
|
padding: 0.75em;
|
|
margin-bottom: 1.5em;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
box-sizing: border-box;
|
|
font-size: 1rem;
|
|
font-family: 'Courier New', monospace;
|
|
transition: border-color 0.3s ease-in-out, box-shadow 0.3s;
|
|
}
|
|
|
|
textarea:focus {
|
|
outline: none;
|
|
border-color: #007bff;
|
|
box-shadow: 0 0 5px rgba(0, 123, 255, 0.2);
|
|
}
|
|
|
|
.input-group {
|
|
margin-bottom: 1.5em;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
margin-bottom: 0.5em;
|
|
font-weight: bold;
|
|
}
|
|
|
|
input[type="text"] {
|
|
width: calc(100% - 12px);
|
|
padding: 0.75em;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
box-sizing: border-box;
|
|
font-size: 1rem;
|
|
transition: border-color 0.3s ease-in-out, box-shadow 0.3s;
|
|
}
|
|
|
|
input[type="text"]:focus {
|
|
outline: none;
|
|
border-color: #007bff;
|
|
box-shadow: 0 0 5px rgba(0, 123, 255, 0.2);
|
|
}
|
|
|
|
button {
|
|
padding: 0.75em 1.5em;
|
|
background-color: #007bff;
|
|
color: #fff;
|
|
border: none;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
transition: background-color 0.3s ease-in-out, transform 0.2s;
|
|
}
|
|
|
|
button:hover {
|
|
background-color: #0056b3;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.output-area {
|
|
margin-top: 2em;
|
|
padding: 1em;
|
|
border: 1px solid #eee;
|
|
border-radius: 6px;
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
h3 {
|
|
margin-top: 0;
|
|
color: #333;
|
|
margin-bottom: 0.75em;
|
|
}
|
|
|
|
pre {
|
|
white-space: pre-wrap;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.9rem;
|
|
color: #222;
|
|
line-height: 1.4;
|
|
margin: 0;
|
|
}
|
|
|
|
.copy-button {
|
|
margin-top: 0.75em;
|
|
background-color: #28a745;
|
|
color: #fff;
|
|
transition: background-color 0.3s ease-in-out, transform 0.2s;
|
|
}
|
|
|
|
.copy-button:hover {
|
|
background-color: #218838;
|
|
transform: translateY(-2px);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>Markdown/JSON Toolkit</h1>
|
|
<nav>
|
|
<button class="tab-button active" data-tab="markdownToJson">Markdown to JSON</button>
|
|
<button class="tab-button" data-tab="jsonUpdate">Update JSON</button>
|
|
<button class="tab-button" data-tab="jsonToMarkdown">JSON to Markdown</button>
|
|
</nav>
|
|
</header>
|
|
|
|
<main>
|
|
<section id="markdownToJson" class="tab-content active">
|
|
<h2>Markdown to JSON</h2>
|
|
<p>Paste your GitHub flavored Markdown table here:</p>
|
|
<textarea id="markdownInput" placeholder="Paste Markdown table"></textarea>
|
|
<button id="convertToJsonBtn">Convert to JSON</button>
|
|
<aside class="output-area">
|
|
<h3>JSON Output</h3>
|
|
<pre id="jsonOutput"></pre>
|
|
<button id="selectJsonBtn" class="copy-button" style="display: none;">Select All</button>
|
|
</aside>
|
|
</section>
|
|
|
|
<section id="jsonUpdate" class="tab-content">
|
|
<h2>Update JSON</h2>
|
|
<p>Paste your existing JSON data here:</p>
|
|
<textarea id="existingJsonInput" placeholder="Paste existing JSON"></textarea>
|
|
|
|
<div class="input-group">
|
|
<label for="newProviderName">New Debrid Provider Name:</label>
|
|
<input type="text" id="newProviderName" placeholder="New Provider Name">
|
|
</div>
|
|
|
|
<div class="input-group">
|
|
<label for="supportedServices">Supported Services (comma-separated):</label>
|
|
<input type="text" id="supportedServices" placeholder="e.g., 1Fichier, 4Shared">
|
|
</div>
|
|
|
|
<button id="updateJsonBtn">Update JSON</button>
|
|
<aside class="output-area">
|
|
<h3>Updated JSON Output</h3>
|
|
<pre id="updatedJsonOutput"></pre>
|
|
<button id="selectUpdatedJsonBtn" class="copy-button" style="display: none;">Select All</button>
|
|
</aside>
|
|
</section>
|
|
|
|
<section id="jsonToMarkdown" class="tab-content">
|
|
<h2>JSON to Markdown</h2>
|
|
<p>Paste your JSON data here:</p>
|
|
<textarea id="inputJsonForMarkdown" placeholder="Paste JSON data"></textarea>
|
|
<button id="convertToMarkdownBtn">Convert to Markdown Table</button>
|
|
<aside class="output-area">
|
|
<h3>Markdown Table Output</h3>
|
|
<pre id="markdownOutput"></pre>
|
|
<button id="selectMarkdownBtn" class="copy-button" style="display: none;">Select All</button>
|
|
</aside>
|
|
</section>
|
|
</main>
|
|
|
|
<script>
|
|
/* script.js */
|
|
const tabButtons = document.querySelectorAll('.tab-button');
|
|
const tabContents = document.querySelectorAll('.tab-content');
|
|
|
|
const markdownInput = document.getElementById('markdownInput');
|
|
const convertToJsonBtn = document.getElementById('convertToJsonBtn');
|
|
const jsonOutput = document.getElementById('jsonOutput');
|
|
const selectJsonBtn = document.getElementById('selectJsonBtn');
|
|
|
|
const existingJsonInput = document.getElementById('existingJsonInput');
|
|
const newProviderNameInput = document.getElementById('newProviderName');
|
|
const supportedServicesInput = document.getElementById('supportedServices');
|
|
const updateJsonBtn = document.getElementById('updateJsonBtn');
|
|
const updatedJsonOutput = document.getElementById('updatedJsonOutput');
|
|
const selectUpdatedJsonBtn = document.getElementById('selectUpdatedJsonBtn');
|
|
|
|
const inputJsonForMarkdown = document.getElementById('inputJsonForMarkdown');
|
|
const convertToMarkdownBtn = document.getElementById('convertToMarkdownBtn');
|
|
const markdownOutput = document.getElementById('markdownOutput');
|
|
const selectMarkdownBtn = document.getElementById('selectMarkdownBtn');
|
|
|
|
let currentJSON = {};
|
|
|
|
// Helper function to activate a tab
|
|
const activateTab = (tabId) => {
|
|
tabContents.forEach(content => content.classList.remove('active'));
|
|
tabButtons.forEach(button => button.classList.remove('active'));
|
|
document.getElementById(tabId).classList.add('active');
|
|
document.querySelector(`.tab-button[data-tab="${tabId}"]`).classList.add('active');
|
|
};
|
|
|
|
// Event listeners for tab clicks
|
|
tabButtons.forEach(button => {
|
|
button.addEventListener('click', (event) => {
|
|
activateTab(event.target.dataset.tab);
|
|
});
|
|
});
|
|
|
|
// Markdown to JSON functionality
|
|
convertToJsonBtn.addEventListener('click', () => {
|
|
const markdownText = markdownInput.value.trim();
|
|
if (!markdownText) {
|
|
jsonOutput.textContent = "Please paste a Markdown table.";
|
|
selectJsonBtn.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
const lines = markdownText.split('\n').map(line => line.trim());
|
|
if (lines.length < 2) {
|
|
jsonOutput.textContent = "Invalid Markdown table.";
|
|
selectJsonBtn.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
const [headerLine, ...dataLines] = lines;
|
|
const headersWithBold = headerLine.split('|').map(h => h.trim()).filter(Boolean);
|
|
const headers = headersWithBold.slice(1).map(h => h.replace(/\*\*/g, ''));
|
|
|
|
const jsonData = {};
|
|
dataLines.forEach(line => {
|
|
if (line.startsWith('|') && !line.includes('-')) {
|
|
const cellsWithBold = line.split('|').map(c => c.trim()).filter(Boolean);
|
|
if (cellsWithBold.length > 0) {
|
|
const host = cellsWithBold[0].replace(/\*\*/g, '');
|
|
jsonData[host] = {};
|
|
for (let i = 1; i < cellsWithBold.length; i++) {
|
|
jsonData[host][headers[i - 1]] = cellsWithBold[i].toLowerCase() === 'yes' ? 'yes' : 'no';
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
currentJSON = jsonData;
|
|
jsonOutput.textContent = JSON.stringify(currentJSON, null, 2);
|
|
selectJsonBtn.style.display = 'block';
|
|
});
|
|
|
|
// Update JSON functionality
|
|
updateJsonBtn.addEventListener('click', () => {
|
|
const jsonInputValue = existingJsonInput.value.trim();
|
|
const newProviderName = newProviderNameInput.value.trim();
|
|
const supportedServicesInputVal = supportedServicesInput.value.trim();
|
|
const supportedServicesList = supportedServicesInputVal.split(',').map(s => s.trim());
|
|
|
|
let baseJSON = currentJSON;
|
|
if (jsonInputValue) {
|
|
try {
|
|
baseJSON = JSON.parse(jsonInputValue);
|
|
} catch (error) {
|
|
updatedJsonOutput.textContent = "Invalid JSON format.";
|
|
selectUpdatedJsonBtn.style.display = 'none';
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!newProviderName || !supportedServicesInputVal) {
|
|
updatedJsonOutput.textContent = "Please enter the new provider name and supported services.";
|
|
selectUpdatedJsonBtn.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
const updatedJSON = { ...baseJSON };
|
|
|
|
for (const serviceName in updatedJSON) {
|
|
updatedJSON[serviceName][newProviderName] = supportedServicesList.includes(serviceName) ? 'yes' : 'no';
|
|
}
|
|
|
|
supportedServicesList.forEach(service => {
|
|
if (!updatedJSON.hasOwnProperty(service)) {
|
|
updatedJSON[service] = {};
|
|
for (const existingService in baseJSON) {
|
|
for (const provider in baseJSON[existingService]) {
|
|
if (!updatedJSON[service].hasOwnProperty(provider)) {
|
|
updatedJSON[service][provider] = 'no';
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
updatedJSON[service][newProviderName] = 'yes';
|
|
}
|
|
});
|
|
|
|
currentJSON = updatedJSON;
|
|
updatedJsonOutput.textContent = JSON.stringify(currentJSON, null, 2);
|
|
selectUpdatedJsonBtn.style.display = 'block';
|
|
});
|
|
|
|
// JSON to Markdown functionality
|
|
convertToMarkdownBtn.addEventListener('click', () => {
|
|
const jsonString = inputJsonForMarkdown.value.trim();
|
|
let jsonData = currentJSON; // Default to currentJSON if input is empty
|
|
|
|
if (jsonString) {
|
|
try {
|
|
jsonData = JSON.parse(jsonString);
|
|
} catch (error) {
|
|
markdownOutput.textContent = "Invalid JSON format.";
|
|
selectMarkdownBtn.style.display = 'none';
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!jsonData || Object.keys(jsonData).length === 0) {
|
|
markdownOutput.textContent = "JSON data is empty.";
|
|
selectMarkdownBtn.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
const services = Object.keys(jsonData);
|
|
const firstService = jsonData[services[0]];
|
|
const providers = Object.keys(firstService);
|
|
|
|
let markdownTable = `| **Service Name** | ${providers.map(p => `**${p}**`).join(' | ')} |\n`;
|
|
markdownTable += `| ----------------- | ${providers.map(() => '---------------').join(' | ')} |\n`;
|
|
markdownTable += services.map(service => {
|
|
const row = `| **${service}** | ${providers.map(provider => jsonData[service][provider].charAt(0).toUpperCase() + jsonData[service][provider].slice(1)).join(' | ')} |`;
|
|
return row;
|
|
}).join('\n');
|
|
|
|
markdownOutput.textContent = markdownTable;
|
|
selectMarkdownBtn.style.display = 'block';
|
|
});
|
|
|
|
// Select all output functionality
|
|
const selectAllOutput = (outputElement) => {
|
|
const range = document.createRange();
|
|
range.selectNodeContents(outputElement);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
document.execCommand('copy');
|
|
selection.removeAllRanges();
|
|
alert('Output copied to clipboard!');
|
|
};
|
|
|
|
selectJsonBtn.addEventListener('click', () => selectAllOutput(jsonOutput));
|
|
selectUpdatedJsonBtn.addEventListener('click', () => selectAllOutput(updatedJsonOutput));
|
|
selectMarkdownBtn.addEventListener('click', () => selectAllOutput(markdownOutput));
|
|
|
|
// Initialize the first tab as active
|
|
activateTab('markdownToJson');
|
|
</script>
|
|
</body>
|
|
</html> |