From c3eaf1afc5061f8d20665355b070724e2acfd584 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Thu, 22 Jul 2021 08:44:28 -0700 Subject: [PATCH 1/4] Migrate core/field_textinput.js to ES6 const/let --- core/field_textinput.js | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/core/field_textinput.js b/core/field_textinput.js index b2153b65e..c4c55dfcf 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -109,7 +109,7 @@ Blockly.FieldTextInput.prototype.DEFAULT_VALUE = ''; * @nocollapse */ Blockly.FieldTextInput.fromJson = function(options) { - var text = Blockly.utils.replaceMessageReferences(options['text']); + const text = Blockly.utils.replaceMessageReferences(options['text']); // `this` might be a subclass of FieldTextInput if that class doesn't override // the static fromJson method. return new this(text, undefined, options); @@ -150,12 +150,12 @@ Blockly.FieldTextInput.prototype.initView = function() { if (this.getConstants().FULL_BLOCK_FIELDS) { // Step one: figure out if this is the only field on this block. // Rendering is quite different in that case. - var nFields = 0; - var nConnections = 0; + let nFields = 0; + let nConnections = 0; // Count the number of fields, excluding text fields - for (var i = 0, input; (input = this.sourceBlock_.inputList[i]); i++) { - for (var j = 0; (input.fieldRow[j]); j++) { + for (let i = 0, input; (input = this.sourceBlock_.inputList[i]); i++) { + for (let j = 0; (input.fieldRow[j]); j++) { nFields ++; } if (input.connection) { @@ -203,7 +203,7 @@ Blockly.FieldTextInput.prototype.doClassValidation_ = function(opt_newValue) { Blockly.FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) { if (this.isBeingEdited_) { this.isTextValid_ = false; - var oldValue = this.value_; + const oldValue = this.value_; // Revert value when the text becomes invalid. this.value_ = this.htmlInput_.untypedDefaultValue_; if (this.sourceBlock_ && Blockly.Events.isEnabled()) { @@ -257,7 +257,7 @@ Blockly.FieldTextInput.prototype.render_ = function() { // doValueUpdate_ so that the code is more centralized. if (this.isBeingEdited_) { this.resizeEditor_(); - var htmlInput = /** @type {!HTMLElement} */(this.htmlInput_); + const htmlInput = /** @type {!HTMLElement} */(this.htmlInput_); if (!this.isTextValid_) { Blockly.utils.dom.addClass(htmlInput, 'blocklyInvalidInput'); Blockly.utils.aria.setState(htmlInput, @@ -296,7 +296,7 @@ Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e, opt_quietInput) { this.workspace_ = (/** @type {!Blockly.BlockSvg} */ (this.sourceBlock_)).workspace; - var quietInput = opt_quietInput || false; + const quietInput = opt_quietInput || false; if (!quietInput && (Blockly.utils.userAgent.MOBILE || Blockly.utils.userAgent.ANDROID || Blockly.utils.userAgent.IPAD)) { @@ -343,28 +343,28 @@ Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { */ Blockly.FieldTextInput.prototype.widgetCreate_ = function() { Blockly.Events.setGroup(true); - var div = Blockly.WidgetDiv.DIV; + const div = Blockly.WidgetDiv.DIV; Blockly.utils.dom.addClass(this.getClickTarget_(), 'editing'); - var htmlInput = /** @type {HTMLInputElement} */ (document.createElement('input')); + const htmlInput = /** @type {HTMLInputElement} */ (document.createElement('input')); htmlInput.className = 'blocklyHtmlInput'; htmlInput.setAttribute('spellcheck', this.spellcheck_); - var scale = this.workspace_.getScale(); - var fontSize = + const scale = this.workspace_.getScale(); + const fontSize = (this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt'; div.style.fontSize = fontSize; htmlInput.style.fontSize = fontSize; - var borderRadius = + let borderRadius = (Blockly.FieldTextInput.BORDERRADIUS * scale) + 'px'; if (this.fullBlockClickTarget_) { - var bBox = this.getScaledBBox(); + const bBox = this.getScaledBBox(); // Override border radius. borderRadius = (bBox.bottom - bBox.top) / 2 + 'px'; // Pull stroke colour from the existing shadow block - var strokeColour = this.sourceBlock_.getParent() ? + const strokeColour = this.sourceBlock_.getParent() ? this.sourceBlock_.getParent().style.colourTertiary : this.sourceBlock_.style.colourTertiary; htmlInput.style.border = (1 * scale) + 'px solid ' + strokeColour; @@ -409,7 +409,7 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() { // Actual disposal. this.unbindInputEvents_(); - var style = Blockly.WidgetDiv.DIV.style; + const style = Blockly.WidgetDiv.DIV.style; style.width = 'auto'; style.height = 'auto'; style.fontSize = ''; @@ -477,11 +477,11 @@ Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) { * @private */ Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(_e) { - var text = this.htmlInput_.value; + const text = this.htmlInput_.value; if (text !== this.htmlInput_.oldValue_) { this.htmlInput_.oldValue_ = text; - var value = this.getValueFromEditorText_(text); + const value = this.getValueFromEditorText_(text); this.setValue(value); this.forceRerender(); this.resizeEditor_(); @@ -512,15 +512,15 @@ Blockly.FieldTextInput.prototype.setEditorValue_ = function(newValue) { * @protected */ Blockly.FieldTextInput.prototype.resizeEditor_ = function() { - var div = Blockly.WidgetDiv.DIV; - var bBox = this.getScaledBBox(); + const div = Blockly.WidgetDiv.DIV; + const bBox = this.getScaledBBox(); div.style.width = bBox.right - bBox.left + 'px'; div.style.height = bBox.bottom - bBox.top + 'px'; // In RTL mode block fields and LTR input fields the left edge moves, // whereas the right edge is fixed. Reposition the editor. - var x = this.sourceBlock_.RTL ? bBox.right - div.offsetWidth : bBox.left; - var xy = new Blockly.utils.Coordinate(x, bBox.top); + const x = this.sourceBlock_.RTL ? bBox.right - div.offsetWidth : bBox.left; + const xy = new Blockly.utils.Coordinate(x, bBox.top); div.style.left = xy.x + 'px'; div.style.top = xy.y + 'px'; From d2a43460d0de014ee671de42f123a6f4ae1bc523 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Thu, 22 Jul 2021 08:49:08 -0700 Subject: [PATCH 2/4] Migrate core/field_textinput.js to goog.module --- core/field_textinput.js | 77 +++++++++++++++++++++-------------------- tests/deps.js | 2 +- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/core/field_textinput.js b/core/field_textinput.js index c4c55dfcf..4585ba382 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -10,7 +10,8 @@ */ 'use strict'; -goog.provide('Blockly.FieldTextInput'); +goog.module('Blockly.FieldTextInput'); +goog.module.declareLegacyNamespace(); goog.require('Blockly.browserEvents'); goog.require('Blockly.DropDownDiv'); @@ -46,7 +47,7 @@ goog.requireType('Blockly.WorkspaceSvg'); * @extends {Blockly.Field} * @constructor */ -Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) { +const FieldTextInput = function(opt_value, opt_validator, opt_config) { /** * Allow browser to spellcheck this field. * @type {boolean} @@ -54,7 +55,7 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) { */ this.spellcheck_ = true; - Blockly.FieldTextInput.superClass_.constructor.call(this, + FieldTextInput.superClass_.constructor.call(this, opt_value, opt_validator, opt_config); /** @@ -91,24 +92,24 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) { */ this.workspace_ = null; }; -Blockly.utils.object.inherits(Blockly.FieldTextInput, Blockly.Field); +Blockly.utils.object.inherits(FieldTextInput, Blockly.Field); /** * The default value for this field. * @type {*} * @protected */ -Blockly.FieldTextInput.prototype.DEFAULT_VALUE = ''; +FieldTextInput.prototype.DEFAULT_VALUE = ''; /** * Construct a FieldTextInput from a JSON arg object, * dereferencing any string table references. * @param {!Object} options A JSON object with options (text, and spellcheck). - * @return {!Blockly.FieldTextInput} The new field instance. + * @return {!FieldTextInput} The new field instance. * @package * @nocollapse */ -Blockly.FieldTextInput.fromJson = function(options) { +FieldTextInput.fromJson = function(options) { const text = Blockly.utils.replaceMessageReferences(options['text']); // `this` might be a subclass of FieldTextInput if that class doesn't override // the static fromJson method. @@ -120,24 +121,24 @@ Blockly.FieldTextInput.fromJson = function(options) { * are not. Editable fields should also be serializable. * @type {boolean} */ -Blockly.FieldTextInput.prototype.SERIALIZABLE = true; +FieldTextInput.prototype.SERIALIZABLE = true; /** * Pixel size of input border radius. * Should match blocklyText's border-radius in CSS. */ -Blockly.FieldTextInput.BORDERRADIUS = 4; +FieldTextInput.BORDERRADIUS = 4; /** * Mouse cursor style when over the hotspot that initiates the editor. */ -Blockly.FieldTextInput.prototype.CURSOR = 'text'; +FieldTextInput.prototype.CURSOR = 'text'; /** * @override */ -Blockly.FieldTextInput.prototype.configure_ = function(config) { - Blockly.FieldTextInput.superClass_.configure_.call(this, config); +FieldTextInput.prototype.configure_ = function(config) { + FieldTextInput.superClass_.configure_.call(this, config); if (typeof config['spellcheck'] == 'boolean') { this.spellcheck_ = config['spellcheck']; } @@ -146,7 +147,7 @@ Blockly.FieldTextInput.prototype.configure_ = function(config) { /** * @override */ -Blockly.FieldTextInput.prototype.initView = function() { +FieldTextInput.prototype.initView = function() { if (this.getConstants().FULL_BLOCK_FIELDS) { // Step one: figure out if this is the only field on this block. // Rendering is quite different in that case. @@ -184,7 +185,7 @@ Blockly.FieldTextInput.prototype.initView = function() { * @return {*} A valid string, or null if invalid. * @protected */ -Blockly.FieldTextInput.prototype.doClassValidation_ = function(opt_newValue) { +FieldTextInput.prototype.doClassValidation_ = function(opt_newValue) { if (opt_newValue === null || opt_newValue === undefined) { return null; } @@ -200,7 +201,7 @@ Blockly.FieldTextInput.prototype.doClassValidation_ = function(opt_newValue) { * the htmlInput_. * @protected */ -Blockly.FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) { +FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) { if (this.isBeingEdited_) { this.isTextValid_ = false; const oldValue = this.value_; @@ -221,7 +222,7 @@ Blockly.FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) { * that this is a string. * @protected */ -Blockly.FieldTextInput.prototype.doValueUpdate_ = function(newValue) { +FieldTextInput.prototype.doValueUpdate_ = function(newValue) { this.isTextValid_ = true; this.value_ = newValue; if (!this.isBeingEdited_) { @@ -234,7 +235,7 @@ Blockly.FieldTextInput.prototype.doValueUpdate_ = function(newValue) { * Updates text field to match the colour/style of the block. * @package */ -Blockly.FieldTextInput.prototype.applyColour = function() { +FieldTextInput.prototype.applyColour = function() { if (this.sourceBlock_ && this.getConstants().FULL_BLOCK_FIELDS) { if (this.borderRect_) { this.borderRect_.setAttribute('stroke', @@ -251,8 +252,8 @@ Blockly.FieldTextInput.prototype.applyColour = function() { * field's value. * @protected */ -Blockly.FieldTextInput.prototype.render_ = function() { - Blockly.FieldTextInput.superClass_.render_.call(this); +FieldTextInput.prototype.render_ = function() { + FieldTextInput.superClass_.render_.call(this); // This logic is done in render_ rather than doValueInvalid_ or // doValueUpdate_ so that the code is more centralized. if (this.isBeingEdited_) { @@ -274,7 +275,7 @@ Blockly.FieldTextInput.prototype.render_ = function() { * Set whether this field is spellchecked by the browser. * @param {boolean} check True if checked. */ -Blockly.FieldTextInput.prototype.setSpellcheck = function(check) { +FieldTextInput.prototype.setSpellcheck = function(check) { if (check == this.spellcheck_) { return; } @@ -292,7 +293,7 @@ Blockly.FieldTextInput.prototype.setSpellcheck = function(check) { * focus. Defaults to false. * @protected */ -Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e, +FieldTextInput.prototype.showEditor_ = function(_opt_e, opt_quietInput) { this.workspace_ = (/** @type {!Blockly.BlockSvg} */ (this.sourceBlock_)).workspace; @@ -311,7 +312,7 @@ Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e, * Mobile browsers have issues with in-line textareas (focus and keyboards). * @private */ -Blockly.FieldTextInput.prototype.showPromptEditor_ = function() { +FieldTextInput.prototype.showPromptEditor_ = function() { Blockly.prompt(Blockly.Msg['CHANGE_VALUE_TITLE'], this.getText(), function(text) { this.setValue(this.getValueFromEditorText_(text)); @@ -324,7 +325,7 @@ Blockly.FieldTextInput.prototype.showPromptEditor_ = function() { * focus. * @private */ -Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { +FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { Blockly.WidgetDiv.show( this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this)); this.htmlInput_ = this.widgetCreate_(); @@ -341,7 +342,7 @@ Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { * @return {!HTMLElement} The newly created text input editor. * @protected */ -Blockly.FieldTextInput.prototype.widgetCreate_ = function() { +FieldTextInput.prototype.widgetCreate_ = function() { Blockly.Events.setGroup(true); const div = Blockly.WidgetDiv.DIV; @@ -356,7 +357,7 @@ Blockly.FieldTextInput.prototype.widgetCreate_ = function() { div.style.fontSize = fontSize; htmlInput.style.fontSize = fontSize; let borderRadius = - (Blockly.FieldTextInput.BORDERRADIUS * scale) + 'px'; + (FieldTextInput.BORDERRADIUS * scale) + 'px'; if (this.fullBlockClickTarget_) { const bBox = this.getScaledBBox(); @@ -395,7 +396,7 @@ Blockly.FieldTextInput.prototype.widgetCreate_ = function() { * DOM-references belonging to the editor. * @protected */ -Blockly.FieldTextInput.prototype.widgetDispose_ = function() { +FieldTextInput.prototype.widgetDispose_ = function() { // Non-disposal related things that we do when the editor closes. this.isBeingEdited_ = false; this.isTextValid_ = true; @@ -426,7 +427,7 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() { * handlers will be bound. * @protected */ -Blockly.FieldTextInput.prototype.bindInputEvents_ = function(htmlInput) { +FieldTextInput.prototype.bindInputEvents_ = function(htmlInput) { // Trap Enter without IME and Esc to hide. this.onKeyDownWrapper_ = Blockly.browserEvents.conditionalBind( htmlInput, 'keydown', this, this.onHtmlInputKeyDown_); @@ -439,7 +440,7 @@ Blockly.FieldTextInput.prototype.bindInputEvents_ = function(htmlInput) { * Unbind handlers for user input and workspace size changes. * @protected */ -Blockly.FieldTextInput.prototype.unbindInputEvents_ = function() { +FieldTextInput.prototype.unbindInputEvents_ = function() { if (this.onKeyDownWrapper_) { Blockly.browserEvents.unbind(this.onKeyDownWrapper_); this.onKeyDownWrapper_ = null; @@ -455,7 +456,7 @@ Blockly.FieldTextInput.prototype.unbindInputEvents_ = function() { * @param {!Event} e Keyboard event. * @protected */ -Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) { +FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) { if (e.keyCode == Blockly.utils.KeyCodes.ENTER) { Blockly.WidgetDiv.hide(); Blockly.DropDownDiv.hideWithoutAnimation(); @@ -476,7 +477,7 @@ Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) { * @param {!Event} _e Keyboard event. * @private */ -Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(_e) { +FieldTextInput.prototype.onHtmlInputChange_ = function(_e) { const text = this.htmlInput_.value; if (text !== this.htmlInput_.oldValue_) { this.htmlInput_.oldValue_ = text; @@ -495,7 +496,7 @@ Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(_e) { * @param {*} newValue New value. * @protected */ -Blockly.FieldTextInput.prototype.setEditorValue_ = function(newValue) { +FieldTextInput.prototype.setEditorValue_ = function(newValue) { this.isDirty_ = true; if (this.isBeingEdited_) { // In the case this method is passed an invalid value, we still @@ -511,7 +512,7 @@ Blockly.FieldTextInput.prototype.setEditorValue_ = function(newValue) { * Resize the editor to fit the text. * @protected */ -Blockly.FieldTextInput.prototype.resizeEditor_ = function() { +FieldTextInput.prototype.resizeEditor_ = function() { const div = Blockly.WidgetDiv.DIV; const bBox = this.getScaledBBox(); div.style.width = bBox.right - bBox.left + 'px'; @@ -531,7 +532,7 @@ Blockly.FieldTextInput.prototype.resizeEditor_ = function() { * @return {boolean} True if the field is tab navigable. * @override */ -Blockly.FieldTextInput.prototype.isTabNavigable = function() { +FieldTextInput.prototype.isTabNavigable = function() { return true; }; @@ -544,7 +545,7 @@ Blockly.FieldTextInput.prototype.isTabNavigable = function() { * @protected * @override */ -Blockly.FieldTextInput.prototype.getText_ = function() { +FieldTextInput.prototype.getText_ = function() { if (this.isBeingEdited_ && this.htmlInput_) { // We are currently editing, return the HTML input value instead. return this.htmlInput_.value; @@ -561,7 +562,7 @@ Blockly.FieldTextInput.prototype.getText_ = function() { * @return {string} The text to show on the HTML input. * @protected */ -Blockly.FieldTextInput.prototype.getEditorText_ = function(value) { +FieldTextInput.prototype.getEditorText_ = function(value) { return String(value); }; @@ -575,8 +576,10 @@ Blockly.FieldTextInput.prototype.getEditorText_ = function(value) { * @return {*} The value to store. * @protected */ -Blockly.FieldTextInput.prototype.getValueFromEditorText_ = function(text) { +FieldTextInput.prototype.getValueFromEditorText_ = function(text) { return text; }; -Blockly.fieldRegistry.register('field_input', Blockly.FieldTextInput); +Blockly.fieldRegistry.register('field_input', FieldTextInput); + +exports = FieldTextInput; diff --git a/tests/deps.js b/tests/deps.js index da56c39f2..0e96aa2c3 100644 --- a/tests/deps.js +++ b/tests/deps.js @@ -59,7 +59,7 @@ goog.addDependency('../../core/field_label_serializable.js', ['Blockly.FieldLabe goog.addDependency('../../core/field_multilineinput.js', ['Blockly.FieldMultilineInput'], ['Blockly.Css', 'Blockly.Field', 'Blockly.FieldTextInput', 'Blockly.WidgetDiv', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.KeyCodes', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es5'}); goog.addDependency('../../core/field_number.js', ['Blockly.FieldNumber'], ['Blockly.FieldTextInput', 'Blockly.fieldRegistry', 'Blockly.utils.aria', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/field_registry.js', ['Blockly.fieldRegistry'], ['Blockly.registry']); -goog.addDependency('../../core/field_textinput.js', ['Blockly.FieldTextInput'], ['Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.WidgetDiv', 'Blockly.browserEvents', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent']); +goog.addDependency('../../core/field_textinput.js', ['Blockly.FieldTextInput'], ['Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.WidgetDiv', 'Blockly.browserEvents', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/field_variable.js', ['Blockly.FieldVariable'], ['Blockly.Events.BlockChange', 'Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.Xml', 'Blockly.fieldRegistry', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.object']); goog.addDependency('../../core/flyout_base.js', ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutMetricsManager', 'Blockly.Gesture', 'Blockly.IFlyout', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.toolbox', 'Blockly.utils.xml']); goog.addDependency('../../core/flyout_button.js', ['Blockly.FlyoutButton'], ['Blockly.Css', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.style'], {'lang': 'es5'}); From 9573f2b7ec102a0621e40ddbda6da9b1238795a7 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Thu, 22 Jul 2021 10:21:57 -0700 Subject: [PATCH 3/4] Migrate core/field_textinput.js to named requires --- core/field_textinput.js | 120 ++++++++++++++++++++-------------------- tests/deps.js | 2 +- 2 files changed, 62 insertions(+), 60 deletions(-) diff --git a/core/field_textinput.js b/core/field_textinput.js index 4585ba382..07c0e3282 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -13,25 +13,27 @@ goog.module('Blockly.FieldTextInput'); goog.module.declareLegacyNamespace(); -goog.require('Blockly.browserEvents'); -goog.require('Blockly.DropDownDiv'); -goog.require('Blockly.Events'); +/* eslint-disable-next-line no-unused-vars */ +const BlockSvg = goog.requireType('Blockly.BlockSvg'); +const Coordinate = goog.require('Blockly.utils.Coordinate'); +const DropDownDiv = goog.require('Blockly.DropDownDiv'); +const Events = goog.require('Blockly.Events'); +const Field = goog.require('Blockly.Field'); +const KeyCodes = goog.require('Blockly.utils.KeyCodes'); +const Msg = goog.require('Blockly.Msg'); +const WidgetDiv = goog.require('Blockly.WidgetDiv'); +/* eslint-disable-next-line no-unused-vars */ +const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg'); +const aria = goog.require('Blockly.utils.aria'); +const browserEvents = goog.require('Blockly.browserEvents'); +const dom = goog.require('Blockly.utils.dom'); +const fieldRegistry = goog.require('Blockly.fieldRegistry'); +const userAgent = goog.require('Blockly.utils.userAgent'); +const {inherits} = goog.require('Blockly.utils.object'); +const {prompt: blocklyPrompt} = goog.require('Blockly'); +const {replaceMessageReferences} = goog.require('Blockly.utils'); /** @suppress {extraRequire} */ goog.require('Blockly.Events.BlockChange'); -goog.require('Blockly.Field'); -goog.require('Blockly.fieldRegistry'); -goog.require('Blockly.Msg'); -goog.require('Blockly.utils'); -goog.require('Blockly.utils.aria'); -goog.require('Blockly.utils.Coordinate'); -goog.require('Blockly.utils.dom'); -goog.require('Blockly.utils.KeyCodes'); -goog.require('Blockly.utils.object'); -goog.require('Blockly.utils.userAgent'); -goog.require('Blockly.WidgetDiv'); - -goog.requireType('Blockly.BlockSvg'); -goog.requireType('Blockly.WorkspaceSvg'); /** @@ -44,7 +46,7 @@ goog.requireType('Blockly.WorkspaceSvg'); * @param {Object=} opt_config A map of options used to configure the field. * See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation} * for a list of properties this parameter supports. - * @extends {Blockly.Field} + * @extends {Field} * @constructor */ const FieldTextInput = function(opt_value, opt_validator, opt_config) { @@ -66,14 +68,14 @@ const FieldTextInput = function(opt_value, opt_validator, opt_config) { /** * Key down event data. - * @type {?Blockly.browserEvents.Data} + * @type {?browserEvents.Data} * @private */ this.onKeyDownWrapper_ = null; /** * Key input event data. - * @type {?Blockly.browserEvents.Data} + * @type {?browserEvents.Data} * @private */ this.onKeyInputWrapper_ = null; @@ -87,12 +89,12 @@ const FieldTextInput = function(opt_value, opt_validator, opt_config) { /** * The workspace that this field belongs to. - * @type {?Blockly.WorkspaceSvg} + * @type {?WorkspaceSvg} * @protected */ this.workspace_ = null; }; -Blockly.utils.object.inherits(FieldTextInput, Blockly.Field); +inherits(FieldTextInput, Field); /** * The default value for this field. @@ -110,7 +112,7 @@ FieldTextInput.prototype.DEFAULT_VALUE = ''; * @nocollapse */ FieldTextInput.fromJson = function(options) { - const text = Blockly.utils.replaceMessageReferences(options['text']); + const text = replaceMessageReferences(options['text']); // `this` might be a subclass of FieldTextInput if that class doesn't override // the static fromJson method. return new this(text, undefined, options); @@ -207,8 +209,8 @@ FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) { const oldValue = this.value_; // Revert value when the text becomes invalid. this.value_ = this.htmlInput_.untypedDefaultValue_; - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))( + if (this.sourceBlock_ && Events.isEnabled()) { + Events.fire(new (Events.get(Events.BLOCK_CHANGE))( this.sourceBlock_, 'field', this.name || null, oldValue, this.value_)); } } @@ -260,13 +262,13 @@ FieldTextInput.prototype.render_ = function() { this.resizeEditor_(); const htmlInput = /** @type {!HTMLElement} */(this.htmlInput_); if (!this.isTextValid_) { - Blockly.utils.dom.addClass(htmlInput, 'blocklyInvalidInput'); - Blockly.utils.aria.setState(htmlInput, - Blockly.utils.aria.State.INVALID, true); + dom.addClass(htmlInput, 'blocklyInvalidInput'); + aria.setState(htmlInput, + aria.State.INVALID, true); } else { - Blockly.utils.dom.removeClass(htmlInput, 'blocklyInvalidInput'); - Blockly.utils.aria.setState(htmlInput, - Blockly.utils.aria.State.INVALID, false); + dom.removeClass(htmlInput, 'blocklyInvalidInput'); + aria.setState(htmlInput, + aria.State.INVALID, false); } } }; @@ -296,11 +298,11 @@ FieldTextInput.prototype.setSpellcheck = function(check) { FieldTextInput.prototype.showEditor_ = function(_opt_e, opt_quietInput) { this.workspace_ = - (/** @type {!Blockly.BlockSvg} */ (this.sourceBlock_)).workspace; + (/** @type {!BlockSvg} */ (this.sourceBlock_)).workspace; const quietInput = opt_quietInput || false; - if (!quietInput && (Blockly.utils.userAgent.MOBILE || - Blockly.utils.userAgent.ANDROID || - Blockly.utils.userAgent.IPAD)) { + if (!quietInput && (userAgent.MOBILE || + userAgent.ANDROID || + userAgent.IPAD)) { this.showPromptEditor_(); } else { this.showInlineEditor_(quietInput); @@ -313,7 +315,7 @@ FieldTextInput.prototype.showEditor_ = function(_opt_e, * @private */ FieldTextInput.prototype.showPromptEditor_ = function() { - Blockly.prompt(Blockly.Msg['CHANGE_VALUE_TITLE'], this.getText(), + blocklyPrompt(Msg['CHANGE_VALUE_TITLE'], this.getText(), function(text) { this.setValue(this.getValueFromEditorText_(text)); }.bind(this)); @@ -326,7 +328,7 @@ FieldTextInput.prototype.showPromptEditor_ = function() { * @private */ FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { - Blockly.WidgetDiv.show( + WidgetDiv.show( this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this)); this.htmlInput_ = this.widgetCreate_(); this.isBeingEdited_ = true; @@ -343,10 +345,10 @@ FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { * @protected */ FieldTextInput.prototype.widgetCreate_ = function() { - Blockly.Events.setGroup(true); - const div = Blockly.WidgetDiv.DIV; + Events.setGroup(true); + const div = WidgetDiv.DIV; - Blockly.utils.dom.addClass(this.getClickTarget_(), 'editing'); + dom.addClass(this.getClickTarget_(), 'editing'); const htmlInput = /** @type {HTMLInputElement} */ (document.createElement('input')); htmlInput.className = 'blocklyHtmlInput'; @@ -406,11 +408,11 @@ FieldTextInput.prototype.widgetDispose_ = function() { if (this.onFinishEditing_) { this.onFinishEditing_(this.value_); } - Blockly.Events.setGroup(false); + Events.setGroup(false); // Actual disposal. this.unbindInputEvents_(); - const style = Blockly.WidgetDiv.DIV.style; + const style = WidgetDiv.DIV.style; style.width = 'auto'; style.height = 'auto'; style.fontSize = ''; @@ -418,7 +420,7 @@ FieldTextInput.prototype.widgetDispose_ = function() { style.boxShadow = ''; this.htmlInput_ = null; - Blockly.utils.dom.removeClass(this.getClickTarget_(), 'editing'); + dom.removeClass(this.getClickTarget_(), 'editing'); }; /** @@ -429,10 +431,10 @@ FieldTextInput.prototype.widgetDispose_ = function() { */ FieldTextInput.prototype.bindInputEvents_ = function(htmlInput) { // Trap Enter without IME and Esc to hide. - this.onKeyDownWrapper_ = Blockly.browserEvents.conditionalBind( + this.onKeyDownWrapper_ = browserEvents.conditionalBind( htmlInput, 'keydown', this, this.onHtmlInputKeyDown_); // Resize after every input change. - this.onKeyInputWrapper_ = Blockly.browserEvents.conditionalBind( + this.onKeyInputWrapper_ = browserEvents.conditionalBind( htmlInput, 'input', this, this.onHtmlInputChange_); }; @@ -442,11 +444,11 @@ FieldTextInput.prototype.bindInputEvents_ = function(htmlInput) { */ FieldTextInput.prototype.unbindInputEvents_ = function() { if (this.onKeyDownWrapper_) { - Blockly.browserEvents.unbind(this.onKeyDownWrapper_); + browserEvents.unbind(this.onKeyDownWrapper_); this.onKeyDownWrapper_ = null; } if (this.onKeyInputWrapper_) { - Blockly.browserEvents.unbind(this.onKeyInputWrapper_); + browserEvents.unbind(this.onKeyInputWrapper_); this.onKeyInputWrapper_ = null; } }; @@ -457,16 +459,16 @@ FieldTextInput.prototype.unbindInputEvents_ = function() { * @protected */ FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) { - if (e.keyCode == Blockly.utils.KeyCodes.ENTER) { - Blockly.WidgetDiv.hide(); - Blockly.DropDownDiv.hideWithoutAnimation(); - } else if (e.keyCode == Blockly.utils.KeyCodes.ESC) { + if (e.keyCode == KeyCodes.ENTER) { + WidgetDiv.hide(); + DropDownDiv.hideWithoutAnimation(); + } else if (e.keyCode == KeyCodes.ESC) { this.setValue(this.htmlInput_.untypedDefaultValue_); - Blockly.WidgetDiv.hide(); - Blockly.DropDownDiv.hideWithoutAnimation(); - } else if (e.keyCode == Blockly.utils.KeyCodes.TAB) { - Blockly.WidgetDiv.hide(); - Blockly.DropDownDiv.hideWithoutAnimation(); + WidgetDiv.hide(); + DropDownDiv.hideWithoutAnimation(); + } else if (e.keyCode == KeyCodes.TAB) { + WidgetDiv.hide(); + DropDownDiv.hideWithoutAnimation(); this.sourceBlock_.tab(this, !e.shiftKey); e.preventDefault(); } @@ -513,7 +515,7 @@ FieldTextInput.prototype.setEditorValue_ = function(newValue) { * @protected */ FieldTextInput.prototype.resizeEditor_ = function() { - const div = Blockly.WidgetDiv.DIV; + const div = WidgetDiv.DIV; const bBox = this.getScaledBBox(); div.style.width = bBox.right - bBox.left + 'px'; div.style.height = bBox.bottom - bBox.top + 'px'; @@ -521,7 +523,7 @@ FieldTextInput.prototype.resizeEditor_ = function() { // In RTL mode block fields and LTR input fields the left edge moves, // whereas the right edge is fixed. Reposition the editor. const x = this.sourceBlock_.RTL ? bBox.right - div.offsetWidth : bBox.left; - const xy = new Blockly.utils.Coordinate(x, bBox.top); + const xy = new Coordinate(x, bBox.top); div.style.left = xy.x + 'px'; div.style.top = xy.y + 'px'; @@ -580,6 +582,6 @@ FieldTextInput.prototype.getValueFromEditorText_ = function(text) { return text; }; -Blockly.fieldRegistry.register('field_input', FieldTextInput); +fieldRegistry.register('field_input', FieldTextInput); exports = FieldTextInput; diff --git a/tests/deps.js b/tests/deps.js index 0e96aa2c3..c68a0441f 100644 --- a/tests/deps.js +++ b/tests/deps.js @@ -59,7 +59,7 @@ goog.addDependency('../../core/field_label_serializable.js', ['Blockly.FieldLabe goog.addDependency('../../core/field_multilineinput.js', ['Blockly.FieldMultilineInput'], ['Blockly.Css', 'Blockly.Field', 'Blockly.FieldTextInput', 'Blockly.WidgetDiv', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.KeyCodes', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es5'}); goog.addDependency('../../core/field_number.js', ['Blockly.FieldNumber'], ['Blockly.FieldTextInput', 'Blockly.fieldRegistry', 'Blockly.utils.aria', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/field_registry.js', ['Blockly.fieldRegistry'], ['Blockly.registry']); -goog.addDependency('../../core/field_textinput.js', ['Blockly.FieldTextInput'], ['Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.WidgetDiv', 'Blockly.browserEvents', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_textinput.js', ['Blockly.FieldTextInput'], ['Blockly', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.WidgetDiv', 'Blockly.browserEvents', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/field_variable.js', ['Blockly.FieldVariable'], ['Blockly.Events.BlockChange', 'Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.Xml', 'Blockly.fieldRegistry', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.object']); goog.addDependency('../../core/flyout_base.js', ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutMetricsManager', 'Blockly.Gesture', 'Blockly.IFlyout', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.toolbox', 'Blockly.utils.xml']); goog.addDependency('../../core/flyout_button.js', ['Blockly.FlyoutButton'], ['Blockly.Css', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.style'], {'lang': 'es5'}); From 13272c37136992a91639e6c27ed0fcd100f9cabb Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Thu, 22 Jul 2021 10:23:06 -0700 Subject: [PATCH 4/4] clang-format core/field_textinput.js --- core/field_textinput.js | 76 +++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/core/field_textinput.js b/core/field_textinput.js index 07c0e3282..cfa891e34 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -44,7 +44,8 @@ goog.require('Blockly.Events.BlockChange'); * changes to the field's value. Takes in a string & returns a validated * string, or null to abort the change. * @param {Object=} opt_config A map of options used to configure the field. - * See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation} + * See the [field creation documentation]{@link + * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation} * for a list of properties this parameter supports. * @extends {Field} * @constructor @@ -57,8 +58,8 @@ const FieldTextInput = function(opt_value, opt_validator, opt_config) { */ this.spellcheck_ = true; - FieldTextInput.superClass_.constructor.call(this, - opt_value, opt_validator, opt_config); + FieldTextInput.superClass_.constructor.call( + this, opt_value, opt_validator, opt_config); /** * The HTML input element. @@ -159,7 +160,7 @@ FieldTextInput.prototype.initView = function() { // Count the number of fields, excluding text fields for (let i = 0, input; (input = this.sourceBlock_.inputList[i]); i++) { for (let j = 0; (input.fieldRow[j]); j++) { - nFields ++; + nFields++; } if (input.connection) { nConnections++; @@ -211,7 +212,8 @@ FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) { this.value_ = this.htmlInput_.untypedDefaultValue_; if (this.sourceBlock_ && Events.isEnabled()) { Events.fire(new (Events.get(Events.BLOCK_CHANGE))( - this.sourceBlock_, 'field', this.name || null, oldValue, this.value_)); + this.sourceBlock_, 'field', this.name || null, oldValue, + this.value_)); } } }; @@ -240,11 +242,11 @@ FieldTextInput.prototype.doValueUpdate_ = function(newValue) { FieldTextInput.prototype.applyColour = function() { if (this.sourceBlock_ && this.getConstants().FULL_BLOCK_FIELDS) { if (this.borderRect_) { - this.borderRect_.setAttribute('stroke', - this.sourceBlock_.style.colourTertiary); + this.borderRect_.setAttribute( + 'stroke', this.sourceBlock_.style.colourTertiary); } else { - this.sourceBlock_.pathObject.svgPath.setAttribute('fill', - this.getConstants().FIELD_BORDER_RECT_COLOUR); + this.sourceBlock_.pathObject.svgPath.setAttribute( + 'fill', this.getConstants().FIELD_BORDER_RECT_COLOUR); } } }; @@ -260,15 +262,13 @@ FieldTextInput.prototype.render_ = function() { // doValueUpdate_ so that the code is more centralized. if (this.isBeingEdited_) { this.resizeEditor_(); - const htmlInput = /** @type {!HTMLElement} */(this.htmlInput_); + const htmlInput = /** @type {!HTMLElement} */ (this.htmlInput_); if (!this.isTextValid_) { dom.addClass(htmlInput, 'blocklyInvalidInput'); - aria.setState(htmlInput, - aria.State.INVALID, true); + aria.setState(htmlInput, aria.State.INVALID, true); } else { dom.removeClass(htmlInput, 'blocklyInvalidInput'); - aria.setState(htmlInput, - aria.State.INVALID, false); + aria.setState(htmlInput, aria.State.INVALID, false); } } }; @@ -295,14 +295,11 @@ FieldTextInput.prototype.setSpellcheck = function(check) { * focus. Defaults to false. * @protected */ -FieldTextInput.prototype.showEditor_ = function(_opt_e, - opt_quietInput) { - this.workspace_ = - (/** @type {!BlockSvg} */ (this.sourceBlock_)).workspace; +FieldTextInput.prototype.showEditor_ = function(_opt_e, opt_quietInput) { + this.workspace_ = (/** @type {!BlockSvg} */ (this.sourceBlock_)).workspace; const quietInput = opt_quietInput || false; - if (!quietInput && (userAgent.MOBILE || - userAgent.ANDROID || - userAgent.IPAD)) { + if (!quietInput && + (userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD)) { this.showPromptEditor_(); } else { this.showInlineEditor_(quietInput); @@ -315,10 +312,9 @@ FieldTextInput.prototype.showEditor_ = function(_opt_e, * @private */ FieldTextInput.prototype.showPromptEditor_ = function() { - blocklyPrompt(Msg['CHANGE_VALUE_TITLE'], this.getText(), - function(text) { - this.setValue(this.getValueFromEditorText_(text)); - }.bind(this)); + blocklyPrompt(Msg['CHANGE_VALUE_TITLE'], this.getText(), function(text) { + this.setValue(this.getValueFromEditorText_(text)); + }.bind(this)); }; /** @@ -328,13 +324,12 @@ FieldTextInput.prototype.showPromptEditor_ = function() { * @private */ FieldTextInput.prototype.showInlineEditor_ = function(quietInput) { - WidgetDiv.show( - this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this)); + WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this)); this.htmlInput_ = this.widgetCreate_(); this.isBeingEdited_ = true; if (!quietInput) { - this.htmlInput_.focus({preventScroll:true}); + this.htmlInput_.focus({preventScroll: true}); this.htmlInput_.select(); } }; @@ -350,16 +345,15 @@ FieldTextInput.prototype.widgetCreate_ = function() { dom.addClass(this.getClickTarget_(), 'editing'); - const htmlInput = /** @type {HTMLInputElement} */ (document.createElement('input')); + const htmlInput = + /** @type {HTMLInputElement} */ (document.createElement('input')); htmlInput.className = 'blocklyHtmlInput'; htmlInput.setAttribute('spellcheck', this.spellcheck_); const scale = this.workspace_.getScale(); - const fontSize = - (this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt'; + const fontSize = (this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt'; div.style.fontSize = fontSize; htmlInput.style.fontSize = fontSize; - let borderRadius = - (FieldTextInput.BORDERRADIUS * scale) + 'px'; + let borderRadius = (FieldTextInput.BORDERRADIUS * scale) + 'px'; if (this.fullBlockClickTarget_) { const bBox = this.getScaledBBox(); @@ -368,14 +362,14 @@ FieldTextInput.prototype.widgetCreate_ = function() { borderRadius = (bBox.bottom - bBox.top) / 2 + 'px'; // Pull stroke colour from the existing shadow block const strokeColour = this.sourceBlock_.getParent() ? - this.sourceBlock_.getParent().style.colourTertiary : - this.sourceBlock_.style.colourTertiary; + this.sourceBlock_.getParent().style.colourTertiary : + this.sourceBlock_.style.colourTertiary; htmlInput.style.border = (1 * scale) + 'px solid ' + strokeColour; div.style.borderRadius = borderRadius; div.style.transition = 'box-shadow 0.25s ease 0s'; if (this.getConstants().FIELD_TEXTINPUT_BOX_SHADOW) { - div.style.boxShadow = 'rgba(255, 255, 255, 0.3) 0 0 0 ' + - (4 * scale) + 'px'; + div.style.boxShadow = + 'rgba(255, 255, 255, 0.3) 0 0 0 ' + (4 * scale) + 'px'; } } htmlInput.style.borderRadius = borderRadius; @@ -539,10 +533,10 @@ FieldTextInput.prototype.isTabNavigable = function() { }; /** - * Use the `getText_` developer hook to override the field's text representation. - * When we're currently editing, return the current HTML value instead. - * Otherwise, return null which tells the field to use the default behaviour - * (which is a string cast of the field's value). + * Use the `getText_` developer hook to override the field's text + * representation. When we're currently editing, return the current HTML value + * instead. Otherwise, return null which tells the field to use the default + * behaviour (which is a string cast of the field's value). * @return {?string} The HTML value if we're editing, otherwise null. * @protected * @override