add color with separate rgba
This commit is contained in:
parent
6ed4bd1227
commit
667447b9a2
4 changed files with 198 additions and 39 deletions
|
@ -56,7 +56,42 @@ const Audio = function(tp, record) {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const addAudioMapping = (layer, propTitle, options = new AudioMappingOptions()) => {
|
const getAudioMappingOptions = (layer, propTitle) => {
|
||||||
|
if (propTitle === 'color') {
|
||||||
|
if (config.audio.colorSeparateRGBA) {
|
||||||
|
const r = new AudioMappingOptions();
|
||||||
|
const g = new AudioMappingOptions();
|
||||||
|
const b = new AudioMappingOptions();
|
||||||
|
const a = new AudioMappingOptions();
|
||||||
|
return [{r}, {g}, {b}, {a}];
|
||||||
|
} else {
|
||||||
|
const rgba = new AudioMappingOptions();
|
||||||
|
rgba.min_out = {r: 0, b: 0, g: 0, a: 0};
|
||||||
|
rgba.max_out = {r: 1, b: 1, g: 1, a: 1};
|
||||||
|
return rgba;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const o = new AudioMappingOptions();
|
||||||
|
// TODO: get min_out, max_out from layer.props
|
||||||
|
// check for typeof layer.props[propTitle.split('.')[0]] blabla
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// potentially recursive
|
||||||
|
const addAudioMapping = (layer, propTitle, options = false) => {
|
||||||
|
if (!options) {
|
||||||
|
options = getAudioMappingOptions(layer, propTitle);
|
||||||
|
if (Array.isArray(options)) {
|
||||||
|
let isGood = true;
|
||||||
|
options.forEach((o) => {
|
||||||
|
const subPropKey = Object.keys(o)[0];
|
||||||
|
const subPropTitle = `${propTitle}.${subPropKey}`;
|
||||||
|
isGood = addAudioMapping(layer, subPropTitle, o[subPropKey]) ? isGood : false;
|
||||||
|
});
|
||||||
|
return isGood;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!mapping.hasOwnProperty(layer.id())) {
|
if (!mapping.hasOwnProperty(layer.id())) {
|
||||||
mapping[layer.id()] = {};
|
mapping[layer.id()] = {};
|
||||||
}
|
}
|
||||||
|
@ -85,6 +120,15 @@ const Audio = function(tp, record) {
|
||||||
}
|
}
|
||||||
if (!mapping[layer.id()].hasOwnProperty(propTitle)) {
|
if (!mapping[layer.id()].hasOwnProperty(propTitle)) {
|
||||||
// no propTitle
|
// no propTitle
|
||||||
|
// perhaps color?
|
||||||
|
if (config.audio.separateRGBA && propTitle === 'color') {
|
||||||
|
let isGood = true;
|
||||||
|
isGood = removeAudioMapping(layer, 'color.r');
|
||||||
|
isGood = removeAudioMapping(layer, 'color.g');
|
||||||
|
isGood = removeAudioMapping(layer, 'color.b');
|
||||||
|
isGood = removeAudioMapping(layer, 'color.a');
|
||||||
|
return isGood;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
delete mapping[layer.id()][propTitle];
|
delete mapping[layer.id()][propTitle];
|
||||||
|
@ -103,7 +147,28 @@ const Audio = function(tp, record) {
|
||||||
audioOptions.classList.add(toCssClass(`audioOptions${propTitle}`));
|
audioOptions.classList.add(toCssClass(`audioOptions${propTitle}`));
|
||||||
audioOptions.style.position = 'relative';
|
audioOptions.style.position = 'relative';
|
||||||
audioOptions.style.width = '100%';
|
audioOptions.style.width = '100%';
|
||||||
|
if (propTitle.split('.')[0] === 'color' && propTitle.split('.').length > 1) {
|
||||||
|
switch(propTitle.split('.')[1]) {
|
||||||
|
case 'r': {
|
||||||
|
audioOptions.style.background = 'rgba(255,0,0,0.2)';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'g': {
|
||||||
|
audioOptions.style.background = 'rgba(0,255,0,0.2)';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'b': {
|
||||||
|
audioOptions.style.background = 'rgba(0,0,255,0.2)';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'a': {
|
||||||
|
audioOptions.style.background = 'rgba(255,255,255,0.2)';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
audioOptions.style.background = 'rgba(0,255,255,0.2)';
|
audioOptions.style.background = 'rgba(0,255,255,0.2)';
|
||||||
|
}
|
||||||
audioOptions.style.order = parseInt(container.style.order) + 1;
|
audioOptions.style.order = parseInt(container.style.order) + 1;
|
||||||
|
|
||||||
const updateMappingOptions = () => {
|
const updateMappingOptions = () => {
|
||||||
|
@ -157,7 +222,7 @@ const Audio = function(tp, record) {
|
||||||
sync_titleDom.innerHTML = 'sync with:';
|
sync_titleDom.innerHTML = 'sync with:';
|
||||||
sync_Dom.append(sync_titleDom);
|
sync_Dom.append(sync_titleDom);
|
||||||
|
|
||||||
audio_sync_options.forEach((o, oi) => {
|
audio_sync_options.forEach((o) => {
|
||||||
const sync_inputDom_label = document.createElement('label');
|
const sync_inputDom_label = document.createElement('label');
|
||||||
sync_inputDom_label.for = `audio_sync${o}`;
|
sync_inputDom_label.for = `audio_sync${o}`;
|
||||||
sync_inputDom_label.innerHTML = o;
|
sync_inputDom_label.innerHTML = o;
|
||||||
|
@ -243,11 +308,12 @@ const Audio = function(tp, record) {
|
||||||
}
|
}
|
||||||
const container = panelPropTitle.parentNode.parentNode;
|
const container = panelPropTitle.parentNode.parentNode;
|
||||||
|
|
||||||
if (propTitle === 'color') {
|
if (propTitle === 'color' && config.audio.colorSeparateRGBA) {
|
||||||
createAudioOptions(layer, `${propTitle}.r`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
|
// NOTE: attach reversed, because container.after(audioOptions)
|
||||||
createAudioOptions(layer, `${propTitle}.g`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
|
|
||||||
createAudioOptions(layer, `${propTitle}.b`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
|
|
||||||
createAudioOptions(layer, `${propTitle}.a`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
|
createAudioOptions(layer, `${propTitle}.a`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
|
||||||
|
createAudioOptions(layer, `${propTitle}.b`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
|
||||||
|
createAudioOptions(layer, `${propTitle}.g`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
|
||||||
|
createAudioOptions(layer, `${propTitle}.r`, container).classList.add(toCssClass(`audioOptions${propTitle}`));
|
||||||
} else {
|
} else {
|
||||||
createAudioOptions(layer, propTitle, container);
|
createAudioOptions(layer, propTitle, container);
|
||||||
}
|
}
|
||||||
|
@ -411,7 +477,7 @@ const Audio = function(tp, record) {
|
||||||
|
|
||||||
// Distortion curve for the waveshaper, thanks to Kevin Ennis
|
// Distortion curve for the waveshaper, thanks to Kevin Ennis
|
||||||
// http://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion
|
// http://stackoverflow.com/questions/22312841/waveshaper-node-in-webaudio-how-to-emulate-distortion
|
||||||
function makeDistortionCurve(amount) {
|
const makeDistortionCurve = (amount) => {
|
||||||
let k = typeof amount === "number" ? amount : 50,
|
let k = typeof amount === "number" ? amount : 50,
|
||||||
n_samples = 44100,
|
n_samples = 44100,
|
||||||
curve = new Float32Array(n_samples),
|
curve = new Float32Array(n_samples),
|
||||||
|
@ -490,7 +556,7 @@ const Audio = function(tp, record) {
|
||||||
console.log("getUserMedia not supported on your browser!");
|
console.log("getUserMedia not supported on your browser!");
|
||||||
}
|
}
|
||||||
|
|
||||||
function visualize() {
|
const visualize = () => {
|
||||||
const WIDTH = canvas.width;
|
const WIDTH = canvas.width;
|
||||||
const HEIGHT = canvas.height;
|
const HEIGHT = canvas.height;
|
||||||
|
|
||||||
|
@ -643,18 +709,21 @@ const Audio = function(tp, record) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
Object.keys(values).forEach((layerID) => {
|
Object.keys(values).forEach((layerID) => {
|
||||||
|
window.debugPreValues = clone(values[layerID]);
|
||||||
deFlattenObject(values[layerID]);
|
deFlattenObject(values[layerID]);
|
||||||
|
window.debugValues = clone(values[layerID]);
|
||||||
record.liveUpdater.immediateUpdate(getLayer(layerID), values[layerID]);
|
record.liveUpdater.immediateUpdate(getLayer(layerID), values[layerID]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const position = tp.sheet.sequence.position;
|
||||||
propsToSet.forEach((p) => {
|
propsToSet.forEach((p) => {
|
||||||
const title = tp
|
const title = tp
|
||||||
.getPanelPropTitle(p.title)
|
.getPanelPropTitle(p.title);
|
||||||
.parentNode.parentNode;
|
|
||||||
|
|
||||||
if (title !== null) {
|
if (title !== null) {
|
||||||
const inputElement = title
|
const inputElement = title
|
||||||
|
.parentNode.parentNode
|
||||||
.querySelector('input.recording');
|
.querySelector('input.recording');
|
||||||
|
|
||||||
if (inputElement !== null) {
|
if (inputElement !== null) {
|
||||||
|
@ -662,6 +731,10 @@ const Audio = function(tp, record) {
|
||||||
inputElement.dispatchEvent(new Event('change'));
|
inputElement.dispatchEvent(new Event('change'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
record.addValue(p.id, p.title, p.value, position);
|
||||||
|
if (!config.audio.colorSeparateRGBA || p.title === 'color.a') {
|
||||||
|
record.liveUpdate(p.layer, position);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -683,7 +756,7 @@ const Audio = function(tp, record) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function voiceChange() {
|
const voiceChange = () => {
|
||||||
distortion.oversample = "4x";
|
distortion.oversample = "4x";
|
||||||
biquadFilter.gain.setTargetAtTime(0, audioCtx.currentTime, 0);
|
biquadFilter.gain.setTargetAtTime(0, audioCtx.currentTime, 0);
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,7 @@ const config = {
|
||||||
fftBandsAnalysed: 256 * 8,
|
fftBandsAnalysed: 256 * 8,
|
||||||
fftBandsUsed: 256 * 8 / 2,
|
fftBandsUsed: 256 * 8 / 2,
|
||||||
fftHeight: 256 / 2,
|
fftHeight: 256 / 2,
|
||||||
|
colorSeparateRGBA: true,
|
||||||
},
|
},
|
||||||
record: {
|
record: {
|
||||||
ignoreProps: ['transformOrigin', 'fontFamily', 'text', 'mirror_x', 'mirror_y', 'mirror_xy'],
|
ignoreProps: ['transformOrigin', 'fontFamily', 'text', 'mirror_x', 'mirror_y', 'mirror_xy'],
|
||||||
|
|
|
@ -49,7 +49,7 @@ const LiveBuffer = function() {
|
||||||
subValueBuffer.delete(value_time_s);
|
subValueBuffer.delete(value_time_s);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ const LiveBuffer = function() {
|
||||||
let mergedValues = {};
|
let mergedValues = {};
|
||||||
let didMergeValues = {};
|
let didMergeValues = {};
|
||||||
valueBuffer[id].forEach((value, value_time_s) => {
|
valueBuffer[id].forEach((value, value_time_s) => {
|
||||||
if (value_time_s < time_s) {
|
if (value_time_s <= time_s) {
|
||||||
mergedValues = {...mergedValues, ...value};
|
mergedValues = {...mergedValues, ...value};
|
||||||
} else {
|
} else {
|
||||||
if (Object.keys(didMergeValues).length === 0) {
|
if (Object.keys(didMergeValues).length === 0) {
|
||||||
|
@ -116,7 +116,20 @@ const LiveUpdater = function(tp, buffy) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.immediateUpdate = (layer, values) => {
|
this.immediateUpdate = (layer, values) => {
|
||||||
const v = {...layer.theatreObject.value, ...values};
|
const cv = clone(values);
|
||||||
|
if (cv.hasOwnProperty('color.r')) {
|
||||||
|
cv['color'] = {
|
||||||
|
r: cv['color.r'],
|
||||||
|
g: cv['color.g'],
|
||||||
|
b: cv['color.b'],
|
||||||
|
a: cv['color.a'],
|
||||||
|
};
|
||||||
|
delete cv['color.r'];
|
||||||
|
delete cv['color.g'];
|
||||||
|
delete cv['color.b'];
|
||||||
|
delete cv['color.a'];
|
||||||
|
}
|
||||||
|
const v = {...layer.theatreObject.value, ...cv};
|
||||||
const p = layer.values2cppProps(v);
|
const p = layer.values2cppProps(v);
|
||||||
if (p !== false) {
|
if (p !== false) {
|
||||||
const id = layer.id();
|
const id = layer.id();
|
||||||
|
@ -152,8 +165,13 @@ const Record = function(tp) {
|
||||||
buffy.register(layerID);
|
buffy.register(layerID);
|
||||||
// handle UI only if layer is selected
|
// handle UI only if layer is selected
|
||||||
if (getLayer(layerID).isSelected()) {
|
if (getLayer(layerID).isSelected()) {
|
||||||
|
let cPropTitle = clone(propTitle);
|
||||||
|
// if colors are separate, there is still just one button
|
||||||
|
if (cPropTitle.indexOf('color.') === 0) {
|
||||||
|
cPropTitle = 'color';
|
||||||
|
}
|
||||||
const button = tp
|
const button = tp
|
||||||
.getPanelPropTitle(propTitle)
|
.getPanelPropTitle(cPropTitle)
|
||||||
.parentNode.parentNode
|
.parentNode.parentNode
|
||||||
.querySelector('.recordButton');
|
.querySelector('.recordButton');
|
||||||
if (button !== null) {
|
if (button !== null) {
|
||||||
|
@ -174,8 +192,13 @@ const Record = function(tp) {
|
||||||
}
|
}
|
||||||
// handle UI only if layer is selected
|
// handle UI only if layer is selected
|
||||||
if (getLayer(layerID).isSelected()) {
|
if (getLayer(layerID).isSelected()) {
|
||||||
|
let cPropTitle = clone(propTitle);
|
||||||
|
// if colors are separate, there is still just one button
|
||||||
|
if (cPropTitle.indexOf('color.') === 0) {
|
||||||
|
cPropTitle = 'color';
|
||||||
|
}
|
||||||
const button = tp
|
const button = tp
|
||||||
.getPanelPropTitle(propTitle)
|
.getPanelPropTitle(cPropTitle)
|
||||||
.parentNode.parentNode
|
.parentNode.parentNode
|
||||||
.querySelector('.recordButton');
|
.querySelector('.recordButton');
|
||||||
if (button !== null) {
|
if (button !== null) {
|
||||||
|
@ -310,8 +333,44 @@ const Record = function(tp) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let lastPositions = {};
|
||||||
|
const addValue = (layerID,
|
||||||
|
propTitle,
|
||||||
|
value,
|
||||||
|
position = tp.sheet.sequence.position,
|
||||||
|
lastPosition = buffy.NO_TIME) => {
|
||||||
|
hot[layerID][propTitle].recording.push({
|
||||||
|
position,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
const recording = {
|
||||||
|
[propTitle]: value,
|
||||||
|
};
|
||||||
|
if (!lastPositions.hasOwnProperty(layerID)) {
|
||||||
|
lastPositions[layerID] = {};
|
||||||
|
}
|
||||||
|
if (lastPosition === buffy.NO_TIME) {
|
||||||
|
if (!lastPositions[layerID].hasOwnProperty(propTitle)) {
|
||||||
|
lastPositions[layerID][propTitle] = position;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lastPositions[layerID][propTitle] = lastPosition;
|
||||||
|
}
|
||||||
|
buffy.addValues(layerID, recording, position, lastPositions[layerID][propTitle]);
|
||||||
|
};
|
||||||
|
const getValue = (layerID, position) => {
|
||||||
|
const merged = clone(buffy.getValues(layerID, position));
|
||||||
|
deFlattenObject(merged);
|
||||||
|
return merged;
|
||||||
|
};
|
||||||
|
const liveUpdate = (layer, position = tp.sheet.sequence.position) => {
|
||||||
|
const merged = getValue(layer.id(), position);
|
||||||
|
liveUpdater.immediateUpdate(layer, merged);
|
||||||
|
};
|
||||||
|
|
||||||
const startRecording = () => {
|
const startRecording = () => {
|
||||||
console.log('Record::startRecording');
|
console.log('Record::startRecording');
|
||||||
|
lastPositions = {};
|
||||||
tp.sheet.sequence.pause();
|
tp.sheet.sequence.pause();
|
||||||
const layerKeys = Object.keys(hot);
|
const layerKeys = Object.keys(hot);
|
||||||
layerKeys.forEach((layerID) => {
|
layerKeys.forEach((layerID) => {
|
||||||
|
@ -324,22 +383,14 @@ const Record = function(tp) {
|
||||||
const input_clone = cloneInput(layerID, propTitle);
|
const input_clone = cloneInput(layerID, propTitle);
|
||||||
let lastPosition = buffy.NO_TIME;
|
let lastPosition = buffy.NO_TIME;
|
||||||
if (input_clone !== null) {
|
if (input_clone !== null) {
|
||||||
input_clone.addEventListener('change', (e) => {
|
//input_clone.addEventListener('change', () => {
|
||||||
const position = tp.sheet.sequence.position;
|
//const position = tp.sheet.sequence.position;
|
||||||
const value = parseFloat(input_clone.value);
|
//const value = parseFloat(input_clone.value);
|
||||||
hot[layerID][propTitle].recording.push({
|
//addValue(layerID, propTitle, value, position, lastPosition);
|
||||||
position,
|
//const merged = getValue(layerID, position);
|
||||||
value,
|
//liveUpdater.immediateUpdate(layer, merged);
|
||||||
});
|
//lastPosition = position;
|
||||||
const recording = {
|
//});
|
||||||
[propTitle]: value,
|
|
||||||
};
|
|
||||||
buffy.addValues(layerID, recording, position, lastPosition);
|
|
||||||
const merged = clone(buffy.getValues(layerID, position));
|
|
||||||
deFlattenObject(merged);
|
|
||||||
liveUpdater.immediateUpdate(layer, merged);
|
|
||||||
lastPosition = position;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
console.log('Record::startRecording', `whoops input_clone for ${propTitle} is null`);
|
console.log('Record::startRecording', `whoops input_clone for ${propTitle} is null`);
|
||||||
}
|
}
|
||||||
|
@ -373,12 +424,39 @@ const Record = function(tp) {
|
||||||
// NOTE: layerID is not actually used atm
|
// NOTE: layerID is not actually used atm
|
||||||
// and should be the layer anyways
|
// and should be the layer anyways
|
||||||
uncloneInput(layerID, propTitle);
|
uncloneInput(layerID, propTitle);
|
||||||
|
// special treatment if we have sperate RGBA for color
|
||||||
|
if (propTitle.indexOf('color.') === 0) {
|
||||||
|
if (propTitle === 'color.r') {
|
||||||
|
const recording = [];
|
||||||
|
hot[layerID]['color.r'].recording.forEach((rr, ri) => {
|
||||||
|
if (ri < hot[layerID]['color.r'].recording.length &&
|
||||||
|
ri < hot[layerID]['color.g'].recording.length &&
|
||||||
|
ri < hot[layerID]['color.b'].recording.length &&
|
||||||
|
ri < hot[layerID]['color.a'].recording.length) {
|
||||||
|
const r = clone(rr);
|
||||||
|
r.value = {
|
||||||
|
r: hot[layerID]['color.r'].recording[ri].value,
|
||||||
|
g: hot[layerID]['color.g'].recording[ri].value,
|
||||||
|
b: hot[layerID]['color.b'].recording[ri].value,
|
||||||
|
a: hot[layerID]['color.a'].recording[ri].value,
|
||||||
|
};
|
||||||
|
recording.push(r);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
keyframes.push({
|
||||||
|
path: ['color'],
|
||||||
|
keyframes: recording,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
keyframes.push({
|
keyframes.push({
|
||||||
path: propTitle.split('.'),
|
path: propTitle.split('.'),
|
||||||
keyframes: hot[layerID][propTitle].recording,
|
keyframes: hot[layerID][propTitle].recording,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
//setTimeout(() => {
|
//setTimeout(() => {
|
||||||
|
const kf = clone(keyframes);
|
||||||
promises.push(() => {
|
promises.push(() => {
|
||||||
return new Promise((subResolve) => {
|
return new Promise((subResolve) => {
|
||||||
tp.setKeyframes(layer, keyframes).then(() => {
|
tp.setKeyframes(layer, keyframes).then(() => {
|
||||||
|
@ -404,6 +482,9 @@ const Record = function(tp) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// public
|
// public
|
||||||
|
this.addValue = addValue;
|
||||||
|
this.getValue = getValue;
|
||||||
|
this.liveUpdate = liveUpdate;
|
||||||
this.liveUpdater = liveUpdater;
|
this.liveUpdater = liveUpdater;
|
||||||
this.addRecordButton = addRecordButton;
|
this.addRecordButton = addRecordButton;
|
||||||
this.addHot = addHot;
|
this.addHot = addHot;
|
||||||
|
|
|
@ -347,7 +347,7 @@ function verifyVariableTimeProject(vt_project) {
|
||||||
|
|
||||||
function clone(a) {
|
function clone(a) {
|
||||||
return JSON.parse(JSON.stringify(a));
|
return JSON.parse(JSON.stringify(a));
|
||||||
};
|
}
|
||||||
|
|
||||||
function getParents(elem, until = null) {
|
function getParents(elem, until = null) {
|
||||||
const parents = [];
|
const parents = [];
|
||||||
|
@ -461,7 +461,11 @@ const deFlattenObject = (o, ignoreKeys = [], pathSymbol = '.') => {
|
||||||
for (let i = ks.length - 1; i > 0; i--) {
|
for (let i = ks.length - 1; i > 0; i--) {
|
||||||
sos = {[ks[i]]: sos};
|
sos = {[ks[i]]: sos};
|
||||||
}
|
}
|
||||||
|
if (typeof o[ks[0]] === 'object') {
|
||||||
|
o[ks[0]] = {...o[ks[0]], ...sos};
|
||||||
|
} else {
|
||||||
o[ks[0]] = sos;
|
o[ks[0]] = sos;
|
||||||
|
}
|
||||||
delete o[k];
|
delete o[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue