From fad1bbb875e5f043ec4a4a793cc2d19a2c8574a4 Mon Sep 17 00:00:00 2001 From: themancalledjakob Date: Sat, 7 Oct 2023 18:01:00 +0200 Subject: [PATCH] nested props --- bin/web/js/audio.js | 52 +++++++++++++++++------------ bin/web/js/record.js | 26 ++++++++++----- bin/web/js/theatre-play.js | 3 +- bin/web/js/utils.js | 68 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 32 deletions(-) diff --git a/bin/web/js/audio.js b/bin/web/js/audio.js index 8d12f44..60b332a 100644 --- a/bin/web/js/audio.js +++ b/bin/web/js/audio.js @@ -1,6 +1,10 @@ import { mapValue, mix, + toCssClass, + flattenObject, + deFlattenObject, + clone, } from './utils.js'; window.mapValue = mapValue; @@ -100,28 +104,28 @@ const Audio = function(tp, record) { console.log('Audio::addAudioOptions::error',`cannot find panelPropTitle "${propTitle}"`); return; } - if (tp.getPanel().querySelector(`.audioOptions${propTitle}`) !== null) { + if (tp.getPanel().querySelector(toCssClass(`audioOptions${propTitle}`, '.')) !== null) { //console.log('Audio::addAudioOptions::error',`audioOptions already exist for "${propTitle}"`); return; } - const container = tp.getPanelPropContainer(panelPropTitle); + const container = panelPropTitle.parentNode.parentNode; const mappingOptions = mapping[layer.id()][propTitle]; const panel = tp.getPanel(); const audioOptions = document.createElement('div'); audioOptions.classList.add('audioOptions'); audioOptions.classList.add('audioOptionsTypeDefault'); - audioOptions.classList.add(`audioOptions${propTitle}`); + audioOptions.classList.add(toCssClass(`audioOptions${propTitle}`)); audioOptions.style.position = 'relative'; audioOptions.style.width = '100%'; audioOptions.style.background = 'rgba(0,255,255,0.2)'; audioOptions.style.order = parseInt(container.style.order) + 1; const updateMappingOptions = () => { - mappingOptions.min_out = parseFloat(panel.querySelector(`#audio_min${propTitle}`).value); - mappingOptions.max_out = parseFloat(panel.querySelector(`#audio_max${propTitle}`).value); + mappingOptions.min_out = parseFloat(panel.querySelector(toCssClass(`audio_min${propTitle}`,'#')).value); + mappingOptions.max_out = parseFloat(panel.querySelector(toCssClass(`audio_max${propTitle}`,'#')).value); mappingOptions.sync = - panel.querySelector(`input[name="audio_sync${propTitle}"]:checked`).value; - const s = panel.querySelector(`#audio_smoothing${propTitle}`).value; + panel.querySelector(`input[name="${toCssClass('audio_sync' + propTitle)}"]:checked`).value; + const s = panel.querySelector(toCssClass(`audio_smoothing${propTitle}`,'#')).value; mappingOptions.smoothing = parseFloat(s); }; @@ -132,24 +136,24 @@ const Audio = function(tp, record) { min_inputDom_label.innerHTML = 'audio_min'; const min_inputDom = document.createElement('input'); min_inputDom.type = 'number'; - min_inputDom.name = `audio_min${propTitle}`; - min_inputDom.id = `audio_min${propTitle}`; + min_inputDom.name = toCssClass(`audio_min${propTitle}`); + min_inputDom.id = toCssClass(`audio_min${propTitle}`); min_inputDom.value = `${mappingOptions.min_out}`; const max_inputDom_label = document.createElement('label'); max_inputDom_label.for = 'audio_max'; max_inputDom_label.innerHTML = 'audio_max'; const max_inputDom = document.createElement('input'); max_inputDom.type = 'number'; - max_inputDom.name = `audio_max${propTitle}`; - max_inputDom.id = `audio_max${propTitle}`; + max_inputDom.name = toCssClass(`audio_max${propTitle}`); + max_inputDom.id = toCssClass(`audio_max${propTitle}`); max_inputDom.value = `${mappingOptions.max_out}`; const smoothing_inputDom_label = document.createElement('label'); smoothing_inputDom_label.for = 'audio_smoothing'; smoothing_inputDom_label.innerHTML = 'audio_smoothing'; const smoothing_inputDom = document.createElement('input'); smoothing_inputDom.type = 'number'; - smoothing_inputDom.name = `audio_smoothing${propTitle}`; - smoothing_inputDom.id = `audio_smoothing${propTitle}`; + smoothing_inputDom.name = toCssClass(`audio_smoothing${propTitle}`); + smoothing_inputDom.id = toCssClass(`audio_smoothing${propTitle}`); smoothing_inputDom.value = mappingOptions.smoothing; smoothing_inputDom.min = 0; smoothing_inputDom.max = 1; @@ -173,8 +177,8 @@ const Audio = function(tp, record) { sync_inputDom_label.innerHTML = o; const sync_inputDom = document.createElement('input'); sync_inputDom.type = 'radio'; - sync_inputDom.name = `audio_sync${propTitle}`; - sync_inputDom.id = `audio_sync${propTitle}${o}`; + sync_inputDom.name = toCssClass(`audio_sync${propTitle}`); + sync_inputDom.id = toCssClass(`audio_sync${propTitle}${o}`); sync_inputDom.value = o; // default select first option if (o === mappingOptions.sync) { @@ -257,11 +261,11 @@ const Audio = function(tp, record) { // only selected layers have options // otherwise the ui is not there if (layer.isSelected()) { - const audioOptions = panel.querySelector(`.audioOptions${propTitle}`); + const audioOptions = panel.querySelector(toCssClass(`audioOptions${propTitle}`,'.')); if (audioOptions !== null) { audioOptions.remove(); } - const audioButton = panel.querySelector(`.audioButton${propTitle}`); + const audioButton = panel.querySelector(toCssClass(`audioButton${propTitle}`,'.')); if (audioButton !== null) { audioButton.classList.remove('active'); } @@ -273,7 +277,8 @@ const Audio = function(tp, record) { const panel = tp.getPanel(); const panelPropTitle = tp.getPanelPropTitle(propTitle); if (panelPropTitle !== null) { - const container = tp.getPanelPropContainer(panelPropTitle); + //const container = tp.getPanelPropContainer(panelPropTitle); + const container = panelPropTitle.parentNode.parentNode; if (container === null) { console.log("Audio::addAudioButton", @@ -285,7 +290,7 @@ const Audio = function(tp, record) { } else { const button = document.createElement('div'); button.classList.add('audioButton'); - button.classList.add(`audioButton${propTitle}`); + button.classList.add(toCssClass(`audioButton${propTitle}`)); button.innerHTML = `audio`; container.append(button); button.addEventListener('click', () => { @@ -312,7 +317,9 @@ const Audio = function(tp, record) { }; const injectPanel = (layer) => { - const props = Object.keys(layer.theatreObject.value); + const flatValues = clone(layer.theatreObject.value); + flattenObject(flatValues, ['color']); + const props = Object.keys(flatValues); props.forEach((propTitle) => { if (config.audio.ignoreProps.indexOf(propTitle) < 0) { let isActive = false; @@ -604,7 +611,6 @@ const Audio = function(tp, record) { default: break; } - if (m.sync === 'volume') {} }); } }); @@ -626,13 +632,15 @@ const Audio = function(tp, record) { }; }); Object.keys(values).forEach((layerID) => { + deFlattenObject(values[layerID]); record.liveUpdater.immediateUpdate(getLayer(layerID), values[layerID]); }); } } else { propsToSet.forEach((p) => { const title = tp - .getPanelPropContainer(p.title); + .getPanelPropTitle(p.title) + .parentNode.parentNode; if (title !== null) { const inputElement = title diff --git a/bin/web/js/record.js b/bin/web/js/record.js index 1fad8b6..dc7511c 100644 --- a/bin/web/js/record.js +++ b/bin/web/js/record.js @@ -1,6 +1,9 @@ import { clone, sequencialPromises, + toCssClass, + flattenObject, + deFlattenObject, } from './utils.js'; const LiveBuffer = function() { @@ -150,7 +153,8 @@ const Record = function(tp) { // handle UI only if layer is selected if (getLayer(layerID).isSelected()) { const button = tp - .getPanelPropContainer(propTitle) + .getPanelPropTitle(propTitle) + .parentNode.parentNode .querySelector('.recordButton'); if (button !== null) { button.classList.add('active'); @@ -171,7 +175,8 @@ const Record = function(tp) { // handle UI only if layer is selected if (getLayer(layerID).isSelected()) { const button = tp - .getPanelPropContainer(propTitle) + .getPanelPropTitle(propTitle) + .parentNode.parentNode .querySelector('.recordButton'); if (button !== null) { button.classList.remove('active'); @@ -188,7 +193,7 @@ const Record = function(tp) { const panel = tp.getPanel(); const panelPropTitle = tp.getPanelPropTitle(propTitle); if (panelPropTitle !== null) { - const container = tp.getPanelPropContainer(panelPropTitle); + const container = panelPropTitle.parentNode.parentNode; if (container === null) { console.log("Record::addRecordButton", `impossible! cannot find panelPropContainer for ${propTitle}`); @@ -199,7 +204,7 @@ const Record = function(tp) { } else { const button = document.createElement('div'); button.classList.add('recordButton'); - button.classList.add(`recordButton${propTitle}`); + button.classList.add(toCssClass(`recordButton${propTitle}`)); button.innerHTML = `record`; container.append(button); button.addEventListener('click', () => { @@ -238,7 +243,7 @@ const Record = function(tp) { const panel = tp.getPanel(); const panelPropTitle = tp.getPanelPropTitle(propTitle); if (panelPropTitle !== null) { - const container = tp.getPanelPropContainer(panelPropTitle); + const container = panelPropTitle.parentNode.parentNode; if (container === null) { console.log("Record::cloneInput", `impossible! cannot find panelPropContainer for ${propTitle}`); @@ -268,7 +273,7 @@ const Record = function(tp) { const panel = tp.getPanel(); const panelPropTitle = tp.getPanelPropTitle(propTitle); if (panelPropTitle !== null) { - const container = tp.getPanelPropContainer(panelPropTitle); + const container = panelPropTitle.parentNode.parentNode; if (container === null) { console.log("Record::uncloneInput", `impossible! cannot find panelPropContainer for ${propTitle}`); @@ -295,7 +300,9 @@ const Record = function(tp) { }; const injectPanel = (layer) => { - const props = Object.keys(layer.theatreObject.value); + const flatValues = clone(layer.theatreObject.value); + flattenObject(flatValues, ['color']); + const props = Object.keys(flatValues); props.forEach((propTitle) => { if (config.record.ignoreProps.indexOf(propTitle) < 0) { addRecordButton(layer, propTitle); @@ -328,7 +335,8 @@ const Record = function(tp) { [propTitle]: value, }; buffy.addValues(layerID, recording, position, lastPosition); - const merged = buffy.getValues(layerID, position); + const merged = clone(buffy.getValues(layerID, position)); + deFlattenObject(merged); liveUpdater.immediateUpdate(layer, merged); lastPosition = position; }); @@ -366,7 +374,7 @@ const Record = function(tp) { // and should be the layer anyways uncloneInput(layerID, propTitle); keyframes.push({ - path: [propTitle], + path: propTitle.split('.'), keyframes: hot[layerID][propTitle].recording, }); }); diff --git a/bin/web/js/theatre-play.js b/bin/web/js/theatre-play.js index 09e268b..4168ff1 100644 --- a/bin/web/js/theatre-play.js +++ b/bin/web/js/theatre-play.js @@ -167,7 +167,8 @@ const TheatrePlay = function(autoInit = false) { window.removeEventListener('sequenceEvent', finishedSequencedEvent); resolve(true); } else { - console.log('TheatrePlayu::setSequenced', 'ignored event', e, e.detail); + // pretty verbose + //console.log('TheatrePlayu::setSequenced', 'ignored event', e, e.detail); } } }; diff --git a/bin/web/js/utils.js b/bin/web/js/utils.js index 9b69b78..f7d04a6 100644 --- a/bin/web/js/utils.js +++ b/bin/web/js/utils.js @@ -414,6 +414,70 @@ const sequencialPromises = async (iterable, callback = false) => { } }; +// NOTE: this is not perfect, +// but good enough for our use case +// theoretically we would have to get +// rid of all special characters +const toCssClass = (text, prefix = '') => { + return prefix + 'vt_' + text + .replaceAll('.','-dot-') + .replaceAll('#','-hash-') + ; +}; + +const renameProperty = (o, old_key, new_key) => { + Object.defineProperty(o, new_key, + Object.getOwnPropertyDescriptor(o, old_key)); + delete o[old_key]; +}; + +const flattenObject = (o, ignoreKeys = [], pathSymbol = '.') => { + if (typeof o !== 'object') { + return o; + } + let doItAgain = false; + Object.keys(o).forEach((k) => { + if (typeof o[k] === 'object' && + ignoreKeys.indexOf(k) < 0) { + doItAgain = true; + Object.keys(o[k]).forEach((sk) => { + const nk = `${k}${pathSymbol}${sk}`; + o[nk] = o[k][sk]; + }); + delete o[k]; + } + }); + if (doItAgain) { + flattenObject(o, ignoreKeys, pathSymbol); + } +}; + +const deFlattenObject = (o, ignoreKeys = [], pathSymbol = '.') => { + Object.keys(o).forEach((k) => { + if (ignoreKeys.indexOf(k) < 0) { + const ks = k.split(pathSymbol); + if (ks.length > 1) { + let sos = o[k]; + for (let i = ks.length - 1; i > 0; i--) { + sos = {[ks[i]]: sos}; + } + o[ks[0]] = sos; + delete o[k]; + } + } + }); +}; + +///////////////////////////////////// +// you can test these functions in +// the browser like so: +/* + import("./web/js/utils.js").then((module) => { + const toast = module.toCssClass('lol.lol'); + console.log(toast); + }); +*/ +// ///////////////////////////////////// export { @@ -435,4 +499,8 @@ export { mapValue, isMobile, sequencialPromises, + toCssClass, + renameProperty, + flattenObject, + deFlattenObject, }