audioLayer panel injection
dependencies hashes: openFrameworks d78075f4bca6be2a2533c6e51a75cc1f18404501 ofxMsdfgen e14da13d02c4dff04fb69d7923469f606924e6c3 ofxGPUFont d482bb7cbdf6b296fa4ab5abcf73fb5ff8c8b239 ofxVariableLab 0b5f9bdebc1e5550621957e73c040c258ec6317b ofxProfiler a868e34fa1a79189dd4fbdede2938e308535e5e8 theatre 15b2b9543be7a9ed5c8889e04df715f91d479d45
This commit is contained in:
parent
8353bb837c
commit
3b6a57024d
6 changed files with 281 additions and 46 deletions
|
@ -732,3 +732,19 @@ input:disabled {
|
||||||
background: darkgrey;
|
background: darkgrey;
|
||||||
color: lightgrey;
|
color: lightgrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ASYA: most the following code does not work*/
|
||||||
|
.onOffIndicatorWrapper input[type=checkbox] + label::after {
|
||||||
|
content: ' OFF';
|
||||||
|
}
|
||||||
|
|
||||||
|
.onOffIndicatorWrapper input[type=checkbox]:checked + label{
|
||||||
|
color: #1cba94;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onOffIndicatorWrapper input[type=checkbox]:checked + label::after{
|
||||||
|
content: ' ON';
|
||||||
|
}
|
||||||
|
.onOffIndicatorWrapper {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
import {
|
import {
|
||||||
sanitizeTheatreKey,
|
sanitizeTheatreKey,
|
||||||
arraysEqual,
|
arraysEqual,
|
||||||
|
clone,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
|
const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
|
||||||
// private
|
// private
|
||||||
let props = { };
|
let props = { };
|
||||||
if (typeof values === 'string') {
|
if (typeof values === 'string') {
|
||||||
const filename = sanitizeTheatreKey(values);
|
const audioID = sanitizeTheatreKey(values);
|
||||||
values = {};
|
values = {};
|
||||||
values[filename] = false;
|
values[audioID] = false;
|
||||||
}
|
}
|
||||||
if (typeof values === 'object') {
|
if (typeof values === 'object') {
|
||||||
props[Object.keys(values)[0]] = tp.core.types.boolean(false);
|
props[Object.keys(values)[0]] = tp.core.types.boolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onValuesChangeCallbacks = [];
|
const onValuesChangeCallbacks = [];
|
||||||
|
let hierarchyPanelButton = null;
|
||||||
|
let panelFinderTimeout = false;
|
||||||
|
|
||||||
// private functions
|
// private functions
|
||||||
//
|
//
|
||||||
const onValuesChange = (values) => {
|
const onValuesChange = (values) => {
|
||||||
console.log(values);
|
|
||||||
const n_callbacks = onValuesChangeCallbacks.length;
|
const n_callbacks = onValuesChangeCallbacks.length;
|
||||||
const successes = [];
|
const successes = [];
|
||||||
if (n_callbacks > 0) {
|
if (n_callbacks > 0) {
|
||||||
|
@ -38,18 +40,173 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const getHierarchyPanelButton = () => {
|
||||||
const findInjectPanel = () => {
|
if (hierarchyPanelButton === null) {
|
||||||
console.log('AudioLayer::findInjectPanel');
|
hierarchyPanelButton = tp.shadowRoot.querySelector(`.layerMover${this.id()}`);
|
||||||
const sequencePanel = tp.getSequencePanel();
|
}
|
||||||
const sequencePanelLeft = tp.getSequencePanelLeft();
|
return hierarchyPanelButton;
|
||||||
};
|
};
|
||||||
|
|
||||||
const setTime = (filename, start, stop) => {
|
const getPanelPropDiamond = (panelPropTitle) => {
|
||||||
filename = sanitizeTheatreKey(filename);
|
return panelPropTitle.previousSibling;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPanelPropInput = (panelPropTitle) => {
|
||||||
|
return panelPropTitle
|
||||||
|
.parentElement
|
||||||
|
.nextSibling
|
||||||
|
.querySelector('input');
|
||||||
|
};
|
||||||
|
|
||||||
|
const findInjectPanel = () => {
|
||||||
|
let doItAgain = true;
|
||||||
|
|
||||||
|
console.log('AudioLayer::findInjectPanel');
|
||||||
|
const panel = tp.getPanel();
|
||||||
|
|
||||||
|
let panelSuccesses = 0;
|
||||||
|
const audioIDs = Object.keys(this.theatreObject.value);
|
||||||
|
{ // PANEL
|
||||||
|
for (let i = 0; i < audioIDs.length; i++) {
|
||||||
|
const audioID = audioIDs[i];
|
||||||
|
let panelPropTitle = tp.getPanelPropTitle(audioID);
|
||||||
|
const panelPropContainer = tp.getPanelPropContainer(panelPropTitle);
|
||||||
|
if (panelPropTitle === null || panelPropContainer == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1) prevent unwanted interaction
|
||||||
|
|
||||||
|
// remove theatrejs listeners
|
||||||
|
// such as contextmenu
|
||||||
|
// NOTE: this does not work with stop(Immediate)Propagation here
|
||||||
|
const tmp = panelPropTitle.cloneNode(true);
|
||||||
|
panelPropTitle.parentNode.replaceChild(tmp, panelPropTitle);
|
||||||
|
panelPropTitle = tmp;
|
||||||
|
|
||||||
|
// remove diamond
|
||||||
|
const diamond = getPanelPropDiamond(panelPropTitle);
|
||||||
|
if (diamond !== null) {
|
||||||
|
diamond.remove();
|
||||||
|
}
|
||||||
|
// unchangeable boolean
|
||||||
|
const boolean = getPanelPropInput(panelPropTitle);
|
||||||
|
boolean.disabled = true;
|
||||||
|
boolean.parentElement.classList.add('onOffIndicatorWrapper');
|
||||||
|
boolean.classList.add('onOffIndicator');
|
||||||
|
boolean.style.width = 'auto';
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="main_panel_button button delete${audioID}">
|
||||||
|
delete
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
for (let i = 0; i < container.children.length; i++) {
|
||||||
|
panelPropContainer.append(container.children[i]);
|
||||||
|
};
|
||||||
|
panelPropContainer.querySelector(`.button.delete${audioID}`)
|
||||||
|
.addEventListener('click', () => {
|
||||||
|
this.removeFile(audioID);
|
||||||
|
});
|
||||||
|
|
||||||
|
panelSuccesses++;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (panelSuccesses === audioIDs.length) {
|
||||||
|
doItAgain = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doItAgain) {
|
||||||
|
clearTimeout(panelFinderTimeout);
|
||||||
|
panelFinderTimeout = setTimeout(() => {
|
||||||
|
this.findInjectPanel();
|
||||||
|
}, 30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const findInjectSequencePanel = () => {
|
||||||
|
let doItAgain = true;
|
||||||
|
let sequencePanelSuccess = 0;
|
||||||
|
const sequencePanel = tp.getSequencePanel();
|
||||||
|
const sequencePanelLeft = tp.getSequencePanelLeft();
|
||||||
|
if (sequencePanelLeft !== null) { // SEQUENCE PANEL LEFT
|
||||||
|
const propTitles = sequencePanelLeft.querySelectorAll('[data-testid="SequencePanel-PropTitle"]');
|
||||||
|
const audioLayerTitle = (() => {
|
||||||
|
const titles = sequencePanelLeft.querySelectorAll('[data-testid="SequencePanel-Title"]');
|
||||||
|
for (let i = 0; i < titles.length; i++) {
|
||||||
|
if (titles[i].innerHTML === this.id()) {
|
||||||
|
return titles[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})();
|
||||||
|
if (audioLayerTitle !== null) {
|
||||||
|
const audioSection = audioLayerTitle.closest('li');
|
||||||
|
if (audioSection !== null) {
|
||||||
|
const audioPropTitles = audioSection.querySelectorAll('[data-testid="SequencePanel-PropTitle"]');
|
||||||
|
window.audioPropTitles = audioPropTitles;
|
||||||
|
audioPropTitles.forEach((audioPropTitle) => {
|
||||||
|
const diamond = (() => {
|
||||||
|
const potential = audioPropTitle.nextSibling;
|
||||||
|
if (potential !== null
|
||||||
|
&& potential.nodeName.toLowerCase() === 'div'
|
||||||
|
&& potential.querySelectorAll(':scope > [data-pi-key]').length === 3) {
|
||||||
|
return potential;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})();
|
||||||
|
if (diamond !== null) {
|
||||||
|
diamond.style.display = 'none';
|
||||||
|
}
|
||||||
|
if (audioPropTitle.innerHTML.length > config.audio.maxFilenameLength) {
|
||||||
|
const file = audioPropTitle.innerHTML;
|
||||||
|
const m = (config.audio.maxFilenameLength / 2) - 2;
|
||||||
|
audioPropTitle.innerHTML = file.substr(0,m) + '..' + file.substr(file.length - m, m);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sequencePanelSuccess++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sequencePanelLeft !== null) { // SEQUENCE PANEL RIGHT
|
||||||
|
const audioSectionAnchor = sequencePanelLeft
|
||||||
|
.nextSibling
|
||||||
|
.querySelector(`[data-pi-key*="${this.id()}"]`);
|
||||||
|
if (audioSectionAnchor !== null) {
|
||||||
|
const audioSection = audioSectionAnchor.closest('li');
|
||||||
|
if (audioSection !== null) {
|
||||||
|
audioSection.addEventListener('contextmenu', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
});
|
||||||
|
audioSection.querySelectorAll('*').forEach((element) => {
|
||||||
|
element.addEventListener('contextmenu', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
sequencePanelSuccess++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequencePanelSuccess === 2) {
|
||||||
|
doItAgain = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doItAgain) {
|
||||||
|
clearTimeout(panelFinderTimeout);
|
||||||
|
panelFinderTimeout = setTimeout(() => {
|
||||||
|
this.findInjectSequencePanel();
|
||||||
|
}, 30);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setTime = (audioID, start, stop) => {
|
||||||
|
audioID = sanitizeTheatreKey(audioID);
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const keyframes = [{
|
const keyframes = [{
|
||||||
path: [filename],
|
path: [audioID],
|
||||||
keyframes: [{
|
keyframes: [{
|
||||||
position: -1,
|
position: -1,
|
||||||
value: false,
|
value: false,
|
||||||
|
@ -67,11 +224,11 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}];
|
}];
|
||||||
if (tp.getKeyframes(this, [filename]).length > 0) {
|
if (tp.getKeyframes(this, [audioID]).length > 0) {
|
||||||
tp.studio.transaction(({
|
tp.studio.transaction(({
|
||||||
__experimental_deleteKeyframes
|
__experimental_deleteKeyframes
|
||||||
}) => {
|
}) => {
|
||||||
__experimental_deleteKeyframes(this.theatreObject.props[filename]);
|
__experimental_deleteKeyframes(this.theatreObject.props[audioID]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
tp.addKeyframes(this, keyframes).then(() => {
|
tp.addKeyframes(this, keyframes).then(() => {
|
||||||
|
@ -80,6 +237,15 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getValue = (audioID) => {
|
||||||
|
const values = this.theatreObject.value;
|
||||||
|
if (typeof values[audioID] !== 'undefined') {
|
||||||
|
return values[audioID];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.theatreObject = tp.addObject(audioLayerID, this.props, onValuesChange);
|
this.theatreObject = tp.addObject(audioLayerID, this.props, onValuesChange);
|
||||||
|
@ -97,33 +263,21 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const remove = () => {
|
const remove = () => {
|
||||||
|
tp.removeObject(audioLayerID);
|
||||||
};
|
};
|
||||||
|
|
||||||
// public
|
// public
|
||||||
this.props = props;
|
this.props = props;
|
||||||
|
|
||||||
this.findInjectPanel = findInjectPanel;
|
this.findInjectPanel = findInjectPanel;
|
||||||
|
this.findInjectSequencePanel = findInjectSequencePanel;
|
||||||
|
|
||||||
this.setFile = (filename) => {
|
this.addFile = (audioID) => {
|
||||||
filename = sanitizeTheatreKey(filename);
|
|
||||||
const values = {};
|
|
||||||
values[filename] = false;
|
|
||||||
props = {};
|
|
||||||
props[filename] = tp.core.types.boolean(values[filename]);
|
|
||||||
this.props = props;
|
|
||||||
tp.changeObject(audioLayerID, this.props);
|
|
||||||
tp.studio.transaction(({
|
|
||||||
set
|
|
||||||
}) => {
|
|
||||||
set(this.theatreObject.props, values);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
this.addFile = (filename) => {
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
filename = sanitizeTheatreKey(filename);
|
audioID = sanitizeTheatreKey(audioID);
|
||||||
const values = this.theatreObject.value;
|
const values = this.theatreObject.value;
|
||||||
values[filename] = false;
|
values[audioID] = false;
|
||||||
props[filename] = tp.core.types.boolean(values[filename]);
|
props[audioID] = tp.core.types.boolean(values[audioID]);
|
||||||
this.props = props;
|
this.props = props;
|
||||||
|
|
||||||
const setDummy = () => {
|
const setDummy = () => {
|
||||||
|
@ -154,12 +308,12 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
|
||||||
setDummy();
|
setDummy();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
this.removeFile = (filename) => {
|
this.removeFile = (audioID) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
filename = sanitizeTheatreKey(filename);
|
audioID = sanitizeTheatreKey(audioID);
|
||||||
const values = this.theatreObject.value;
|
const values = clone(this.theatreObject.value);
|
||||||
delete values[filename];
|
delete values[audioID];
|
||||||
delete props[filename];
|
delete props[audioID];
|
||||||
this.props = props;
|
this.props = props;
|
||||||
const setDummy = () => {
|
const setDummy = () => {
|
||||||
tp.changeObject(audioLayerID, {
|
tp.changeObject(audioLayerID, {
|
||||||
|
@ -175,16 +329,53 @@ const AudioLayer = function(tp, audioLayerID, values = false, autoInit = true) {
|
||||||
const givenValueKeys = Object.keys(againChangedValues);
|
const givenValueKeys = Object.keys(againChangedValues);
|
||||||
if (arraysEqual(expectedValueKeys, givenValueKeys)) {
|
if (arraysEqual(expectedValueKeys, givenValueKeys)) {
|
||||||
resolve();
|
resolve();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tp.changeObject(audioLayerID, this.props);
|
tp.changeObject(audioLayerID, this.props);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setDummy();
|
setDummy();
|
||||||
|
|
||||||
|
if (!audio.audioPlayer.remove(audioID)) {
|
||||||
|
console.log('AudioLayer::remove', `could not remove ${audioID} from audioPlayer`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
this.setTime = setTime;
|
this.setTime = setTime;
|
||||||
|
|
||||||
|
this.getValue = getValue;
|
||||||
|
|
||||||
|
this.isSelected = () => {
|
||||||
|
const panel = getHierarchyPanelButton();
|
||||||
|
if (panel === null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
const notSelected = panel.querySelector('.not-selected');
|
||||||
|
return !notSelected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.select = () => {
|
||||||
|
const panel = getHierarchyPanelButton();
|
||||||
|
const selectables = panel.querySelector('.not-selected');
|
||||||
|
if (selectables !== null && typeof selectables.dispatchEvent === 'function') {
|
||||||
|
var clickEvent = new MouseEvent("click", {
|
||||||
|
"view": window,
|
||||||
|
"bubbles": true,
|
||||||
|
"cancelable": false
|
||||||
|
});
|
||||||
|
selectables.dispatchEvent(clickEvent);
|
||||||
|
} else {
|
||||||
|
window.debugElement = panel;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.init = init;
|
this.init = init;
|
||||||
|
|
||||||
this.id = () => {
|
this.id = () => {
|
||||||
|
|
|
@ -59,19 +59,32 @@ const AudioPlayer = function() {
|
||||||
startTime
|
startTime
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
this.remove = (audioID) => {
|
||||||
|
const index = audioElements.findIndex((e) => { return e.audioID === audioID; });
|
||||||
|
if (index < 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
audioElements.splice(index, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
this.update = () => {
|
this.update = () => {
|
||||||
audioElements.forEach((audioElement, i) => {
|
audioElements.forEach((audioElement, i) => {
|
||||||
if (tp.isPlaying() && !record.isRecording()) {
|
if (tp.isPlaying() && !record.isRecording()) {
|
||||||
const shouldBePlaying = getAudioLayer().theatreObject.value[audioElement.audioID];
|
const shouldBePlaying = getAudioLayer().getValue([audioElement.audioID]);
|
||||||
if (shouldBePlaying && audioElement.audioDomElement.paused) {
|
if (shouldBePlaying !== null) {
|
||||||
// sequence position is always greater than startTime
|
if (shouldBePlaying && audioElement.audioDomElement.paused) {
|
||||||
// this is true, as it is written
|
// sequence position is always greater than startTime
|
||||||
const diff = tp.sheet.sequence.position - audioElement.startTime;
|
// this is true, as it is written
|
||||||
audioElement.audioDomElement.currentTime = diff;
|
const diff = tp.sheet.sequence.position - audioElement.startTime;
|
||||||
audioElement.audioDomElement.play();
|
audioElement.audioDomElement.currentTime = diff;
|
||||||
} else if (!shouldBePlaying && !audioElement.audioDomElement.paused) {
|
audioElement.audioDomElement.play();
|
||||||
audioElement.audioDomElement.pause();
|
} else if (!shouldBePlaying && !audioElement.audioDomElement.paused) {
|
||||||
audioElement.audioDomElement.currentTime = 0;
|
audioElement.audioDomElement.pause();
|
||||||
|
audioElement.audioDomElement.currentTime = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('AudioPlayer::update',`${audioElement.audioID} does not exist in audioLayer.`);
|
||||||
}
|
}
|
||||||
} else if (!audioElement.audioDomElement.paused) {
|
} else if (!audioElement.audioDomElement.paused) {
|
||||||
audioElement.audioDomElement.pause();
|
audioElement.audioDomElement.pause();
|
||||||
|
|
|
@ -586,6 +586,9 @@ const deleteLayer = (id, saveProject = true) => {
|
||||||
const addAudioLayer = (filename = false) => {
|
const addAudioLayer = (filename = false) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const audioLayerID = (() => {
|
const audioLayerID = (() => {
|
||||||
|
if (audioLayers.length === 0) {
|
||||||
|
return 'audio';
|
||||||
|
}
|
||||||
let index = 0;
|
let index = 0;
|
||||||
for (let i = 0; i < audioLayers.length; i++) {
|
for (let i = 0; i < audioLayers.length; i++) {
|
||||||
if (audioLayers.findIndex((audioLayer) => audioLayer.id() === `audio-${index}`) < 0) {
|
if (audioLayers.findIndex((audioLayer) => audioLayer.id() === `audio-${index}`) < 0) {
|
||||||
|
|
|
@ -284,6 +284,11 @@ const TheatrePlay = function(autoInit = false) {
|
||||||
friendlySequenceNames();
|
friendlySequenceNames();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dirty, should be not in here:
|
||||||
|
if (getAudioLayers().length > 0) {
|
||||||
|
getAudioLayer().findInjectSequencePanel();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//public
|
//public
|
||||||
|
|
|
@ -372,6 +372,12 @@ function getParents(elem, until = null) {
|
||||||
return parents;
|
return parents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeAllEventListeners(elem) {
|
||||||
|
const tmp = elem.cloneNode(true);
|
||||||
|
elem.parentNode.replaceChild(tmp, elem);
|
||||||
|
elem = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
function arraysEqual(a, b, sortingMatters = false) {
|
function arraysEqual(a, b, sortingMatters = false) {
|
||||||
if (a === b) return true;
|
if (a === b) return true;
|
||||||
if (a == null || b == null) return false;
|
if (a == null || b == null) return false;
|
||||||
|
@ -618,6 +624,7 @@ export {
|
||||||
mixObject,
|
mixObject,
|
||||||
clone,
|
clone,
|
||||||
getParents,
|
getParents,
|
||||||
|
removeAllEventListeners,
|
||||||
arraysEqual,
|
arraysEqual,
|
||||||
mapValue,
|
mapValue,
|
||||||
smoothValue,
|
smoothValue,
|
||||||
|
|
Loading…
Reference in a new issue