Hello, world
This commit is contained in:
parent
fc13029ba2
commit
2c86135000
1 changed files with 448 additions and 0 deletions
448
index.html
Normal file
448
index.html
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
<!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;
|
||||
}
|
||||
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-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;
|
||||
}
|
||||
@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>
|
||||
|
||||
<!-- 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 = `
|
||||
<h3>Project</h3>
|
||||
<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">
|
||||
`;
|
||||
container.appendChild(newProject);
|
||||
}
|
||||
|
||||
// Function to add more funding channels dynamically
|
||||
function addFundingChannel() {
|
||||
const container = document.getElementById('fundingChannelsContainer');
|
||||
const newChannel = document.createElement('div');
|
||||
newChannel.classList.add('channel-section');
|
||||
newChannel.innerHTML = `
|
||||
<h3>Channel</h3>
|
||||
<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="gateway">Gateway</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>
|
||||
`;
|
||||
container.appendChild(newChannel);
|
||||
}
|
||||
|
||||
|
||||
// Function to add more funding plans dynamically
|
||||
function addFundingPlan() {
|
||||
const container = document.getElementById('fundingPlansContainer');
|
||||
const newPlan = document.createElement('div');
|
||||
newPlan.classList.add('plan-section');
|
||||
newPlan.innerHTML = `
|
||||
<h3>Plan</h3>
|
||||
<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>
|
||||
`;
|
||||
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 = `
|
||||
<h3>History</h3>
|
||||
<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>
|
||||
`;
|
||||
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() {
|
||||
console.log("hello");
|
||||
addFundingChannel();
|
||||
addFundingPlan();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue