mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-24 15:02:52 -05:00
Merge pull request #1434 from kchadha/fix-variable-xml
Fix variable xml
This commit is contained in:
commit
6b26e6e68c
3 changed files with 230 additions and 4 deletions
|
@ -269,7 +269,7 @@ class Target extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renames the variable with the given id to newName.
|
* Renames the variable with the given id to newName.
|
||||||
* @param {string} id Id of renamed variable.
|
* @param {string} id Id of variable to rename.
|
||||||
* @param {string} newName New name for the variable.
|
* @param {string} newName New name for the variable.
|
||||||
*/
|
*/
|
||||||
renameVariable (id, newName) {
|
renameVariable (id, newName) {
|
||||||
|
@ -301,7 +301,7 @@ class Target extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the variable with the given id from the dictionary of variables.
|
* Removes the variable with the given id from the dictionary of variables.
|
||||||
* @param {string} id Id of renamed variable.
|
* @param {string} id Id of variable to delete.
|
||||||
*/
|
*/
|
||||||
deleteVariable (id) {
|
deleteVariable (id) {
|
||||||
if (this.variables.hasOwnProperty(id)) {
|
if (this.variables.hasOwnProperty(id)) {
|
||||||
|
@ -313,6 +313,56 @@ class Target extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a clone of the variable with the given id from the dictionary of
|
||||||
|
* this target's variables.
|
||||||
|
* @param {string} id Id of variable to duplicate.
|
||||||
|
* @param {boolean=} optKeepOriginalId Optional flag to keep the original variable ID
|
||||||
|
* for the duplicate variable. This is necessary when cloning a sprite, for example.
|
||||||
|
* @return {?Variable} The duplicated variable, or null if
|
||||||
|
* the original variable was not found.
|
||||||
|
*/
|
||||||
|
duplicateVariable (id, optKeepOriginalId) {
|
||||||
|
if (this.variables.hasOwnProperty(id)) {
|
||||||
|
const originalVariable = this.variables[id];
|
||||||
|
const newVariable = new Variable(
|
||||||
|
optKeepOriginalId ? id : null, // conditionally keep original id or generate a new one
|
||||||
|
originalVariable.name,
|
||||||
|
originalVariable.type,
|
||||||
|
originalVariable.isCloud
|
||||||
|
);
|
||||||
|
newVariable.value = originalVariable.value;
|
||||||
|
return newVariable;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate the dictionary of this target's variables as part of duplicating.
|
||||||
|
* this target or making a clone.
|
||||||
|
* @param {object=} optBlocks Optional block container for the target being duplicated.
|
||||||
|
* If provided, new variables will be generated with new UIDs and any variable references
|
||||||
|
* in this blocks container will be updated to refer to the corresponding new IDs.
|
||||||
|
* @return {object} The duplicated dictionary of variables
|
||||||
|
*/
|
||||||
|
duplicateVariables (optBlocks) {
|
||||||
|
let allVarRefs;
|
||||||
|
if (optBlocks) {
|
||||||
|
allVarRefs = optBlocks.getAllVariableAndListReferences();
|
||||||
|
}
|
||||||
|
return Object.keys(this.variables).reduce((accum, varId) => {
|
||||||
|
const newVariable = this.duplicateVariable(varId, !optBlocks);
|
||||||
|
accum[newVariable.id] = newVariable;
|
||||||
|
if (optBlocks && allVarRefs) {
|
||||||
|
const currVarRefs = allVarRefs[varId];
|
||||||
|
if (currVarRefs) {
|
||||||
|
this.mergeVariables(varId, newVariable.id, currVarRefs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accum;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post/edit sprite info.
|
* Post/edit sprite info.
|
||||||
* @param {object} data An object with sprite info data to set.
|
* @param {object} data An object with sprite info data to set.
|
||||||
|
|
|
@ -1003,7 +1003,7 @@ class RenderedTarget extends Target {
|
||||||
newClone.currentCostume = this.currentCostume;
|
newClone.currentCostume = this.currentCostume;
|
||||||
newClone.rotationStyle = this.rotationStyle;
|
newClone.rotationStyle = this.rotationStyle;
|
||||||
newClone.effects = JSON.parse(JSON.stringify(this.effects));
|
newClone.effects = JSON.parse(JSON.stringify(this.effects));
|
||||||
newClone.variables = JSON.parse(JSON.stringify(this.variables));
|
newClone.variables = this.duplicateVariables();
|
||||||
newClone.initDrawable(StageLayering.SPRITE_LAYER);
|
newClone.initDrawable(StageLayering.SPRITE_LAYER);
|
||||||
newClone.updateAllDrawableProperties();
|
newClone.updateAllDrawableProperties();
|
||||||
// Place behind the current target.
|
// Place behind the current target.
|
||||||
|
@ -1029,7 +1029,7 @@ class RenderedTarget extends Target {
|
||||||
newTarget.currentCostume = this.currentCostume;
|
newTarget.currentCostume = this.currentCostume;
|
||||||
newTarget.rotationStyle = this.rotationStyle;
|
newTarget.rotationStyle = this.rotationStyle;
|
||||||
newTarget.effects = JSON.parse(JSON.stringify(this.effects));
|
newTarget.effects = JSON.parse(JSON.stringify(this.effects));
|
||||||
newTarget.variables = JSON.parse(JSON.stringify(this.variables));
|
newTarget.variables = this.duplicateVariables(newTarget.blocks);
|
||||||
newTarget.updateAllDrawableProperties();
|
newTarget.updateAllDrawableProperties();
|
||||||
newTarget.goBehindOther(this);
|
newTarget.goBehindOther(this);
|
||||||
return newTarget;
|
return newTarget;
|
||||||
|
|
|
@ -148,6 +148,182 @@ test('deleteVariable2', t => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('duplicateVariable creates a new variable with a new ID by default', t => {
|
||||||
|
const target = new Target();
|
||||||
|
target.createVariable('a var ID', 'foo', Variable.SCALAR_TYPE);
|
||||||
|
t.equal(Object.keys(target.variables).length, 1);
|
||||||
|
const originalVariable = target.variables['a var ID'];
|
||||||
|
originalVariable.value = 10;
|
||||||
|
const newVariable = target.duplicateVariable('a var ID');
|
||||||
|
// Duplicating a variable should not add the variable to the current target
|
||||||
|
t.equal(Object.keys(target.variables).length, 1);
|
||||||
|
// Duplicate variable should have a different ID from the original unless specified to keep the original ID.
|
||||||
|
t.notEqual(newVariable.id, 'a var ID');
|
||||||
|
t.type(target.variables[newVariable.id], 'undefined');
|
||||||
|
|
||||||
|
// Duplicate variable should start out with the same value as the original variable
|
||||||
|
t.equal(newVariable.value, originalVariable.value);
|
||||||
|
|
||||||
|
// Modifying one variable should not modify the other
|
||||||
|
newVariable.value = 15;
|
||||||
|
t.notEqual(newVariable.value, originalVariable.value);
|
||||||
|
t.equal(originalVariable.value, 10);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('duplicateVariable creates a new variable with a original ID if specified', t => {
|
||||||
|
const target = new Target();
|
||||||
|
target.createVariable('a var ID', 'foo', Variable.SCALAR_TYPE);
|
||||||
|
t.equal(Object.keys(target.variables).length, 1);
|
||||||
|
const originalVariable = target.variables['a var ID'];
|
||||||
|
originalVariable.value = 10;
|
||||||
|
const newVariable = target.duplicateVariable('a var ID', true);
|
||||||
|
// Duplicating a variable should not add the variable to the current target
|
||||||
|
t.equal(Object.keys(target.variables).length, 1);
|
||||||
|
// Duplicate variable should have the same ID as the original when specified
|
||||||
|
t.equal(newVariable.id, 'a var ID');
|
||||||
|
|
||||||
|
// Duplicate variable should start out with the same value as the original variable
|
||||||
|
t.equal(newVariable.value, originalVariable.value);
|
||||||
|
|
||||||
|
// Modifying one variable should not modify the other
|
||||||
|
newVariable.value = 15;
|
||||||
|
t.notEqual(newVariable.value, originalVariable.value);
|
||||||
|
t.equal(originalVariable.value, 10);
|
||||||
|
// The target should still have the original variable with the original value
|
||||||
|
t.equal(target.variables['a var ID'].value, 10);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('duplicateVariable returns null if variable with specified ID does not exist', t => {
|
||||||
|
const target = new Target();
|
||||||
|
|
||||||
|
const variable = target.duplicateVariable('a var ID');
|
||||||
|
t.equal(variable, null);
|
||||||
|
t.equal(Object.keys(target.variables).length, 0);
|
||||||
|
|
||||||
|
target.createVariable('var id', 'foo', Variable.SCALAR_TYPE);
|
||||||
|
t.equal(Object.keys(target.variables).length, 1);
|
||||||
|
|
||||||
|
const anotherVariable = target.duplicateVariable('another var ID');
|
||||||
|
t.equal(anotherVariable, null);
|
||||||
|
t.equal(Object.keys(target.variables).length, 1);
|
||||||
|
t.type(target.variables['another var ID'], 'undefined');
|
||||||
|
t.type(target.variables['var id'], 'object');
|
||||||
|
t.notEqual(target.variables['var id'], null);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('duplicateVariables duplicates all variables', t => {
|
||||||
|
const target = new Target();
|
||||||
|
target.createVariable('var ID 1', 'var1', Variable.SCALAR_TYPE);
|
||||||
|
target.createVariable('var ID 2', 'var2', Variable.SCALAR_TYPE);
|
||||||
|
|
||||||
|
t.equal(Object.keys(target.variables).length, 2);
|
||||||
|
|
||||||
|
const var1 = target.variables['var ID 1'];
|
||||||
|
const var2 = target.variables['var ID 2'];
|
||||||
|
|
||||||
|
var1.value = 3;
|
||||||
|
var2.value = 'foo';
|
||||||
|
|
||||||
|
const duplicateVariables = target.duplicateVariables();
|
||||||
|
|
||||||
|
// Duplicating a target's variables should not change the target's own variables.
|
||||||
|
t.equal(Object.keys(target.variables).length, 2);
|
||||||
|
t.equal(Object.keys(duplicateVariables).length, 2);
|
||||||
|
|
||||||
|
// Should be able to find original var IDs in both this target's variables and
|
||||||
|
// the duplicate variables since a blocks container was not specified.
|
||||||
|
t.equal(target.variables.hasOwnProperty('var ID 1'), true);
|
||||||
|
t.equal(target.variables.hasOwnProperty('var ID 2'), true);
|
||||||
|
t.equal(duplicateVariables.hasOwnProperty('var ID 1'), true);
|
||||||
|
t.equal(duplicateVariables.hasOwnProperty('var ID 1'), true);
|
||||||
|
|
||||||
|
// Values of the duplicate varaiables should match the value of the original values at the time of duplication
|
||||||
|
t.equal(target.variables['var ID 1'].value, duplicateVariables['var ID 1'].value);
|
||||||
|
t.equal(duplicateVariables['var ID 1'].value, 3);
|
||||||
|
t.equal(target.variables['var ID 2'].value, duplicateVariables['var ID 2'].value);
|
||||||
|
t.equal(duplicateVariables['var ID 2'].value, 'foo');
|
||||||
|
|
||||||
|
// The two sets of variables should still be distinct, modifying the target's variables
|
||||||
|
// should not affect the duplicated variables, and vice-versa
|
||||||
|
|
||||||
|
var1.value = 10;
|
||||||
|
t.equal(target.variables['var ID 1'].value, 10);
|
||||||
|
t.equal(duplicateVariables['var ID 1'].value, 3); // should remain unchanged from initial value
|
||||||
|
|
||||||
|
duplicateVariables['var ID 2'].value = 'bar';
|
||||||
|
t.equal(target.variables['var ID 2'].value, 'foo');
|
||||||
|
|
||||||
|
// Deleting a variable on the target should not change the duplicated variables
|
||||||
|
target.deleteVariable('var ID 1');
|
||||||
|
t.equal(Object.keys(target.variables).length, 1);
|
||||||
|
t.equal(Object.keys(duplicateVariables).length, 2);
|
||||||
|
t.type(duplicateVariables['var ID 1'], 'object');
|
||||||
|
t.notEqual(duplicateVariables['var ID 1'], null);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('duplicateVariables re-IDs variables when a block container is provided', t => {
|
||||||
|
const target = new Target();
|
||||||
|
|
||||||
|
target.createVariable('mock var id', 'a mock variable', Variable.SCALAR_TYPE);
|
||||||
|
target.createVariable('another var id', 'var2', Variable.SCALAR_TYPE);
|
||||||
|
|
||||||
|
// Create a block on the target which references the variable with id 'mock var id'
|
||||||
|
target.blocks.createBlock(adapter(events.mockVariableBlock)[0]);
|
||||||
|
|
||||||
|
t.type(target.blocks.getBlock('a block'), 'object');
|
||||||
|
t.type(target.blocks.getBlock('a block').fields.VARIABLE, 'object');
|
||||||
|
t.equal(target.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id');
|
||||||
|
t.equal(target.blocks.getBlock('a block').fields.VARIABLE.value, 'a mock variable');
|
||||||
|
|
||||||
|
// Deep clone this target's blocks to pass in to 'duplicateVariables'
|
||||||
|
const copiedBlocks = target.blocks.duplicate();
|
||||||
|
|
||||||
|
// The copied block should still have the same ID, and its VARIABLE field should still refer to
|
||||||
|
// the original variable id
|
||||||
|
t.type(copiedBlocks.getBlock('a block'), 'object');
|
||||||
|
t.type(copiedBlocks.getBlock('a block').fields.VARIABLE, 'object');
|
||||||
|
t.equal(copiedBlocks.getBlock('a block').fields.VARIABLE.id, 'mock var id');
|
||||||
|
t.equal(copiedBlocks.getBlock('a block').fields.VARIABLE.value, 'a mock variable');
|
||||||
|
|
||||||
|
const duplicateVariables = target.duplicateVariables(copiedBlocks);
|
||||||
|
|
||||||
|
// Duplicate variables should have new IDs
|
||||||
|
t.equal(Object.keys(duplicateVariables).length, 2);
|
||||||
|
t.type(duplicateVariables['mock var id'], 'undefined');
|
||||||
|
t.type(duplicateVariables['another var id'], 'undefined');
|
||||||
|
|
||||||
|
// Duplicate variables still have the same names..
|
||||||
|
const dupes = Object.values(duplicateVariables);
|
||||||
|
const dupeVarNames = dupes.map(v => v.name);
|
||||||
|
|
||||||
|
t.notEqual(dupeVarNames.indexOf('a mock variable'), -1);
|
||||||
|
t.notEqual(dupeVarNames.indexOf('var2'), -1);
|
||||||
|
|
||||||
|
// Duplicating variables should not change blocks on current target
|
||||||
|
t.type(target.blocks.getBlock('a block'), 'object');
|
||||||
|
t.equal(target.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id');
|
||||||
|
t.equal(target.blocks.getBlock('a block').fields.VARIABLE.value, 'a mock variable');
|
||||||
|
|
||||||
|
// The copied blocks passed into duplicateVariables should now reference the new
|
||||||
|
// variable ID
|
||||||
|
const mockVariableDupe = dupes[dupeVarNames.indexOf('a mock variable')];
|
||||||
|
const mockVarDupeID = mockVariableDupe.id;
|
||||||
|
|
||||||
|
t.type(copiedBlocks.getBlock('a block'), 'object');
|
||||||
|
t.equal(copiedBlocks.getBlock('a block').fields.VARIABLE.id, mockVarDupeID);
|
||||||
|
t.equal(copiedBlocks.getBlock('a block').fields.VARIABLE.value, 'a mock variable');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
test('lookupOrCreateList creates a list if var with given id or var with given name does not exist', t => {
|
test('lookupOrCreateList creates a list if var with given id or var with given name does not exist', t => {
|
||||||
const target = new Target();
|
const target = new Target();
|
||||||
const variables = target.variables;
|
const variables = target.variables;
|
||||||
|
|
Loading…
Reference in a new issue