649 lines
33 KiB
HTML
649 lines
33 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>funding.json generator</title>
|
|
<style>
|
|
body {
|
|
font-family: system-ui, sans-serif;
|
|
margin: 0;
|
|
padding: 20px;
|
|
background-color: #f4f4f9;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
flex-wrap: wrap;
|
|
}
|
|
form, pre, .json-output {
|
|
width: 48%;
|
|
}
|
|
.output {
|
|
width: 96%;
|
|
min-height: 420px;
|
|
}
|
|
label {
|
|
font-weight: bold;
|
|
display: block;
|
|
margin: 10px 0 5px;
|
|
}
|
|
label.required:after {
|
|
content: " *";
|
|
color: red;
|
|
}
|
|
input, textarea, select {
|
|
width: 100%;
|
|
padding: 8px;
|
|
margin-bottom: 10px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
box-sizing: border-box;
|
|
}
|
|
button {
|
|
padding: 10px 0;
|
|
font-size: 16px;
|
|
border: none;
|
|
cursor: pointer;
|
|
background-color: #1DB954;
|
|
color: white;
|
|
border-radius: 4px;
|
|
width: 100%;
|
|
margin-top: 10px;
|
|
}
|
|
button.secondary {
|
|
background-color: #696969;
|
|
margin-top: 0px;
|
|
}
|
|
button.ghost {
|
|
background-color: transparent;
|
|
color: #aeaeae;
|
|
width: fit-content;
|
|
padding-left: 10px;
|
|
padding-right: 10px;
|
|
font-size: 12px;
|
|
}
|
|
button.ghost:hover {
|
|
background-color: #aeaeae;
|
|
color: white;
|
|
}
|
|
pre {
|
|
padding: 10px;
|
|
background-color: #eee;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
height: auto;
|
|
max-height: 600px;
|
|
overflow-y: auto;
|
|
margin-bottom: 10px;
|
|
}
|
|
.dynamic-section {
|
|
margin-bottom: 2px;
|
|
}
|
|
h2, h3 {
|
|
margin-top: 4px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.section {
|
|
margin-bottom: 30px;
|
|
}
|
|
.entity-section, .project-section, .channel-section, .plan-section, .history-section {
|
|
border: 1px solid #ccc;
|
|
padding: 10px;
|
|
margin-bottom: 20px;
|
|
border-radius: 4px;
|
|
background-color: #fff;
|
|
position: relative;
|
|
}
|
|
.project-section::before,
|
|
.channel-section::before,
|
|
.plan-section::before,
|
|
.history-section::before {
|
|
position: absolute;
|
|
top: 5px;
|
|
left: 10px;
|
|
font-weight: bold;
|
|
color: #555;
|
|
}
|
|
.error {
|
|
color: red;
|
|
font-size: 0.9em;
|
|
margin-top: -5px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.removableHeader {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 20px 0 20px 0;
|
|
align-items: center;
|
|
}
|
|
.removableHeader > * {
|
|
margin: 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
flex-direction: column;
|
|
}
|
|
form, pre, .json-output {
|
|
width: 100%;
|
|
}
|
|
.output {
|
|
width: 94%;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<form id="fundingForm">
|
|
<h1>FLOSS Fund Application Form</h1>
|
|
|
|
<!-- File Upload Button -->
|
|
<div class="section">
|
|
<label for="fileUpload"><strong>Load an existing funding.json</strong></label>
|
|
<input type="file" id="fileUpload" accept=".json">
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.getElementById('fileUpload').addEventListener('change', function(event) {
|
|
const file = event.target.files[0];
|
|
if (!file) return;
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
try {
|
|
const data = JSON.parse(e.target.result);
|
|
|
|
// Populate Entity
|
|
if (data.entity) {
|
|
document.getElementById('entityType').value = data.entity.type || '';
|
|
document.getElementById('entityRole').value = data.entity.role || '';
|
|
document.getElementById('entityName').value = data.entity.name || '';
|
|
document.getElementById('entityEmail').value = data.entity.email || '';
|
|
document.getElementById('entityPhone').value = data.entity.phone || '';
|
|
document.getElementById('entityDescription').value = data.entity.description || '';
|
|
if (data.entity.webpageUrl) {
|
|
document.getElementById('entityWebpageUrl').value = data.entity.webpageUrl.url || '';
|
|
document.getElementById('entityWebpageWellKnown').value = data.entity.webpageUrl.wellKnown || '';
|
|
}
|
|
}
|
|
|
|
// Clear and Populate Projects
|
|
const projectsContainer = document.getElementById('projectsContainer');
|
|
projectsContainer.innerHTML = '';
|
|
if (Array.isArray(data.projects)) {
|
|
data.projects.forEach(project => {
|
|
addProject();
|
|
const section = projectsContainer.lastElementChild;
|
|
section.querySelector('.projectGuid').value = project.guid || '';
|
|
section.querySelector('.projectName').value = project.name || '';
|
|
section.querySelector('.projectDescription').value = project.description || '';
|
|
if (project.webpageUrl) {
|
|
section.querySelector('.projectWebpageUrl').value = project.webpageUrl.url || '';
|
|
section.querySelector('.projectWebpageWellKnown').value = project.webpageUrl.wellKnown || '';
|
|
}
|
|
if (project.repositoryUrl) {
|
|
section.querySelector('.projectRepositoryUrl').value = project.repositoryUrl.url || '';
|
|
section.querySelector('.projectRepositoryWellKnown').value = project.repositoryUrl.wellKnown || '';
|
|
}
|
|
section.querySelector('.projectLicenses').value = Array.isArray(project.licenses) ? project.licenses.join(', ') : '';
|
|
section.querySelector('.projectTags').value = Array.isArray(project.tags) ? project.tags.join(', ') : '';
|
|
});
|
|
}
|
|
|
|
// Clear and Populate Funding Channels
|
|
const fundingChannelsContainer = document.getElementById('fundingChannelsContainer');
|
|
fundingChannelsContainer.innerHTML = '';
|
|
if (data.funding && Array.isArray(data.funding.channels)) {
|
|
data.funding.channels.forEach((channel, idx) => {
|
|
addFundingChannel(true);
|
|
const section = fundingChannelsContainer.lastElementChild;
|
|
section.querySelector('.fundingChannelGuid').value = channel.guid || '';
|
|
section.querySelector('.fundingChannelType').value = channel.type || '';
|
|
section.querySelector('.fundingChannelAddress').value = channel.address || '';
|
|
section.querySelector('.fundingChannelDescription').value = channel.description || '';
|
|
});
|
|
// Hide remove button for first channel if only one
|
|
if (fundingChannelsContainer.children.length === 1) {
|
|
fundingChannelsContainer.querySelector('.remove-btn').style.display = 'none';
|
|
}
|
|
} else {
|
|
// Add one empty channel if none
|
|
addFundingChannel(false);
|
|
}
|
|
|
|
// Clear and Populate Funding Plans
|
|
const fundingPlansContainer = document.getElementById('fundingPlansContainer');
|
|
fundingPlansContainer.innerHTML = '';
|
|
if (data.funding && Array.isArray(data.funding.plans)) {
|
|
data.funding.plans.forEach((plan, idx) => {
|
|
addFundingPlan(true);
|
|
const section = fundingPlansContainer.lastElementChild;
|
|
section.querySelector('.planGuid').value = plan.guid || '';
|
|
section.querySelector('.planStatus').value = plan.status || '';
|
|
section.querySelector('.planName').value = plan.name || '';
|
|
section.querySelector('.planDescription').value = plan.description || '';
|
|
section.querySelector('.planAmount').value = plan.amount != null ? plan.amount : '';
|
|
section.querySelector('.planCurrency').value = plan.currency || '';
|
|
section.querySelector('.planFrequency').value = plan.frequency || '';
|
|
section.querySelector('.planChannels').value = Array.isArray(plan.channels) ? plan.channels.join(', ') : '';
|
|
});
|
|
// Hide remove button for first plan if only one
|
|
if (fundingPlansContainer.children.length === 1) {
|
|
fundingPlansContainer.querySelector('.remove-btn').style.display = 'none';
|
|
}
|
|
} else {
|
|
// Add one empty plan if none
|
|
addFundingPlan(false);
|
|
}
|
|
|
|
// Clear and Populate Funding History
|
|
const fundingHistoryContainer = document.getElementById('fundingHistoryContainer');
|
|
fundingHistoryContainer.innerHTML = '';
|
|
if (data.funding && Array.isArray(data.funding.history)) {
|
|
data.funding.history.forEach(history => {
|
|
addFundingHistory();
|
|
const section = fundingHistoryContainer.lastElementChild;
|
|
section.querySelector('.historyYear').value = history.year || '';
|
|
section.querySelector('.historyIncome').value = history.income != null ? history.income : '';
|
|
section.querySelector('.historyExpenses').value = history.expenses != null ? history.expenses : '';
|
|
section.querySelector('.historyTaxes').value = history.taxes != null ? history.taxes : '';
|
|
section.querySelector('.historyCurrency').value = history.currency || '';
|
|
section.querySelector('.historyDescription').value = history.description || '';
|
|
});
|
|
}
|
|
|
|
// Show JSON in output
|
|
document.getElementById('output').textContent = JSON.stringify(data, null, 2);
|
|
} catch (err) {
|
|
alert('Invalid JSON file.');
|
|
}
|
|
};
|
|
reader.readAsText(file);
|
|
});
|
|
});
|
|
</script>
|
|
</div>
|
|
|
|
<!-- Entity Section -->
|
|
<div class="section">
|
|
<h2>Entity</h2>
|
|
<div class="entity-section">
|
|
<label for="entityType" class="required">Type</label>
|
|
<select id="entityType" required>
|
|
<option value="">Select Type</option>
|
|
<option value="individual">Individual</option>
|
|
<option value="group">Group</option>
|
|
<option value="organisation">Organisation</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
|
|
<label for="entityRole" class="required">Role</label>
|
|
<select id="entityRole" required>
|
|
<option value="">Select Role</option>
|
|
<option value="owner">Owner</option>
|
|
<option value="steward">Steward</option>
|
|
<option value="maintainer">Maintainer</option>
|
|
<option value="contributor">Contributor</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
|
|
<label for="entityName" class="required">Name</label>
|
|
<input type="text" id="entityName" placeholder="Name of the entity" maxlength="250" required>
|
|
|
|
<label for="entityEmail" class="required">Email</label>
|
|
<input type="email" id="entityEmail" maxlength="250"
|
|
required>
|
|
|
|
<label for="entityPhone">Phone</label>
|
|
<input type="tel" id="entityPhone" maxlength="32">
|
|
|
|
<label for="entityDescription"
|
|
class="required">Description</label>
|
|
<textarea id="entityDescription" placeholder="Information about the entity" maxlength="2000" required></textarea>
|
|
|
|
<label for="entityWebpageUrl" class="required">Webpage
|
|
URL</label>
|
|
<input type="url" id="entityWebpageUrl" maxlength="250"
|
|
required placeholder="Webpage with information about the entity">
|
|
|
|
<label for="entityWebpageWellKnown">Well known URL</label>
|
|
<input type="url" id="entityWebpageWellKnown"
|
|
maxlength="250" placeholder="If the above URL and the URL of the funding.json do not have the same hostname">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Projects Section -->
|
|
<div class="section">
|
|
<h2>Projects</h2>
|
|
<div id="projectsContainer"></div>
|
|
<button type="button" class="secondary"
|
|
onclick="addProject()">Add Project</button>
|
|
</div>
|
|
|
|
<!-- Funding Section -->
|
|
<div class="section">
|
|
<h2>Funding</h2>
|
|
|
|
<!-- Funding Channels (Dynamic) -->
|
|
<div id="fundingChannelsContainer"></div>
|
|
<button type="button" class="secondary"
|
|
onclick="addFundingChannel()">Add Funding Channel</button>
|
|
|
|
<!-- Funding Plans (Dynamic) -->
|
|
<div id="fundingPlansContainer" style="margin-top: 42px;"></div>
|
|
<button type="button" class="secondary"
|
|
onclick="addFundingPlan()">Add Funding Plan</button>
|
|
|
|
<!-- Funding History (Dynamic) -->
|
|
<div id="fundingHistoryContainer" style="margin-top:
|
|
42px;"></div>
|
|
<button type="button" class="secondary"
|
|
onclick="addFundingHistory()">Add Funding History</button>
|
|
</div>
|
|
|
|
<!-- Generate JSON Button -->
|
|
<button type="submit">Generate JSON</button>
|
|
</form>
|
|
|
|
<!-- JSON Output Section -->
|
|
<div class="json-output">
|
|
<h2>funding.json</h2>
|
|
<pre id="output" class="output"></pre>
|
|
<button id="copyButton" class="primary" style="width: 100%;">Copy funding.json</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Function to add more projects dynamically
|
|
function addProject() {
|
|
const container = document.getElementById('projectsContainer');
|
|
const newProject = document.createElement('div');
|
|
newProject.classList.add('project-section');
|
|
newProject.innerHTML = `
|
|
<div class="removableHeader">
|
|
<h3>Project</h3>
|
|
<button type="button" class="ghost remove-btn">Remove</button>
|
|
</div>
|
|
|
|
<label for="projectGuid" class="required">GUID</label>
|
|
<input type="text" class="projectGuid" maxlength="32" pattern="^[a-z0-9\-]+$" required placeholder="my-cool-project">
|
|
|
|
<label for="projectName" class="required">Name</label>
|
|
<input type="text" class="projectName" maxlength="250" placeholder="Name of the project" required>
|
|
|
|
<label for="projectDescription" class="required">Description</label>
|
|
<textarea class="projectDescription" maxlength="2000" placeholder="Description of the project" required></textarea>
|
|
|
|
<label for="projectWebpageUrl" class="required">Webpage URL</label>
|
|
<input type="url" class="projectWebpageUrl" maxlength="250" required placeholder="Webpage with information about the project">
|
|
|
|
<label for="projectWebpageWellKnown">Well known URL</label>
|
|
<input type="url" class="projectWebpageWellKnown" maxlength="250" placeholder="If the above URL and the URL of the funding.json do not have the same hostname">
|
|
|
|
<label for="projectRepositoryUrl" class="required">Repository URL</label>
|
|
<input type="url" class="projectRepositoryUrl" maxlength="250" required placeholder="Repository where the project's source code and assets are">
|
|
|
|
<label for="projectRepositoryWellKnown">Repository Well Known URL (optional)</label>
|
|
<input type="url" class="projectRepositoryWellKnown" maxlength="250" placeholder="If the above URL and the URL of the funding.json do not have the same hostname">
|
|
|
|
<label for="projectLicenses" class="required">Licenses (comma separated, up to 5)</label>
|
|
<input type="text" class="projectLicenses" required placeholder="spdx:GPL-3.0, spdx:CC-BY-SA-4.0">
|
|
|
|
<label for="projectTags" class="required">Tags (comma separated, up to 10)</label>
|
|
<input type="text" class="projectTags" required placeholder="programming, developer-tools">
|
|
`;
|
|
|
|
// Add event listener to remove this project
|
|
const removeBtn = newProject.querySelector(".remove-btn");
|
|
removeBtn.addEventListener("click", function () {
|
|
container.removeChild(newProject);
|
|
});
|
|
|
|
container.appendChild(newProject);
|
|
}
|
|
|
|
// Function to add more funding channels dynamically
|
|
function addFundingChannel(showRemove = true) {
|
|
const container = document.getElementById('fundingChannelsContainer');
|
|
const newChannel = document.createElement('div');
|
|
newChannel.classList.add('channel-section');
|
|
newChannel.innerHTML = `
|
|
<div class="removableHeader">
|
|
<h3>Channel</h3>
|
|
<button type="button" class="ghost remove-btn">Remove</button>
|
|
</div>
|
|
|
|
<label for="fundingChannelGuid" class="required">GUID</label>
|
|
<input type="text" class="fundingChannelGuid" maxlength="32" pattern="^[a-z0-9\-]+$" required placeholder="mybank, my-paypal">
|
|
|
|
<label for="fundingChannelType" class="required">Type</label>
|
|
<select class="fundingChannelType" required>
|
|
<option value="">Select Type</option>
|
|
<option value="bank">Bank</option>
|
|
<option value="payment-provider">Payment provider</option>
|
|
<option value="cheque">Cheque</option>
|
|
<option value="cash">Cash</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
|
|
<label for="fundingChannelAddress">Address</label>
|
|
<input type="text" class="fundingChannelAddress" maxlength="250" placeholder="Account: 12345 (branch: ABCX)">
|
|
|
|
<label for="fundingChannelDescription">Description</label>
|
|
<textarea class="fundingChannelDescription" maxlength="500" placeholder="Additional description or instructions for the payment channel"></textarea>
|
|
`;
|
|
|
|
const removeBtn = newChannel.querySelector(".remove-btn");
|
|
|
|
if (showRemove) {
|
|
// Add event listener to remove this channel
|
|
removeBtn.addEventListener("click", function () {
|
|
container.removeChild(newChannel);
|
|
});
|
|
} else {
|
|
removeBtn.style.display = 'none';
|
|
}
|
|
|
|
|
|
container.appendChild(newChannel);
|
|
}
|
|
|
|
|
|
// Function to add more funding plans dynamically
|
|
function addFundingPlan(showRemove = true) {
|
|
const container = document.getElementById('fundingPlansContainer');
|
|
const newPlan = document.createElement('div');
|
|
newPlan.classList.add('plan-section');
|
|
newPlan.innerHTML = `
|
|
<div class="removableHeader">
|
|
<h3>Plan</h3>
|
|
<button type="button" class="ghost remove-btn">Remove</button>
|
|
</div>
|
|
|
|
<label for="planGuid" class="required">GUID</label>
|
|
<input type="text" class="planGuid" maxlength="32" pattern="^[a-z0-9\-]+$" required placeholder="mybank, paypal">
|
|
|
|
<label for="planStatus" class="required">Status</label>
|
|
<select class="planStatus" required>
|
|
<option value="">Select Status</option>
|
|
<option value="active">Active</option>
|
|
<option value="inactive">Inactive</option>
|
|
</select>
|
|
|
|
<label for="planName" class="required">Name</label>
|
|
<input type="text" class="planName" required placeholder="Starter support plan, Infra hosting, Monthly funding plan">
|
|
|
|
<label for="planDescription">Description</label>
|
|
<textarea class="planDescription" placeholder="Additional description or instructions for the funding plan"></textarea>
|
|
|
|
<label for="planAmount" class="required">Amount</label>
|
|
<input type="number" class="planAmount" min="0" step="0.01" required placeholder="0">
|
|
|
|
<label for="planCurrency" class="required">Currency</label>
|
|
<input type="text" class="planCurrency" pattern="^[A-Z]{3}$" maxlength="3" required placeholder="USD">
|
|
|
|
<label for="planFrequency" class="required">Frequency</label>
|
|
<select class="planFrequency" required>
|
|
<option value="">Select Frequency</option>
|
|
<option value="one-time">One-time</option>
|
|
<option value="weekly">Weekly</option>
|
|
<option value="fortnightly">Fortnightly</option>
|
|
<option value="monthly">Monthly</option>
|
|
<option value="yearly">Yearly</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
|
|
<label for="planChannels" class="required">Channel GUIDs (comma-separated)</label>
|
|
<input type="text" class="planChannels" required>
|
|
`;
|
|
|
|
const removeBtn = newPlan.querySelector(".remove-btn");
|
|
if (showRemove) {
|
|
// Add event listener to remove this plan
|
|
removeBtn.addEventListener("click", function () {
|
|
container.removeChild(newPlan);
|
|
});
|
|
} else {
|
|
removeBtn.style.display = 'none';
|
|
}
|
|
|
|
container.appendChild(newPlan);
|
|
}
|
|
|
|
// Function to add more funding history dynamically
|
|
function addFundingHistory() {
|
|
const container = document.getElementById('fundingHistoryContainer');
|
|
const newHistory = document.createElement('div');
|
|
newHistory.classList.add('history-section');
|
|
newHistory.innerHTML = `
|
|
<div class="removableHeader">
|
|
<h3>History</h3>
|
|
<button type="button" class="ghost remove-btn">Remove</button>
|
|
</div>
|
|
|
|
<label for="historyYear" class="required">Year</label>
|
|
<input type="number" class="historyYear" min="1900" max="2100" required placeholder="2024">
|
|
|
|
<label for="historyIncome">Income (optional)</label>
|
|
<input type="number" class="historyIncome" min="0" step="0.01" placeholder="0">
|
|
|
|
<label for="historyExpenses">Expenses (optional)</label>
|
|
<input type="number" class="historyExpenses" min="0" step="0.01" placeholder="0">
|
|
|
|
<label for="historyTaxes">Taxes (optional)</label>
|
|
<input type="number" class="historyTaxes" min="0" step="0.01" placeholder="0">
|
|
|
|
<label for="historyCurrency" class="required">Currency</label>
|
|
<input type="text" class="historyCurrency" pattern="^[A-Z]{3}$" maxlength="3" required placeholder="USD">
|
|
|
|
<label for="historyDescription">Description (optional)</label>
|
|
<textarea class="historyDescription" maxlength="500"></textarea>
|
|
`;
|
|
|
|
// Add event listener to remove this history
|
|
const removeBtn = newHistory.querySelector(".remove-btn");
|
|
removeBtn.addEventListener("click", function () {
|
|
container.removeChild(newHistory);
|
|
});
|
|
|
|
container.appendChild(newHistory);
|
|
}
|
|
|
|
// Function to generate the JSON when the form is submitted
|
|
document.getElementById('fundingForm').addEventListener('submit', function(event) {
|
|
event.preventDefault();
|
|
const channelGUIDs = [...document.querySelectorAll('.channel-section')].map(channel => channel.querySelector('.fundingChannelGuid').value);
|
|
// Validate plan channels
|
|
const planSections = [...document.querySelectorAll('.plan-section')];
|
|
for (const plan of planSections) {
|
|
const planChannels = plan.querySelector('.planChannels').value.split(',').map(guid => guid.trim());
|
|
for (const guid of planChannels) {
|
|
if (!channelGUIDs.includes(guid)) {
|
|
alert(`Error: Channel GUID "${guid}" in plan "${plan.querySelector('.planGuid').value}" does not match any defined channel GUIDs.`);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
const output = {
|
|
version: "v1.0.0",
|
|
entity: {
|
|
type: document.getElementById('entityType').value,
|
|
role: document.getElementById('entityRole').value,
|
|
name: document.getElementById('entityName').value,
|
|
email: document.getElementById('entityEmail').value,
|
|
phone: document.getElementById('entityPhone').value || undefined,
|
|
description: document.getElementById('entityDescription').value,
|
|
webpageUrl: {
|
|
url: document.getElementById('entityWebpageUrl').value,
|
|
wellKnown: document.getElementById('entityWebpageWellKnown').value || undefined
|
|
}
|
|
},
|
|
projects: [...document.querySelectorAll('.project-section')].length > 0 ? [...document.querySelectorAll('.project-section')].map(project => ({
|
|
guid: project.querySelector('.projectGuid').value,
|
|
name: project.querySelector('.projectName').value,
|
|
description: project.querySelector('.projectDescription').value,
|
|
webpageUrl: {
|
|
url: project.querySelector('.projectWebpageUrl').value,
|
|
wellKnown: project.querySelector('.projectWebpageWellKnown').value || undefined
|
|
},
|
|
repositoryUrl: {
|
|
url: project.querySelector('.projectRepositoryUrl').value,
|
|
wellKnown: project.querySelector('.projectRepositoryWellKnown').value || undefined
|
|
},
|
|
licenses: project.querySelector('.projectLicenses').value.split(',').map(license => license.trim()),
|
|
tags: project.querySelector('.projectTags').value.split(',').map(tag => tag.trim()),
|
|
})) : [],
|
|
funding: {
|
|
channels: [...document.querySelectorAll('.channel-section')].map(channel => ({
|
|
guid: channel.querySelector('.fundingChannelGuid').value,
|
|
type: channel.querySelector('.fundingChannelType').value,
|
|
address: channel.querySelector('.fundingChannelAddress').value || undefined,
|
|
description: channel.querySelector('.fundingChannelDescription').value || undefined,
|
|
})),
|
|
plans: [...document.querySelectorAll('.plan-section')].map(plan => ({
|
|
guid: plan.querySelector('.planGuid').value,
|
|
status: plan.querySelector('.planStatus').value,
|
|
name: plan.querySelector('.planName').value,
|
|
description: plan.querySelector('.planDescription').value || undefined,
|
|
amount: parseFloat(plan.querySelector('.planAmount').value),
|
|
currency: plan.querySelector('.planCurrency').value,
|
|
frequency: plan.querySelector('.planFrequency').value,
|
|
channels: plan.querySelector('.planChannels').value.split(',').map(guid => guid.trim()),
|
|
})),
|
|
history: [...document.querySelectorAll('.history-section')].length > 0 ? [...document.querySelectorAll('.history-section')].map(history => ({
|
|
year: parseInt(history.querySelector('.historyYear').value),
|
|
income: parseFloat(history.querySelector('.historyIncome').value) || undefined,
|
|
expenses: parseFloat(history.querySelector('.historyExpenses').value) || undefined,
|
|
taxes: parseFloat(history.querySelector('.historyTaxes').value) || undefined,
|
|
currency: history.querySelector('.historyCurrency').value,
|
|
description: history.querySelector('.historyDescription').value || undefined,
|
|
})): [],
|
|
}
|
|
};
|
|
|
|
document.getElementById('output').textContent = JSON.stringify(output, null, 2);
|
|
|
|
window.scrollTo(0, 0);
|
|
});
|
|
|
|
// Function to copy JSON output to clipboard
|
|
document.getElementById('copyButton').addEventListener('click', function() {
|
|
const outputText = document.getElementById('output').textContent;
|
|
navigator.clipboard.writeText(outputText)
|
|
.then(() => {
|
|
alert('funding.json copied to clipboard');
|
|
})
|
|
.catch(err => {
|
|
alert('Error copying to clipboard: ', err);
|
|
});
|
|
});
|
|
|
|
window.addEventListener('load', function() {
|
|
addFundingChannel(false);
|
|
addFundingPlan(false);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|