Simplify field validator chaining.

This commit is contained in:
Neil Fraser 2016-06-29 17:44:12 -07:00
parent f474e11345
commit 555eac8b7f
10 changed files with 59 additions and 128 deletions

View file

@ -236,6 +236,37 @@ Blockly.Field.prototype.getValidator = function() {
return this.validator_;
};
/**
* Calls the validation function for this field, as well as all the validation
* function for the field's class and its parents.
* @param {string} text Proposed text.
* @return {?string} Revised text, or null if invalid.
*/
Blockly.Field.prototype.callValidator = function(text) {
// Collect a list of validators, from Field, through to the subclass, ending
// with the user's validator.
var validators = [this.getValidator()];
var fieldClass = this.constructor;
while (fieldClass) {
validators.unshift(fieldClass.classValidator);
fieldClass = fieldClass.superClass_;
}
// Call each validator in turn, allowing each to rewrite or reject.
for (var i = 0; i < validators.length; i++) {
var validator = validators[i];
if (validator) {
var result = validator.call(this, text);
if (result === null) {
// Validator rejects value. Game over.
return null;
} else if (result !== undefined) {
text = result;
}
}
}
return text;
};
/**
* Gets the group element for this editable field.
* Used for measuring the size and for positioning.

View file

@ -50,35 +50,6 @@ Blockly.FieldAngle = function(text, opt_validator) {
};
goog.inherits(Blockly.FieldAngle, Blockly.FieldTextInput);
/**
* Sets a new change handler for angle field.
* @param {Function} handler New change handler, or null.
*/
Blockly.FieldAngle.prototype.setValidator = function(handler) {
var wrappedHandler;
if (handler) {
// Wrap the user's change handler together with the angle validator.
wrappedHandler = function(value) {
var v1 = handler.call(this, value);
if (v1 === null) {
var v2 = v1;
} else {
if (v1 === undefined) {
v1 = value;
}
var v2 = Blockly.FieldAngle.angleValidator.call(this, v1);
if (v2 === undefined) {
v2 = v1;
}
}
return v2 === value ? undefined : v2;
};
} else {
wrappedHandler = Blockly.FieldAngle.angleValidator;
}
Blockly.FieldAngle.superClass_.setValidator.call(this, wrappedHandler);
};
/**
* Round angles to the nearest 15 degrees when using mouse.
* Set to 0 to disable rounding.
@ -303,8 +274,9 @@ Blockly.FieldAngle.prototype.updateGraph_ = function() {
* Ensure that only an angle may be entered.
* @param {string} text The user's text.
* @return {?string} A string representing a valid angle, or null if invalid.
* @this {!Blockly.FieldAngle}
*/
Blockly.FieldAngle.angleValidator = function(text) {
Blockly.FieldAngle.classValidator = function(text) {
if (text === null) {
return null;
}

View file

@ -107,12 +107,9 @@ Blockly.FieldCheckbox.prototype.setValue = function(strBool) {
*/
Blockly.FieldCheckbox.prototype.showEditor_ = function() {
var newState = !this.state_;
if (this.sourceBlock_ && this.validator_) {
if (this.sourceBlock_) {
// Call any validation function, and allow it to override.
var override = this.validator_(newState);
if (override !== undefined) {
newState = override;
}
newState = this.callValidator(newState);
}
if (newState !== null) {
this.setValue(String(newState).toUpperCase());

View file

@ -213,12 +213,9 @@ Blockly.FieldColour.prototype.showEditor_ = function() {
function(event) {
var colour = event.target.getSelectedColor() || '#000000';
Blockly.WidgetDiv.hide();
if (thisField.sourceBlock_ && thisField.validator_) {
if (thisField.sourceBlock_) {
// Call any validation function, and allow it to override.
var override = thisField.validator_(colour);
if (override !== undefined) {
colour = override;
}
colour = thisField.callValidator(colour);
}
if (colour !== null) {
thisField.setValue(colour);

View file

@ -82,11 +82,11 @@ Blockly.FieldDate.prototype.getValue = function() {
* @param {string} date The new date.
*/
Blockly.FieldDate.prototype.setValue = function(date) {
if (this.sourceBlock_ && this.validator_) {
var validated = this.validator_(date);
if (this.sourceBlock_) {
var validated = this.callValidator(date);
// If the new date is invalid, validation returns null.
// In this case we still want to display the illegal result.
if (validated !== null && validated !== undefined) {
if (validated !== null) {
date = validated;
}
}
@ -149,12 +149,9 @@ Blockly.FieldDate.prototype.showEditor_ = function() {
function(event) {
var date = event.date ? event.date.toIsoString(true) : '';
Blockly.WidgetDiv.hide();
if (thisField.sourceBlock_ && thisField.validator_) {
if (thisField.sourceBlock_) {
// Call any validation function, and allow it to override.
var override = thisField.validator_(date);
if (override !== undefined) {
date = override;
}
date = thisField.callValidator(date);
}
thisField.setValue(date);
});

View file

@ -108,12 +108,9 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() {
var menuItem = e.target;
if (menuItem) {
var value = menuItem.getValue();
if (thisField.sourceBlock_ && thisField.validator_) {
if (thisField.sourceBlock_) {
// Call any validation function, and allow it to override.
var override = thisField.validator_(value);
if (override !== undefined) {
value = override;
}
value = thisField.callValidator(value);
}
if (value !== null) {
thisField.setValue(value);

View file

@ -67,44 +67,16 @@ Blockly.FieldNumber.prototype.setConstraints = function(min, max, precision) {
this.min_ = isNaN(min) ? -Infinity : min;
max = parseFloat(max);
this.max_ = isNaN(max) ? Infinity : max;
this.setValue(this.numberValidator(this.getValue));
};
/**
* Sets a new change handler for number field.
* @param {Function} handler New change handler, or null.
*/
Blockly.FieldNumber.prototype.setValidator = function(handler) {
var wrappedHandler;
if (handler) {
// Wrap the user's change handler together with the angle validator.
wrappedHandler = function(value) {
var v1 = handler.call(this, value);
if (v1 === null) {
var v2 = v1;
} else {
if (v1 === undefined) {
v1 = value;
}
var v2 = this.numberValidator(v1);
if (v2 === undefined) {
v2 = v1;
}
}
return v2 === value ? undefined : v2;
};
} else {
wrappedHandler = this.numberValidator;
}
Blockly.FieldNumber.superClass_.setValidator.call(this, wrappedHandler);
this.setValue(this.callValidator(this.getValue));
};
/**
* Ensure that only a number in the correct range may be entered.
* @param {string} text The user's text.
* @return {?string} A string representing a valid number, or null if invalid.
* @this {!Blockly.FieldNumber}
*/
Blockly.FieldNumber.prototype.numberValidator = function(text) {
Blockly.FieldNumber.classValidator = function(text) {
if (text === null) {
return null;
}

View file

@ -82,11 +82,11 @@ Blockly.FieldTextInput.prototype.setValue = function(text) {
if (text === null) {
return; // No change if null.
}
if (this.sourceBlock_ && this.validator_) {
var validated = this.validator_(text);
if (this.sourceBlock_) {
var validated = this.callValidator(text);
// If the new text is invalid, validation returns null.
// In this case we still want to display the illegal result.
if (validated !== null && validated !== undefined) {
if (validated !== null) {
text = validated;
}
}
@ -114,11 +114,8 @@ Blockly.FieldTextInput.prototype.showEditor_ = function(opt_quietInput) {
goog.userAgent.IPAD)) {
// Mobile browsers have issues with in-line textareas (focus & keyboards).
var newValue = window.prompt(Blockly.Msg.CHANGE_VALUE_TITLE, this.text_);
if (this.sourceBlock_ && this.validator_) {
var override = this.validator_(newValue);
if (override !== undefined) {
newValue = override;
}
if (this.sourceBlock_) {
newValue = this.callValidator(newValue);
}
this.setValue(newValue);
return;
@ -210,8 +207,8 @@ Blockly.FieldTextInput.prototype.validate_ = function() {
var valid = true;
goog.asserts.assertObject(Blockly.FieldTextInput.htmlInput_);
var htmlInput = Blockly.FieldTextInput.htmlInput_;
if (this.sourceBlock_ && this.validator_) {
valid = this.validator_(htmlInput.value);
if (this.sourceBlock_) {
valid = this.callValidator(htmlInput.value);
}
if (valid === null) {
Blockly.addClass_(htmlInput, 'blocklyInvalidInput');
@ -264,12 +261,12 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
var htmlInput = Blockly.FieldTextInput.htmlInput_;
// Save the edit (if it validates).
var text = htmlInput.value;
if (thisField.sourceBlock_ && thisField.validator_) {
var text1 = thisField.validator_(text);
if (thisField.sourceBlock_) {
var text1 = thisField.callValidator(text);
if (text1 === null) {
// Invalid edit.
text = htmlInput.defaultValue;
} else if (text1 !== undefined) {
} else {
// Validation function has changed the text.
text = text1;
}

View file

@ -48,35 +48,6 @@ Blockly.FieldVariable = function(varname, opt_validator) {
};
goog.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);
/**
* Sets a new change handler for angle field.
* @param {Function} handler New change handler, or null.
*/
Blockly.FieldVariable.prototype.setValidator = function(handler) {
var wrappedHandler;
if (handler) {
// Wrap the user's change handler together with the variable rename handler.
wrappedHandler = function(value) {
var v1 = handler.call(this, value);
if (v1 === null) {
var v2 = v1;
} else {
if (v1 === undefined) {
v1 = value;
}
var v2 = Blockly.FieldVariable.dropdownChange.call(this, v1);
if (v2 === undefined) {
v2 = v1;
}
}
return v2 === value ? undefined : v2;
};
} else {
wrappedHandler = Blockly.FieldVariable.dropdownChange;
}
Blockly.FieldVariable.superClass_.setValidator.call(this, wrappedHandler);
};
/**
* Install this dropdown on a block.
*/
@ -158,7 +129,7 @@ Blockly.FieldVariable.dropdownCreate = function() {
* handled (rename), or undefined if an existing variable was chosen.
* @this {!Blockly.FieldVariable}
*/
Blockly.FieldVariable.dropdownChange = function(text) {
Blockly.FieldVariable.classValidator = function(text) {
function promptName(promptText, defaultText) {
Blockly.hideChaff();
var newVar = window.prompt(promptText, defaultText);

View file

@ -712,7 +712,7 @@ Blockly.WorkspaceSvg.prototype.moveDrag = function(e) {
/**
* Is the user currently dragging a block or scrolling the workspace?
* @return {boolean} True if currently dragging or scrolling
* @return {boolean} True if currently dragging or scrolling.
*/
Blockly.WorkspaceSvg.prototype.isDragging = function() {
return Blockly.dragMode_ == Blockly.DRAG_FREE || this.isScrolling;