Disable having duplicate nodes (except whilst typing)
This commit is contained in:
parent
55c4c91370
commit
00baaacacc
|
@ -379,7 +379,7 @@ Node.prototype.setField = function(field, fieldEditable) {
|
||||||
*/
|
*/
|
||||||
Node.prototype.getField = function() {
|
Node.prototype.getField = function() {
|
||||||
if (this.field === undefined) {
|
if (this.field === undefined) {
|
||||||
this._getDomField();
|
this._getDomField(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.field;
|
return this.field;
|
||||||
|
@ -1891,13 +1891,25 @@ Node.prototype._getDomField = function(silent) {
|
||||||
this.fieldInnerText = util.getInnerText(this.dom.field);
|
this.fieldInnerText = util.getInnerText(this.dom.field);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.fieldInnerText != undefined) {
|
if (this.fieldInnerText !== undefined) {
|
||||||
try {
|
try {
|
||||||
var field = this._unescapeHTML(this.fieldInnerText);
|
var field = this._unescapeHTML(this.fieldInnerText);
|
||||||
|
|
||||||
if (field !== this.field) {
|
if (field !== this.field) {
|
||||||
this.field = field;
|
var existingFieldNames = this.parent.getFieldNames(this);
|
||||||
this._debouncedOnChangeField();
|
var isDuplicate = existingFieldNames.indexOf(field) !== -1;
|
||||||
|
|
||||||
|
if (!isDuplicate) {
|
||||||
|
this.field = field;
|
||||||
|
this._debouncedOnChangeField();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!silent) {
|
||||||
|
// fix duplicate field: change it into a unique name
|
||||||
|
this.field = util.findUniqueName(field, existingFieldNames);
|
||||||
|
this._debouncedOnChangeField();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
@ -1936,54 +1948,6 @@ Node.prototype._updateDomDefault = function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate this node and all it's childs
|
|
||||||
* @return {Array.<{node: Node, error: {message: string}}>} Returns a list with duplicates
|
|
||||||
*/
|
|
||||||
Node.prototype.validate = function () {
|
|
||||||
var errors = [];
|
|
||||||
|
|
||||||
// find duplicate keys
|
|
||||||
if (this.type === 'object') {
|
|
||||||
var keys = {};
|
|
||||||
var duplicateKeys = [];
|
|
||||||
for (var i = 0; i < this.childs.length; i++) {
|
|
||||||
var child = this.childs[i];
|
|
||||||
if (keys.hasOwnProperty(child.field)) {
|
|
||||||
duplicateKeys.push(child.field);
|
|
||||||
}
|
|
||||||
keys[child.field] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duplicateKeys.length > 0) {
|
|
||||||
errors = this.childs
|
|
||||||
.filter(function (node) {
|
|
||||||
return duplicateKeys.indexOf(node.field) !== -1;
|
|
||||||
})
|
|
||||||
.map(function (node) {
|
|
||||||
return {
|
|
||||||
node: node,
|
|
||||||
error: {
|
|
||||||
message: translate('duplicateKey') + ' "' + node.field + '"'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// recurse over the childs
|
|
||||||
if (this.childs) {
|
|
||||||
for (var i = 0; i < this.childs.length; i++) {
|
|
||||||
var e = this.childs[i].validate();
|
|
||||||
if (e.length > 0) {
|
|
||||||
errors = errors.concat(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the dom of the node
|
* Clear the dom of the node
|
||||||
*/
|
*/
|
||||||
|
@ -2958,6 +2922,13 @@ Node.prototype.onEvent = function (event) {
|
||||||
if (target == domField) {
|
if (target == domField) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'blur':
|
case 'blur':
|
||||||
|
this._getDomField(false);
|
||||||
|
this._updateDomField();
|
||||||
|
if (this.field) {
|
||||||
|
domField.innerHTML = this._escapeHTML(this.field);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'change':
|
case 'change':
|
||||||
this._getDomField(true);
|
this._getDomField(true);
|
||||||
this._updateDomField();
|
this._updateDomField();
|
||||||
|
@ -3462,6 +3433,25 @@ Node.prototype._showColorPicker = function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all field names of an object
|
||||||
|
* @param {Node} [excludeNode] Optional node to be excluded from the returned field names
|
||||||
|
* @return {string[]}
|
||||||
|
*/
|
||||||
|
Node.prototype.getFieldNames = function (excludeNode) {
|
||||||
|
if (this.type === 'object') {
|
||||||
|
return this.childs
|
||||||
|
.filter(function (child) {
|
||||||
|
return child !== excludeNode;
|
||||||
|
})
|
||||||
|
.map(function (child) {
|
||||||
|
return child.field;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove nodes
|
* Remove nodes
|
||||||
* @param {Node[] | Node} nodes
|
* @param {Node[] | Node} nodes
|
||||||
|
@ -3526,6 +3516,8 @@ Node.onDuplicate = function(nodes) {
|
||||||
var afterNode = lastNode;
|
var afterNode = lastNode;
|
||||||
var clones = nodes.map(function (node) {
|
var clones = nodes.map(function (node) {
|
||||||
var clone = node.clone();
|
var clone = node.clone();
|
||||||
|
var existingFieldNames = node.parent.getFieldNames();
|
||||||
|
clone.field = util.findUniqueName(node.field, existingFieldNames);
|
||||||
parent.insertAfter(clone, afterNode);
|
parent.insertAfter(clone, afterNode);
|
||||||
afterNode = clone;
|
afterNode = clone;
|
||||||
return clone;
|
return clone;
|
||||||
|
|
|
@ -577,9 +577,6 @@ treemode.validate = function () {
|
||||||
|
|
||||||
var json = root.getValue();
|
var json = root.getValue();
|
||||||
|
|
||||||
// check for duplicate keys
|
|
||||||
var duplicateErrors = root.validate();
|
|
||||||
|
|
||||||
// execute JSON schema validation
|
// execute JSON schema validation
|
||||||
var schemaErrors = [];
|
var schemaErrors = [];
|
||||||
if (this.validateSchema) {
|
if (this.validateSchema) {
|
||||||
|
@ -611,7 +608,7 @@ treemode.validate = function () {
|
||||||
.then(function (customValidationErrors) {
|
.then(function (customValidationErrors) {
|
||||||
// only apply when there was no other validation started whilst resolving async results
|
// only apply when there was no other validation started whilst resolving async results
|
||||||
if (seq === me.validationSequence) {
|
if (seq === me.validationSequence) {
|
||||||
var errorNodes = [].concat(duplicateErrors, schemaErrors, customValidationErrors || []);
|
var errorNodes = [].concat(schemaErrors, customValidationErrors || []);
|
||||||
me._renderValidationErrors(errorNodes);
|
me._renderValidationErrors(errorNodes);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1150,3 +1150,23 @@ exports.makeFieldTooltip = function (schema, locale) {
|
||||||
|
|
||||||
return tooltip;
|
return tooltip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a unique name. Suffix the name with ' (copy)', '(copy 2)', etc
|
||||||
|
* until a unique name is found
|
||||||
|
* @param {string} name
|
||||||
|
* @param {Array} existingPropNames Array with existing prop names
|
||||||
|
*/
|
||||||
|
exports.findUniqueName = function(name, existingPropNames) {
|
||||||
|
var strippedName = name.replace(/ \(copy( \d+)?\)$/, '')
|
||||||
|
var validName = strippedName
|
||||||
|
var i = 1
|
||||||
|
|
||||||
|
while (existingPropNames.indexOf(validName) !== -1) {
|
||||||
|
var copy = 'copy' + (i > 1 ? (' ' + i) : '')
|
||||||
|
validName = `${strippedName} (${copy})`
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return validName
|
||||||
|
}
|
||||||
|
|
|
@ -224,5 +224,51 @@ describe('util', function () {
|
||||||
assert.strictEqual(util.makeFieldTooltip({examples: ['foo']}, 'pt-BR'), 'Exemplos\n"foo"');
|
assert.strictEqual(util.makeFieldTooltip({examples: ['foo']}, 'pt-BR'), 'Exemplos\n"foo"');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should find a unique name', function () {
|
||||||
|
assert.strictEqual(util.findUniqueName('other', [
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c'
|
||||||
|
]), 'other')
|
||||||
|
|
||||||
|
assert.strictEqual(util.findUniqueName('b', [
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c'
|
||||||
|
]), 'b (copy)')
|
||||||
|
|
||||||
|
assert.strictEqual(util.findUniqueName('b', [
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'b (copy)'
|
||||||
|
]), 'b (copy 2)')
|
||||||
|
|
||||||
|
assert.strictEqual(util.findUniqueName('b', [
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'b (copy)',
|
||||||
|
'b (copy 2)'
|
||||||
|
]), 'b (copy 3)')
|
||||||
|
|
||||||
|
assert.strictEqual(util.findUniqueName('b (copy)', [
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'b (copy)',
|
||||||
|
'b (copy 2)',
|
||||||
|
'c'
|
||||||
|
]), 'b (copy 3)')
|
||||||
|
|
||||||
|
assert.strictEqual(util.findUniqueName('b (copy 2)', [
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'b (copy)',
|
||||||
|
'b (copy 2)',
|
||||||
|
'c'
|
||||||
|
]), 'b (copy 3)')
|
||||||
|
})
|
||||||
|
|
||||||
// TODO: thoroughly test all util methods
|
// TODO: thoroughly test all util methods
|
||||||
});
|
});
|
Loading…
Reference in New Issue