5 changed files with 239 additions and 0 deletions
@ -0,0 +1,221 @@ |
|||||
|
{% 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 %} |
Loading…
Reference in new issue