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
222 lines
7.5 KiB
4 days ago
|
{% 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 %}
|