You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
7.5 KiB

{% extends "/Bases/StandardWebPage.html.twig" %}
{% block content %}
<script src="/Static/JS/ThirdParty/abcjs-basic.js"></script>
<script>
let partCounter = 0; // Keeps track of the number of parts
let barCounters = {}; // Keeps track of the number of bars in each part
function generateABC() {
let builder = "";
// Add the tune metadata
builder += "X: 1\n"; // Default index
builder += "T: " + document.getElementById("TuneTitle").value + "\n"; // Title
builder += "S: " + document.getElementById("TuneCopyright").value + "\n"; // Copyright
builder += "M: 4/4\n"; // Default meter
builder += "K: C\n"; // Default key
// Iterate through all parts
for (let partId = 1; partId <= partCounter; partId++) {
if (document.getElementById(`Part_${partId}`)) {
builder += `P: Part ${partId}\n`; // Part header
// Iterate through bars in the part
for (let barId = 1; barId <= barCounters[partId]; barId++) {
const barInput = document.querySelector(`#Part_${partId}_Bar_${barId} .bar-input`);
if (barInput && barInput.value.trim() !== "") {
builder += barInput.value.trim() + " | "; // Append bar content
}
}
// Add a line break after each part
builder += "\n";
}
}
renderABC(builder); // Render the ABC notation
}
function renderABC(payload)
{
document.getElementById('ABCReadout').innerHTML = payload;
const tunes = window.ABCJS.renderAbc(
'NotationContainer',
payload,
{
add_classes: true,
format: {
gchordfont: "Atkinson Hyperlegible",
annotationfont: "Atkinson Hyperlegible",
headerfont: "Atkinson Hyperlegible",
infofont: "Atkinson Hyperlegible",
repeatfont: "Atkinson Hyperlegible",
tempofont: "Atkinson Hyperlegible",
titlefont: "Atkinson Hyperlegible",
voicefont: "Atkinson Hyperlegible",
wordsfont: "Atkinson Hyperlegible",
},
}
);
}
function createNewPart() {
const container = document.getElementById('PartsWrapper');
// Increment the part counter
partCounter++;
barCounters[partCounter] = 8; // Start with 4 bars
// Create a new part container
const newPartDiv = document.createElement('div');
newPartDiv.id = `Part_${partCounter}`;
newPartDiv.classList.add('part');
// Add the title and initial bars to the part
newPartDiv.innerHTML = `
<h3>Part ${partCounter}</h3>
<button class="delete-part-btn" onclick="deletePart(${partCounter})">{{ "Delete Part"|translate }}</button>
<button class="add-bar-btn" onclick="addBar(${partCounter})">{{ "Add Bar"|translate }}</button>
<div class="bars-wrapper" id="BarsWrapper_${partCounter}">
${generateBarsHTML(partCounter, 8)}
</div>
`;
container.appendChild(newPartDiv);
}
function addBar(partId) {
const barsWrapper = document.getElementById(`BarsWrapper_${partId}`);
barCounters[partId]++;
const barDiv = document.createElement('div');
barDiv.classList.add('bar');
barDiv.id = `Part_${partId}_Bar_${barCounters[partId]}`;
barDiv.innerHTML = `
<textarea class="bar-input" placeholder="{{ "Bar"|translate }} ${barCounters[partId]}"></textarea>
<button class="bar-btn remove-bar" onclick="removeBar(${partId}, ${barCounters[partId]})">-</button>
`;
barsWrapper.appendChild(barDiv);
}
function removeBar(partId, barId) {
const barDiv = document.getElementById(`Part_${partId}_Bar_${barId}`);
if (barDiv) {
barDiv.remove();
}
}
function deletePart(partId) {
const partDiv = document.getElementById(`Part_${partId}`);
if (partDiv) {
delete barCounters[partId]; // Clean up the bar counters
partDiv.remove();
}
}
function generateBarsHTML(partId, numBars) {
let barsHTML = '';
for (let i = 1; i <= numBars; i++) {
barsHTML += `
<div class="bar" id="Part_${partId}_Bar_${i}">
<input type="text" class="bar-input" placeholder="{{ "Bar"|translate }} ${i}" onchange=generateABC()></textarea>
<button class="bar-btn remove-bar" onclick="removeBar(${partId}, ${i})">-</button>
</div>
`;
}
return barsHTML;
}
</script>
<style>
.part {
border: 1px solid #ddd;
margin: 1rem 0;
padding: 1rem;
border-radius: 5px;
background: #f9f9f9;
}
.bars-wrapper {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-top: 1rem;
}
.bar {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.bar textarea {
width: 8rem;
height: 3rem;
resize: none;
}
.bar-btn {
padding: 0.25rem 0.5rem;
font-size: 0.9rem;
border: none;
border-radius: 3px;
cursor: pointer;
background-color: #ff4d4d;
color: white;
}
.bar-btn:hover {
background-color: #d93636;
}
.delete-part-btn,
.add-bar-btn {
background-color: #007bff;
color: white;
border: none;
border-radius: 3px;
padding: 0.5rem 1rem;
cursor: pointer;
margin: 0.5rem 0;
}
.delete-part-btn:hover,
.add-bar-btn:hover {
background-color: #0056b3;
}
</style>
<div class="InnerContent">
<h1>{{ "Tune Creator"|translate }}</h1>
<div class="container">
<div class="left-main">
<div>
<label for="TuneTitle">{{ "Tune Title"|translate }}</label><br>
<input id="TuneTitle" name="TuneTitle" type="text" onchange="generateABC()">
<br><br>
<label for="TuneCopyright">{{ "Copyright"|translate }}</label><br>
<input id="TuneCopyright" name="TuneCopyright" type="text" onchange="generateABC()">
<div>
<button onclick="createNewPart()">{{ "Create New Part"|translate }}</button>
<div id="PartsWrapper"></div>
</div>
</div>
</div>
<div class="right">
<textarea id="ABCReadout" style="width: 100%; height: 25rem;" readonly></textarea>
</div>
</div>
<div id="NotationContainer"></div>
</div>
{% endblock %}