web-tuner/frontend/dist/sets-editor.js

404 lines
12 KiB
JavaScript

let _seListEl = document.getElementById('sets-editor-list');
let _seDetailEl = document.getElementById('sets-editor-detail');
let _seSelectedIdx = 0;
let _seEditingIdx = -1;
let _seFrets = [];
let _seNameInput = null;
let _seAutoLabel = null;
function _seDbg() {
if (window.dbg) window.dbg.apply(null, ['[sets-editor]'].concat(Array.prototype.slice.call(arguments)));
}
function _seData() {
return window.getScoreSetsData();
}
function _seDensity(set) { return set && set.type === 'density'; }
document.getElementById('btn-back-explorer').addEventListener('click', function() {
_seEditingIdx = -1;
window.setScoreSetsData(_seData());
window.switchToView('shapes');
});
window.initSetsEditor = function() {
_seDbg('init');
var data = _seData();
_seSelectedIdx = data.selected;
_seEditingIdx = -1;
seRenderList();
seRenderDetail();
};
function seSelectSet(i) {
_seDbg('selectSet', i);
_seSelectedIdx = i;
_seEditingIdx = -1;
seRenderList();
seRenderDetail();
}
function seNewSet() {
_seDbg('newSet');
var name = prompt('New score set name:');
if (!name || !name.trim()) return;
var data = _seData();
data.sets.push({ name: name.trim(), type: '', shapes: [] });
_seSelectedIdx = data.sets.length - 1;
_seEditingIdx = -1;
window.persistSets();
seRenderList();
seRenderDetail();
}
function seRenderList() {
_seListEl.innerHTML = '';
var data = _seData();
data.sets.forEach(function(set, i) {
var item = document.createElement('div');
item.className = 'se-set-item' + (i === _seSelectedIdx ? ' selected' : '');
var nm = document.createElement('span');
nm.className = 'se-set-name';
nm.textContent = set.name;
var badges = document.createElement('span');
badges.className = 'se-set-badges';
var ct = document.createElement('span');
ct.className = 'se-badge';
ct.textContent = set.shapes.length;
badges.appendChild(ct);
if (_seDensity(set)) {
var db = document.createElement('span');
db.className = 'se-badge se-badge-density';
db.textContent = 'density';
badges.appendChild(db);
}
item.appendChild(nm);
item.appendChild(badges);
item.setAttribute('onclick', 'seSelectSet(' + i + ')');
_seListEl.appendChild(item);
});
var btn = document.createElement('button');
btn.className = 'btn-small se-btn-new-set';
btn.textContent = 'New Set';
btn.setAttribute('onclick', 'seNewSet()');
_seListEl.appendChild(btn);
}
function seRenameSet() {
_seDbg('rename');
var set = _seData().sets[_seSelectedIdx];
if (!set) return;
var name = prompt('Rename set:', set.name);
if (!name || !name.trim()) return;
set.name = name.trim();
window.persistSets();
seRenderList();
seRenderDetail();
}
function seDeleteSet() {
_seDbg('deleteSet');
var data = _seData();
var set = data.sets[_seSelectedIdx];
if (!set || !confirm('Delete "' + set.name + '"?')) return;
data.sets.splice(_seSelectedIdx, 1);
if (_seSelectedIdx >= data.sets.length) _seSelectedIdx = data.sets.length - 1;
data.selected = _seSelectedIdx;
window.persistSets();
seRenderList();
seRenderDetail();
}
function seMoveShape(i, dir) {
_seDbg('move', i, dir);
var set = _seData().sets[_seSelectedIdx];
if (!set) return;
var j = i + dir;
if (j < 0 || j >= set.shapes.length) return;
var tmp = set.shapes[i];
set.shapes[i] = set.shapes[j];
set.shapes[j] = tmp;
window.persistSets();
seRenderDetail();
}
function seEditShape(i) {
_seDbg('edit', i);
_seEditingIdx = i;
seRenderDetail();
}
function seDupShape(i) {
_seDbg('dup', i);
var set = _seData().sets[_seSelectedIdx];
if (!set) return;
var orig = set.shapes[i];
set.shapes.splice(i + 1, 0, { name: '', frets: orig.frets.slice() });
window.persistSets();
seRenderDetail();
}
function seDelShape(i) {
_seDbg('del', i);
var set = _seData().sets[_seSelectedIdx];
if (!set) return;
set.shapes.splice(i, 1);
window.persistSets();
seRenderDetail();
}
function seAddShape() {
_seDbg('add');
var set = _seData().sets[_seSelectedIdx];
if (!set) return;
set.shapes.push({ name: '', frets: [0, 0, 0, 0, 0, 0] });
_seEditingIdx = set.shapes.length - 1;
window.persistSets();
seRenderDetail();
}
function seRenderDetail() {
_seDetailEl.innerHTML = '';
var data = _seData();
var set = data.sets[_seSelectedIdx];
if (!set) return;
if (_seEditingIdx >= 0) {
seRenderShapeEditor(set);
return;
}
var density = _seDensity(set);
var header = document.createElement('div');
header.className = 'se-detail-header';
var h3 = document.createElement('h3');
h3.textContent = set.name;
header.appendChild(h3);
if (!density) {
var btnR = document.createElement('button');
btnR.className = 'btn-small';
btnR.textContent = 'Rename';
btnR.setAttribute('onclick', 'seRenameSet()');
header.appendChild(btnR);
if (data.sets.length > 1) {
var btnD = document.createElement('button');
btnD.className = 'btn-small se-btn-danger';
btnD.textContent = 'Delete Set';
btnD.setAttribute('onclick', 'seDeleteSet()');
header.appendChild(btnD);
}
}
_seDetailEl.appendChild(header);
var listEl = document.createElement('div');
listEl.className = 'se-shape-list';
set.shapes.forEach(function(shape, i) {
var row = document.createElement('div');
row.className = 'se-shape-row';
var fb = document.createElement('div');
fb.className = 'fretboard alternative-fretboard se-mini-fb';
var fingering = shape.frets.map(function(f) { return f === -1 ? 'x' : String(f); });
var mx = Math.max.apply(null, shape.frets.filter(function(f) { return f >= 0; }).concat([1]));
window.renderSingleFretboard(fb, fingering, Math.max(mx, 4));
row.appendChild(fb);
var info = document.createElement('div');
info.className = 'se-shape-info';
var nameEl = document.createElement('span');
nameEl.className = 'se-shape-name';
if (shape.name) {
nameEl.textContent = shape.name;
} else {
nameEl.classList.add('se-auto-name');
nameEl.textContent = 'detecting...';
_seAutoDetect(shape.frets).then(function(n) { nameEl.textContent = n || '(unknown)'; });
}
info.appendChild(nameEl);
var fretsEl = document.createElement('span');
fretsEl.className = 'se-shape-frets';
fretsEl.textContent = shape.frets.map(function(f) { return f === -1 ? 'x' : f; }).join(' ');
info.appendChild(fretsEl);
row.appendChild(info);
if (!density) {
var acts = document.createElement('div');
acts.className = 'se-shape-actions';
if (i > 0) {
var up = document.createElement('button');
up.className = 'btn-small';
up.textContent = '\u25B2';
up.title = 'Move up';
up.setAttribute('onclick', 'seMoveShape(' + i + ',-1)');
acts.appendChild(up);
}
if (i < set.shapes.length - 1) {
var dn = document.createElement('button');
dn.className = 'btn-small';
dn.textContent = '\u25BC';
dn.title = 'Move down';
dn.setAttribute('onclick', 'seMoveShape(' + i + ',1)');
acts.appendChild(dn);
}
var ed = document.createElement('button');
ed.className = 'btn-small';
ed.textContent = 'edit';
ed.setAttribute('onclick', 'seEditShape(' + i + ')');
acts.appendChild(ed);
var dp = document.createElement('button');
dp.className = 'btn-small';
dp.textContent = 'dup';
dp.title = 'Duplicate';
dp.setAttribute('onclick', 'seDupShape(' + i + ')');
acts.appendChild(dp);
var dl = document.createElement('button');
dl.className = 'btn-small se-btn-danger';
dl.textContent = 'del';
dl.setAttribute('onclick', 'seDelShape(' + i + ')');
acts.appendChild(dl);
row.appendChild(acts);
}
listEl.appendChild(row);
});
_seDetailEl.appendChild(listEl);
if (!density) {
var btnA = document.createElement('button');
btnA.className = 'btn-small se-btn-add-shape';
btnA.textContent = 'Add Shape';
btnA.setAttribute('onclick', 'seAddShape()');
_seDetailEl.appendChild(btnA);
}
}
function seShapeDone() {
_seDbg('done');
var set = _seData().sets[_seSelectedIdx];
if (!set) return;
var shape = set.shapes[_seEditingIdx];
if (!shape) return;
shape.frets = _seFrets.slice();
shape.name = _seNameInput ? _seNameInput.value.trim() : '';
_seEditingIdx = -1;
window.persistSets();
seRenderList();
seRenderDetail();
}
function seFbCell(s, f) {
var cur = _seFrets[s];
if (f === 0) {
_seFrets[s] = (cur === -1) ? 0 : -1;
} else {
_seFrets[s] = (cur === f) ? 0 : f;
}
_seBuildGrid();
_seUpdateAutoName();
}
function _seBuildGrid() {
var wrap = document.getElementById('se-fb-wrap');
if (!wrap) return;
wrap.innerHTML = '';
var nf = 12;
var table = document.createElement('div');
table.className = 'se-fb-grid';
var hdr = document.createElement('div');
hdr.className = 'se-fb-header-row';
var lbl = document.createElement('div');
lbl.className = 'se-fb-label';
hdr.appendChild(lbl);
for (var f = 0; f <= nf; f++) {
var c = document.createElement('div');
c.className = 'se-fb-fret-num';
c.textContent = f === 0 ? 'nut' : f;
hdr.appendChild(c);
}
table.appendChild(hdr);
var labels = ['1 (low)', '2', '3', '4', '5', '6 (high)'];
for (var s = 0; s < 6; s++) {
var row = document.createElement('div');
row.className = 'se-fb-row';
var sl = document.createElement('div');
sl.className = 'se-fb-label';
sl.textContent = labels[s];
row.appendChild(sl);
for (var f = 0; f <= nf; f++) {
var cell = document.createElement('div');
cell.className = 'se-fb-cell';
if (f === 0) cell.classList.add('se-fb-nut');
var cur = _seFrets[s];
if (f === 0) {
if (cur === -1) { cell.classList.add('se-muted'); cell.textContent = 'x'; }
else if (cur === 0) { cell.classList.add('se-open'); cell.textContent = 'o'; }
} else if (cur === f) {
cell.classList.add('se-dot-active');
}
cell.setAttribute('onclick', 'seFbCell(' + s + ',' + f + ')');
row.appendChild(cell);
}
table.appendChild(row);
}
wrap.appendChild(table);
}
function _seUpdateAutoName() {
_seAutoDetect(_seFrets).then(function(n) {
if (_seAutoLabel) _seAutoLabel.textContent = n ? 'Detected: ' + n : '';
});
}
function seRenderShapeEditor(set) {
var shape = set.shapes[_seEditingIdx];
if (!shape) { _seEditingIdx = -1; seRenderDetail(); return; }
_seFrets = shape.frets.slice();
_seDetailEl.innerHTML = '';
var header = document.createElement('div');
header.className = 'se-editor-header';
var btnDone = document.createElement('button');
btnDone.className = 'btn-small btn-apply';
btnDone.textContent = 'Done';
btnDone.setAttribute('onclick', 'seShapeDone()');
header.appendChild(btnDone);
var title = document.createElement('h3');
title.textContent = 'Edit Shape';
header.appendChild(title);
_seDetailEl.appendChild(header);
var nameRow = document.createElement('div');
nameRow.className = 'se-editor-name-row';
var nl = document.createElement('label');
nl.textContent = 'Name';
_seNameInput = document.createElement('input');
_seNameInput.type = 'text';
_seNameInput.className = 'se-editor-name-input';
_seNameInput.placeholder = 'Auto-detect name';
_seNameInput.value = shape.name;
_seAutoLabel = document.createElement('span');
_seAutoLabel.className = 'se-auto-label';
nameRow.appendChild(nl);
nameRow.appendChild(_seNameInput);
nameRow.appendChild(_seAutoLabel);
_seDetailEl.appendChild(nameRow);
var fbWrap = document.createElement('div');
fbWrap.id = 'se-fb-wrap';
fbWrap.className = 'se-clickable-fb';
_seDetailEl.appendChild(fbWrap);
_seBuildGrid();
_seUpdateAutoName();
}
function _seAutoDetect(frets) {
if (!window.go || !window.go.main || !window.go.main.App) return Promise.resolve('');
return window.go.main.App.IdentifyShape(frets).then(function(n) { return n || ''; }).catch(function() { return ''; });
}