Merge branch 'goog_module' into flyout_button

This commit is contained in:
Aaron Dodson
2021-07-30 08:02:53 -07:00
committed by GitHub
47 changed files with 3961 additions and 3798 deletions

View File

@@ -10,61 +10,63 @@
*/
'use strict';
goog.provide('Blockly.BlockDragger');
goog.module('Blockly.BlockDragger');
goog.module.declareLegacyNamespace();
goog.require('Blockly.blockAnimations');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Coordinate = goog.require('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const IBlockDragger = goog.require('Blockly.IBlockDragger');
/* eslint-disable-next-line no-unused-vars */
const IDragTarget = goog.requireType('Blockly.IDragTarget');
const InsertionMarkerManager = goog.require('Blockly.InsertionMarkerManager');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const blockAnimation = goog.require('Blockly.blockAnimations');
const dom = goog.require('Blockly.utils.dom');
const events = goog.require('Blockly.Events');
const registry = goog.require('Blockly.registry');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockDrag');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockMove');
goog.require('Blockly.IBlockDragger');
goog.require('Blockly.InsertionMarkerManager');
goog.require('Blockly.registry');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.IDragTarget');
goog.requireType('Blockly.WorkspaceSvg');
/**
* Class for a block dragger. It moves blocks around the workspace when they
* are being dragged by a mouse or touch.
* @param {!Blockly.BlockSvg} block The block to drag.
* @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on.
* @param {!BlockSvg} block The block to drag.
* @param {!WorkspaceSvg} workspace The workspace to drag on.
* @constructor
* @implements {Blockly.IBlockDragger}
* @implements {IBlockDragger}
*/
Blockly.BlockDragger = function(block, workspace) {
const BlockDragger = function(block, workspace) {
/**
* The top block in the stack that is being dragged.
* @type {!Blockly.BlockSvg}
* @type {!BlockSvg}
* @protected
*/
this.draggingBlock_ = block;
/**
* The workspace on which the block is being dragged.
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @protected
*/
this.workspace_ = workspace;
/**
* Object that keeps track of connections on dragged blocks.
* @type {!Blockly.InsertionMarkerManager}
* @type {!InsertionMarkerManager}
* @protected
*/
this.draggedConnectionManager_ =
new Blockly.InsertionMarkerManager(this.draggingBlock_);
new InsertionMarkerManager(this.draggingBlock_);
/**
* Which drag area the mouse pointer is over, if any.
* @type {?Blockly.IDragTarget}
* @type {?IDragTarget}
* @private
*/
this.dragTarget_ = null;
@@ -79,7 +81,7 @@ Blockly.BlockDragger = function(block, workspace) {
/**
* The location of the top left corner of the dragging block at the beginning
* of the drag in workspace coordinates.
* @type {!Blockly.utils.Coordinate}
* @type {!Coordinate}
* @protected
*/
this.startXY_ = this.draggingBlock_.getRelativeToSurfaceXY();
@@ -91,14 +93,14 @@ Blockly.BlockDragger = function(block, workspace) {
* @type {Array<!Object>}
* @protected
*/
this.dragIconData_ = Blockly.BlockDragger.initIconData_(block);
this.dragIconData_ = initIconData(block);
};
/**
* Sever all links from this object.
* @package
*/
Blockly.BlockDragger.prototype.dispose = function() {
BlockDragger.prototype.dispose = function() {
this.dragIconData_.length = 0;
if (this.draggedConnectionManager_) {
@@ -110,19 +112,19 @@ Blockly.BlockDragger.prototype.dispose = function() {
* Make a list of all of the icons (comment, warning, and mutator) that are
* on this block and its descendants. Moving an icon moves the bubble that
* extends from it if that bubble is open.
* @param {!Blockly.BlockSvg} block The root block that is being dragged.
* @param {!BlockSvg} block The root block that is being dragged.
* @return {!Array<!Object>} The list of all icons and their locations.
* @private
*/
Blockly.BlockDragger.initIconData_ = function(block) {
const initIconData = function(block) {
// Build a list of icons that need to be moved and where they started.
var dragIconData = [];
var descendants = block.getDescendants(false);
for (var i = 0, descendant; (descendant = descendants[i]); i++) {
var icons = descendant.getIcons();
for (var j = 0; j < icons.length; j++) {
var data = {
// Blockly.utils.Coordinate with x and y properties (workspace
const dragIconData = [];
const descendants = block.getDescendants(false);
for (let i = 0, descendant; (descendant = descendants[i]); i++) {
const icons = descendant.getIcons();
for (let j = 0; j < icons.length; j++) {
const data = {
// Coordinate with x and y properties (workspace
// coordinates).
location: icons[j].getIconLocation(),
// Blockly.Icon
@@ -136,16 +138,15 @@ Blockly.BlockDragger.initIconData_ = function(block) {
/**
* Start dragging a block. This includes moving it to the drag surface.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at mouse down, in pixel units.
* @param {boolean} healStack Whether or not to heal the stack after
* disconnecting.
* @public
*/
Blockly.BlockDragger.prototype.startDrag = function(
currentDragDeltaXY, healStack) {
if (!Blockly.Events.getGroup()) {
Blockly.Events.setGroup(true);
BlockDragger.prototype.startDrag = function(currentDragDeltaXY, healStack) {
if (!events.getGroup()) {
events.setGroup(true);
}
this.fireDragStartEvent_();
@@ -159,9 +160,9 @@ Blockly.BlockDragger.prototype.startDrag = function(
// During a drag there may be a lot of rerenders, but not field changes.
// Turn the cache on so we don't do spurious remeasures during the drag.
Blockly.utils.dom.startTextWidthCache();
dom.startTextWidthCache();
this.workspace_.setResizesEnabled(false);
Blockly.blockAnimations.disconnectUiStop();
blockAnimation.disconnectUiStop();
if (this.shouldDisconnect_(healStack)) {
this.disconnectBlock_(healStack, currentDragDeltaXY);
@@ -180,9 +181,9 @@ Blockly.BlockDragger.prototype.startDrag = function(
* @return {boolean} True to disconnect the block, false otherwise.
* @protected
*/
Blockly.BlockDragger.prototype.shouldDisconnect_ = function(healStack) {
BlockDragger.prototype.shouldDisconnect_ = function(healStack) {
return !!(
this.draggingBlock_.getParent() ||
this.draggingBlock_.getParent() ||
(healStack && this.draggingBlock_.nextConnection &&
this.draggingBlock_.nextConnection.targetBlock()));
};
@@ -191,18 +192,18 @@ Blockly.BlockDragger.prototype.shouldDisconnect_ = function(healStack) {
* Disconnects the block and moves it to a new location.
* @param {boolean} healStack Whether or not to heal the stack after
* disconnecting.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at mouse down, in pixel units.
* @protected
*/
Blockly.BlockDragger.prototype.disconnectBlock_ = function(
BlockDragger.prototype.disconnectBlock_ = function(
healStack, currentDragDeltaXY) {
this.draggingBlock_.unplug(healStack);
var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
var newLoc = Blockly.utils.Coordinate.sum(this.startXY_, delta);
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
const newLoc = Coordinate.sum(this.startXY_, delta);
this.draggingBlock_.translate(newLoc.x, newLoc.y);
Blockly.blockAnimations.disconnectUiEffect(this.draggingBlock_);
blockAnimation.disconnectUiEffect(this.draggingBlock_);
this.draggedConnectionManager_.updateAvailableConnections();
};
@@ -210,31 +211,31 @@ Blockly.BlockDragger.prototype.disconnectBlock_ = function(
* Fire a UI event at the start of a block drag.
* @protected
*/
Blockly.BlockDragger.prototype.fireDragStartEvent_ = function() {
var event = new (Blockly.Events.get(Blockly.Events.BLOCK_DRAG))(
BlockDragger.prototype.fireDragStartEvent_ = function() {
const event = new (events.get(events.BLOCK_DRAG))(
this.draggingBlock_, true, this.draggingBlock_.getDescendants(false));
Blockly.Events.fire(event);
events.fire(event);
};
/**
* Execute a step of block dragging, based on the given event. Update the
* display accordingly.
* @param {!Event} e The most recent move event.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at the start of the drag, in pixel units.
* @public
*/
Blockly.BlockDragger.prototype.drag = function(e, currentDragDeltaXY) {
var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
var newLoc = Blockly.utils.Coordinate.sum(this.startXY_, delta);
BlockDragger.prototype.drag = function(e, currentDragDeltaXY) {
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
const newLoc = Coordinate.sum(this.startXY_, delta);
this.draggingBlock_.moveDuringDrag(newLoc);
this.dragIcons_(delta);
var oldDragTarget = this.dragTarget_;
const oldDragTarget = this.dragTarget_;
this.dragTarget_ = this.workspace_.getDragTarget(e);
this.draggedConnectionManager_.update(delta, this.dragTarget_);
var oldWouldDeleteBlock = this.wouldDeleteBlock_;
const oldWouldDeleteBlock = this.wouldDeleteBlock_;
this.wouldDeleteBlock_ = this.draggedConnectionManager_.wouldDeleteBlock();
if (oldWouldDeleteBlock != this.wouldDeleteBlock_) {
// Prevent unnecessary add/remove class calls.
@@ -253,26 +254,26 @@ Blockly.BlockDragger.prototype.drag = function(e, currentDragDeltaXY) {
/**
* Finish a block drag and put the block back on the workspace.
* @param {!Event} e The mouseup/touchend event.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at the start of the drag, in pixel units.
* @public
*/
Blockly.BlockDragger.prototype.endDrag = function(e, currentDragDeltaXY) {
BlockDragger.prototype.endDrag = function(e, currentDragDeltaXY) {
// Make sure internal state is fresh.
this.drag(e, currentDragDeltaXY);
this.dragIconData_ = [];
this.fireDragEndEvent_();
Blockly.utils.dom.stopTextWidthCache();
dom.stopTextWidthCache();
Blockly.blockAnimations.disconnectUiStop();
blockAnimation.disconnectUiStop();
var preventMove = !!this.dragTarget_ &&
const preventMove = !!this.dragTarget_ &&
this.dragTarget_.shouldPreventMove(this.draggingBlock_);
if (preventMove) {
var newLoc = this.startXY_;
} else {
var newValues = this.getNewLocationAfterDrag_(currentDragDeltaXY);
const newValues = this.getNewLocationAfterDrag_(currentDragDeltaXY);
var delta = newValues.delta;
var newLoc = newValues.newLocation;
}
@@ -282,7 +283,7 @@ Blockly.BlockDragger.prototype.endDrag = function(e, currentDragDeltaXY) {
this.dragTarget_.onDrop(this.draggingBlock_);
}
var deleted = this.maybeDeleteBlock_();
const deleted = this.maybeDeleteBlock_();
if (!deleted) {
// These are expensive and don't need to be done if we're deleting.
this.draggingBlock_.setDragging(false);
@@ -299,25 +300,23 @@ Blockly.BlockDragger.prototype.endDrag = function(e, currentDragDeltaXY) {
}
this.workspace_.setResizesEnabled(true);
Blockly.Events.setGroup(false);
events.setGroup(false);
};
/**
* Calculates the drag delta and new location values after a block is dragged.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the start of the drag, in pixel units.
* @return {{delta: !Blockly.utils.Coordinate, newLocation:
* !Blockly.utils.Coordinate}} New location after drag. delta is in
* @return {{delta: !Coordinate, newLocation:
* !Coordinate}} New location after drag. delta is in
* workspace units. newLocation is the new coordinate where the block should
* end up.
* @protected
*/
Blockly.BlockDragger.prototype.getNewLocationAfterDrag_ = function(
currentDragDeltaXY) {
var newValues = {};
BlockDragger.prototype.getNewLocationAfterDrag_ = function(currentDragDeltaXY) {
const newValues = {};
newValues.delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
newValues.newLocation =
Blockly.utils.Coordinate.sum(this.startXY_, newValues.delta);
newValues.newLocation = Coordinate.sum(this.startXY_, newValues.delta);
return newValues;
};
@@ -328,7 +327,7 @@ Blockly.BlockDragger.prototype.getNewLocationAfterDrag_ = function(
* @return {boolean} True if the block was deleted.
* @protected
*/
Blockly.BlockDragger.prototype.maybeDeleteBlock_ = function() {
BlockDragger.prototype.maybeDeleteBlock_ = function() {
if (this.wouldDeleteBlock_) {
// Fire a move event, so we know where to go back to for an undo.
this.fireMoveEvent_();
@@ -341,11 +340,11 @@ Blockly.BlockDragger.prototype.maybeDeleteBlock_ = function() {
/**
* Updates the necessary information to place a block at a certain location.
* @param {!Blockly.utils.Coordinate} delta The change in location from where
* @param {!Coordinate} delta The change in location from where
* the block started the drag to where it ended the drag.
* @protected
*/
Blockly.BlockDragger.prototype.updateBlockAfterMove_ = function(delta) {
BlockDragger.prototype.updateBlockAfterMove_ = function(delta) {
this.draggingBlock_.moveConnections(delta.x, delta.y);
this.fireMoveEvent_();
if (this.draggedConnectionManager_.wouldConnectBlock()) {
@@ -361,10 +360,10 @@ Blockly.BlockDragger.prototype.updateBlockAfterMove_ = function(delta) {
* Fire a UI event at the end of a block drag.
* @protected
*/
Blockly.BlockDragger.prototype.fireDragEndEvent_ = function() {
var event = new (Blockly.Events.get(Blockly.Events.BLOCK_DRAG))(
BlockDragger.prototype.fireDragEndEvent_ = function() {
const event = new (events.get(events.BLOCK_DRAG))(
this.draggingBlock_, false, this.draggingBlock_.getDescendants(false));
Blockly.Events.fire(event);
events.fire(event);
};
/**
@@ -374,12 +373,12 @@ Blockly.BlockDragger.prototype.fireDragEndEvent_ = function() {
* @param {boolean} isEnd True if we are at the end of a drag, false otherwise.
* @protected
*/
Blockly.BlockDragger.prototype.updateToolboxStyle_ = function(isEnd) {
var toolbox = this.workspace_.getToolbox();
BlockDragger.prototype.updateToolboxStyle_ = function(isEnd) {
const toolbox = this.workspace_.getToolbox();
if (toolbox) {
var style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' :
'blocklyToolboxGrab';
const style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' :
'blocklyToolboxGrab';
if (isEnd && typeof toolbox.removeStyle == 'function') {
toolbox.removeStyle(style);
@@ -394,12 +393,11 @@ Blockly.BlockDragger.prototype.updateToolboxStyle_ = function(isEnd) {
* Fire a move event at the end of a block drag.
* @protected
*/
Blockly.BlockDragger.prototype.fireMoveEvent_ = function() {
var event =
new (Blockly.Events.get(Blockly.Events.BLOCK_MOVE))(this.draggingBlock_);
BlockDragger.prototype.fireMoveEvent_ = function() {
const event = new (events.get(events.BLOCK_MOVE))(this.draggingBlock_);
event.oldCoordinate = this.startXY_;
event.recordNew();
Blockly.Events.fire(event);
events.fire(event);
};
/**
@@ -407,7 +405,7 @@ Blockly.BlockDragger.prototype.fireMoveEvent_ = function() {
* dragging block would be deleted if released immediately.
* @protected
*/
Blockly.BlockDragger.prototype.updateCursorDuringBlockDrag_ = function() {
BlockDragger.prototype.updateCursorDuringBlockDrag_ = function() {
this.draggingBlock_.setDeleteStyle(this.wouldDeleteBlock_);
};
@@ -416,14 +414,14 @@ Blockly.BlockDragger.prototype.updateCursorDuringBlockDrag_ = function() {
* correction for mutator workspaces.
* This function does not consider differing origins. It simply scales the
* input's x and y values.
* @param {!Blockly.utils.Coordinate} pixelCoord A coordinate with x and y
* @param {!Coordinate} pixelCoord A coordinate with x and y
* values in CSS pixel units.
* @return {!Blockly.utils.Coordinate} The input coordinate divided by the
* @return {!Coordinate} The input coordinate divided by the
* workspace scale.
* @protected
*/
Blockly.BlockDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
var result = new Blockly.utils.Coordinate(
BlockDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
const result = new Coordinate(
pixelCoord.x / this.workspace_.scale,
pixelCoord.y / this.workspace_.scale);
if (this.workspace_.isMutator) {
@@ -431,7 +429,7 @@ Blockly.BlockDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
// oddities in our rendering optimizations. The actual scale is the same as
// the scale on the parent workspace.
// Fix that for dragging.
var mainScale = this.workspace_.options.parentWorkspace.scale;
const mainScale = this.workspace_.options.parentWorkspace.scale;
result.scale(1 / mainScale);
}
return result;
@@ -439,26 +437,26 @@ Blockly.BlockDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
/**
* Move all of the icons connected to this drag.
* @param {!Blockly.utils.Coordinate} dxy How far to move the icons from their
* @param {!Coordinate} dxy How far to move the icons from their
* original positions, in workspace units.
* @protected
*/
Blockly.BlockDragger.prototype.dragIcons_ = function(dxy) {
BlockDragger.prototype.dragIcons_ = function(dxy) {
// Moving icons moves their associated bubbles.
for (var i = 0; i < this.dragIconData_.length; i++) {
var data = this.dragIconData_[i];
data.icon.setIconLocation(Blockly.utils.Coordinate.sum(data.location, dxy));
for (let i = 0; i < this.dragIconData_.length; i++) {
const data = this.dragIconData_[i];
data.icon.setIconLocation(Coordinate.sum(data.location, dxy));
}
};
/**
* Get a list of the insertion markers that currently exist. Drags have 0, 1,
* or 2 insertion markers.
* @return {!Array<!Blockly.BlockSvg>} A possibly empty list of insertion
* @return {!Array<!BlockSvg>} A possibly empty list of insertion
* marker blocks.
* @public
*/
Blockly.BlockDragger.prototype.getInsertionMarkers = function() {
BlockDragger.prototype.getInsertionMarkers = function() {
// No insertion markers with the old style of dragged connection managers.
if (this.draggedConnectionManager_ &&
this.draggedConnectionManager_.getInsertionMarkers) {
@@ -467,6 +465,6 @@ Blockly.BlockDragger.prototype.getInsertionMarkers = function() {
return [];
};
Blockly.registry.register(
Blockly.registry.Type.BLOCK_DRAGGER, Blockly.registry.DEFAULT,
Blockly.BlockDragger);
registry.register(registry.Type.BLOCK_DRAGGER, registry.DEFAULT, BlockDragger);
exports = BlockDragger;

View File

@@ -10,15 +10,16 @@
*/
'use strict';
goog.provide('Blockly.FieldImage');
goog.module('Blockly.FieldImage');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.utils');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Size');
goog.require('Blockly.utils.Svg');
const Field = goog.require('Blockly.Field');
const Size = goog.require('Blockly.utils.Size');
const Svg = goog.require('Blockly.utils.Svg');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const {createSvgElement, XLINK_NS} = goog.require('Blockly.utils.dom');
const {inherits} = goog.require('Blockly.utils.object');
const {replaceMessageReferences} = goog.require('Blockly.utils');
/**
@@ -27,32 +28,35 @@ goog.require('Blockly.utils.Svg');
* @param {!(string|number)} width Width of the image.
* @param {!(string|number)} height Height of the image.
* @param {string=} opt_alt Optional alt text for when block is collapsed.
* @param {function(!Blockly.FieldImage)=} opt_onClick Optional function to be
* @param {function(!FieldImage)=} opt_onClick Optional function to be
* called when the image is clicked. If opt_onClick is defined, opt_alt must
* also be defined.
* @param {boolean=} opt_flipRtl Whether to flip the icon in RTL.
* @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/image#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/image#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.Field}
* @extends {Field}
* @constructor
*/
Blockly.FieldImage = function(src, width, height,
opt_alt, opt_onClick, opt_flipRtl, opt_config) {
const FieldImage = function(
src, width, height, opt_alt, opt_onClick, opt_flipRtl, opt_config) {
// Return early.
if (!src) {
throw Error('Src value of an image field is required');
}
src = Blockly.utils.replaceMessageReferences(src);
var imageHeight = Number(Blockly.utils.replaceMessageReferences(height));
var imageWidth = Number(Blockly.utils.replaceMessageReferences(width));
src = replaceMessageReferences(src);
const imageHeight = Number(replaceMessageReferences(height));
const imageWidth = Number(replaceMessageReferences(width));
if (isNaN(imageHeight) || isNaN(imageWidth)) {
throw Error('Height and width values of an image field must cast to' +
' numbers.');
throw Error(
'Height and width values of an image field must cast to' +
' numbers.');
}
if (imageHeight <= 0 || imageWidth <= 0) {
throw Error('Height and width values of an image field must be greater' +
' than 0.');
throw Error(
'Height and width values of an image field must be greater' +
' than 0.');
}
// Initialize configurable properties.
@@ -70,23 +74,21 @@ Blockly.FieldImage = function(src, width, height,
*/
this.altText_ = '';
Blockly.FieldImage.superClass_.constructor.call(
this, src, null, opt_config);
FieldImage.superClass_.constructor.call(this, src, null, opt_config);
if (!opt_config) { // If the config wasn't passed, do old configuration.
this.flipRtl_ = !!opt_flipRtl;
this.altText_ = Blockly.utils.replaceMessageReferences(opt_alt) || '';
this.altText_ = replaceMessageReferences(opt_alt) || '';
}
// Initialize other properties.
/**
* The size of the area rendered by the field.
* @type {Blockly.utils.Size}
* @type {Size}
* @protected
* @override
*/
this.size_ = new Blockly.utils.Size(imageWidth,
imageHeight + Blockly.FieldImage.Y_PADDING);
this.size_ = new Size(imageWidth, imageHeight + FieldImage.Y_PADDING);
/**
* Store the image height, since it is different from the field height.
@@ -97,7 +99,7 @@ Blockly.FieldImage = function(src, width, height,
/**
* The function to be called when this field is clicked.
* @type {?function(!Blockly.FieldImage)}
* @type {?function(!FieldImage)}
* @private
*/
this.clickHandler_ = null;
@@ -113,29 +115,30 @@ Blockly.FieldImage = function(src, width, height,
*/
this.imageElement_ = null;
};
Blockly.utils.object.inherits(Blockly.FieldImage, Blockly.Field);
inherits(FieldImage, Field);
/**
* The default value for this field.
* @type {*}
* @protected
*/
Blockly.FieldImage.prototype.DEFAULT_VALUE = '';
FieldImage.prototype.DEFAULT_VALUE = '';
/**
* Construct a FieldImage from a JSON arg object,
* dereferencing any string table references.
* @param {!Object} options A JSON object with options (src, width, height,
* alt, and flipRtl).
* @return {!Blockly.FieldImage} The new field instance.
* @return {!FieldImage} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldImage.fromJson = function(options) {
FieldImage.fromJson = function(options) {
// `this` might be a subclass of FieldImage if that class doesn't override
// the static fromJson method.
return new this(options['src'], options['width'], options['height'],
undefined, undefined, undefined, options);
return new this(
options['src'], options['width'], options['height'], undefined, undefined,
undefined, options);
};
/**
@@ -144,14 +147,14 @@ Blockly.FieldImage.fromJson = function(options) {
* @type {number}
* @private
*/
Blockly.FieldImage.Y_PADDING = 1;
FieldImage.Y_PADDING = 1;
/**
* Editable fields usually show some sort of UI indicating they are
* editable. This field should not.
* @type {boolean}
*/
Blockly.FieldImage.prototype.EDITABLE = false;
FieldImage.prototype.EDITABLE = false;
/**
* Used to tell if the field needs to be rendered the next time the block is
@@ -160,7 +163,7 @@ Blockly.FieldImage.prototype.EDITABLE = false;
* @type {boolean}
* @protected
*/
Blockly.FieldImage.prototype.isDirty_ = false;
FieldImage.prototype.isDirty_ = false;
/**
* Configure the field based on the given map of options.
@@ -168,27 +171,26 @@ Blockly.FieldImage.prototype.isDirty_ = false;
* @protected
* @override
*/
Blockly.FieldImage.prototype.configure_ = function(config) {
Blockly.FieldImage.superClass_.configure_.call(this, config);
FieldImage.prototype.configure_ = function(config) {
FieldImage.superClass_.configure_.call(this, config);
this.flipRtl_ = !!config['flipRtl'];
this.altText_ = Blockly.utils.replaceMessageReferences(config['alt']) || '';
this.altText_ = replaceMessageReferences(config['alt']) || '';
};
/**
* Create the block UI for this image.
* @package
*/
Blockly.FieldImage.prototype.initView = function() {
this.imageElement_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.IMAGE,
{
FieldImage.prototype.initView = function() {
this.imageElement_ = createSvgElement(
Svg.IMAGE, {
'height': this.imageHeight_ + 'px',
'width': this.size_.width + 'px',
'alt': this.altText_
},
this.fieldGroup_);
this.imageElement_.setAttributeNS(Blockly.utils.dom.XLINK_NS,
'xlink:href', /** @type {string} */ (this.value_));
this.imageElement_.setAttributeNS(
XLINK_NS, 'xlink:href', /** @type {string} */ (this.value_));
if (this.clickHandler_) {
this.imageElement_.style.cursor = 'pointer';
@@ -198,7 +200,7 @@ Blockly.FieldImage.prototype.initView = function() {
/**
* @override
*/
Blockly.FieldImage.prototype.updateSize_ = function() {
FieldImage.prototype.updateSize_ = function() {
// NOP
};
@@ -208,7 +210,7 @@ Blockly.FieldImage.prototype.updateSize_ = function() {
* @return {?string} A string, or null if invalid.
* @protected
*/
Blockly.FieldImage.prototype.doClassValidation_ = function(opt_newValue) {
FieldImage.prototype.doClassValidation_ = function(opt_newValue) {
if (typeof opt_newValue != 'string') {
return null;
}
@@ -221,11 +223,11 @@ Blockly.FieldImage.prototype.doClassValidation_ = function(opt_newValue) {
* that this is a string.
* @protected
*/
Blockly.FieldImage.prototype.doValueUpdate_ = function(newValue) {
FieldImage.prototype.doValueUpdate_ = function(newValue) {
this.value_ = newValue;
if (this.imageElement_) {
this.imageElement_.setAttributeNS(Blockly.utils.dom.XLINK_NS,
'xlink:href', String(this.value_));
this.imageElement_.setAttributeNS(
XLINK_NS, 'xlink:href', String(this.value_));
}
};
@@ -234,7 +236,7 @@ Blockly.FieldImage.prototype.doValueUpdate_ = function(newValue) {
* @return {boolean} True if we should flip in RTL.
* @override
*/
Blockly.FieldImage.prototype.getFlipRtl = function() {
FieldImage.prototype.getFlipRtl = function() {
return this.flipRtl_;
};
@@ -243,7 +245,7 @@ Blockly.FieldImage.prototype.getFlipRtl = function() {
* @param {?string} alt New alt text.
* @public
*/
Blockly.FieldImage.prototype.setAlt = function(alt) {
FieldImage.prototype.setAlt = function(alt) {
if (alt == this.altText_) {
return;
}
@@ -258,7 +260,7 @@ Blockly.FieldImage.prototype.setAlt = function(alt) {
* call the handler.
* @protected
*/
Blockly.FieldImage.prototype.showEditor_ = function() {
FieldImage.prototype.showEditor_ = function() {
if (this.clickHandler_) {
this.clickHandler_(this);
}
@@ -266,10 +268,10 @@ Blockly.FieldImage.prototype.showEditor_ = function() {
/**
* Set the function that is called when this image is clicked.
* @param {?function(!Blockly.FieldImage)} func The function that is called
* @param {?function(!FieldImage)} func The function that is called
* when the image is clicked, or null to remove.
*/
Blockly.FieldImage.prototype.setOnClickHandler = function(func) {
FieldImage.prototype.setOnClickHandler = function(func) {
this.clickHandler_ = func;
};
@@ -281,8 +283,10 @@ Blockly.FieldImage.prototype.setOnClickHandler = function(func) {
* @protected
* @override
*/
Blockly.FieldImage.prototype.getText_ = function() {
FieldImage.prototype.getText_ = function() {
return this.altText_;
};
Blockly.fieldRegistry.register('field_image', Blockly.FieldImage);
fieldRegistry.register('field_image', FieldImage);
exports = FieldImage;

View File

@@ -11,13 +11,14 @@
*/
'use strict';
goog.provide('Blockly.FieldLabel');
goog.module('Blockly.FieldLabel');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.utils');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
const Field = goog.require('Blockly.Field');
const dom = goog.require('Blockly.utils.dom');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const {inherits} = goog.require('Blockly.utils.object');
const {replaceMessageReferences} = goog.require('Blockly.utils');
/**
@@ -26,12 +27,13 @@ goog.require('Blockly.utils.object');
* string. Defaults to an empty string if null or undefined.
* @param {string=} opt_class Optional CSS class for the field's text.
* @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/label#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/label#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.Field}
* @extends {Field}
* @constructor
*/
Blockly.FieldLabel = function(opt_value, opt_class, opt_config) {
const FieldLabel = function(opt_value, opt_class, opt_config) {
/**
* The html class name to use for this field.
* @type {?string}
@@ -39,32 +41,31 @@ Blockly.FieldLabel = function(opt_value, opt_class, opt_config) {
*/
this.class_ = null;
Blockly.FieldLabel.superClass_.constructor.call(
this, opt_value, null, opt_config);
FieldLabel.superClass_.constructor.call(this, opt_value, null, opt_config);
if (!opt_config) { // If the config was not passed use old configuration.
this.class_ = opt_class || null;
}
};
Blockly.utils.object.inherits(Blockly.FieldLabel, Blockly.Field);
inherits(FieldLabel, Field);
/**
* The default value for this field.
* @type {*}
* @protected
*/
Blockly.FieldLabel.prototype.DEFAULT_VALUE = '';
FieldLabel.prototype.DEFAULT_VALUE = '';
/**
* Construct a FieldLabel from a JSON arg object,
* dereferencing any string table references.
* @param {!Object} options A JSON object with options (text, and class).
* @return {!Blockly.FieldLabel} The new field instance.
* @return {!FieldLabel} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldLabel.fromJson = function(options) {
var text = Blockly.utils.replaceMessageReferences(options['text']);
FieldLabel.fromJson = function(options) {
const text = replaceMessageReferences(options['text']);
// `this` might be a subclass of FieldLabel if that class doesn't override
// the static fromJson method.
return new this(text, undefined, options);
@@ -75,13 +76,13 @@ Blockly.FieldLabel.fromJson = function(options) {
* editable. This field should not.
* @type {boolean}
*/
Blockly.FieldLabel.prototype.EDITABLE = false;
FieldLabel.prototype.EDITABLE = false;
/**
* @override
*/
Blockly.FieldLabel.prototype.configure_ = function(config) {
Blockly.FieldLabel.superClass_.configure_.call(this, config);
FieldLabel.prototype.configure_ = function(config) {
FieldLabel.superClass_.configure_.call(this, config);
this.class_ = config['class'];
};
@@ -89,10 +90,10 @@ Blockly.FieldLabel.prototype.configure_ = function(config) {
* Create block UI for this label.
* @package
*/
Blockly.FieldLabel.prototype.initView = function() {
FieldLabel.prototype.initView = function() {
this.createTextElement_();
if (this.class_) {
Blockly.utils.dom.addClass(
dom.addClass(
/** @type {!SVGTextElement} */ (this.textElement_), this.class_);
}
};
@@ -103,7 +104,7 @@ Blockly.FieldLabel.prototype.initView = function() {
* @return {?string} A valid string, or null if invalid.
* @protected
*/
Blockly.FieldLabel.prototype.doClassValidation_ = function(opt_newValue) {
FieldLabel.prototype.doClassValidation_ = function(opt_newValue) {
if (opt_newValue === null || opt_newValue === undefined) {
return null;
}
@@ -114,18 +115,20 @@ Blockly.FieldLabel.prototype.doClassValidation_ = function(opt_newValue) {
* Set the CSS class applied to the field's textElement_.
* @param {?string} cssClass The new CSS class name, or null to remove.
*/
Blockly.FieldLabel.prototype.setClass = function(cssClass) {
FieldLabel.prototype.setClass = function(cssClass) {
if (this.textElement_) {
// This check isn't necessary, but it's faster than letting removeClass
// figure it out.
if (this.class_) {
Blockly.utils.dom.removeClass(this.textElement_, this.class_);
dom.removeClass(this.textElement_, this.class_);
}
if (cssClass) {
Blockly.utils.dom.addClass(this.textElement_, cssClass);
dom.addClass(this.textElement_, cssClass);
}
}
this.class_ = cssClass;
};
Blockly.fieldRegistry.register('field_label', Blockly.FieldLabel);
fieldRegistry.register('field_label', FieldLabel);
exports = FieldLabel;

View File

@@ -11,12 +11,13 @@
*/
'use strict';
goog.provide('Blockly.FieldLabelSerializable');
goog.module('Blockly.FieldLabelSerializable');
goog.module.declareLegacyNamespace();
goog.require('Blockly.FieldLabel');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.utils');
goog.require('Blockly.utils.object');
const FieldLabel = goog.require('Blockly.FieldLabel');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const {inherits} = goog.require('Blockly.utils.object');
const {replaceMessageReferences} = goog.require('Blockly.utils');
/**
@@ -25,29 +26,29 @@ goog.require('Blockly.utils.object');
* string. Defaults to an empty string if null or undefined.
* @param {string=} opt_class Optional CSS class for the field's text.
* @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/label-serializable#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/label-serializable#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.FieldLabel}
* @extends {FieldLabel}
* @constructor
*
*/
Blockly.FieldLabelSerializable = function(opt_value, opt_class, opt_config) {
Blockly.FieldLabelSerializable.superClass_.constructor.call(
const FieldLabelSerializable = function(opt_value, opt_class, opt_config) {
FieldLabelSerializable.superClass_.constructor.call(
this, opt_value, opt_class, opt_config);
};
Blockly.utils.object.inherits(Blockly.FieldLabelSerializable,
Blockly.FieldLabel);
inherits(FieldLabelSerializable, FieldLabel);
/**
* Construct a FieldLabelSerializable from a JSON arg object,
* dereferencing any string table references.
* @param {!Object} options A JSON object with options (text, and class).
* @return {!Blockly.FieldLabelSerializable} The new field instance.
* @return {!FieldLabelSerializable} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldLabelSerializable.fromJson = function(options) {
var text = Blockly.utils.replaceMessageReferences(options['text']);
FieldLabelSerializable.fromJson = function(options) {
const text = replaceMessageReferences(options['text']);
// `this` might be a subclass of FieldLabelSerializable if that class doesn't
// override the static fromJson method.
return new this(text, undefined, options);
@@ -58,14 +59,15 @@ Blockly.FieldLabelSerializable.fromJson = function(options) {
* editable. This field should not.
* @type {boolean}
*/
Blockly.FieldLabelSerializable.prototype.EDITABLE = false;
FieldLabelSerializable.prototype.EDITABLE = false;
/**
* Serializable fields are saved by the XML renderer, non-serializable fields
* are not. This field should be serialized, but only edited programmatically.
* @type {boolean}
*/
Blockly.FieldLabelSerializable.prototype.SERIALIZABLE = true;
FieldLabelSerializable.prototype.SERIALIZABLE = true;
Blockly.fieldRegistry.register(
'field_label_serializable', Blockly.FieldLabelSerializable);
fieldRegistry.register('field_label_serializable', FieldLabelSerializable);
exports = FieldLabelSerializable;

View File

@@ -12,20 +12,21 @@
*/
'use strict';
goog.provide('Blockly.FieldMultilineInput');
goog.module('Blockly.FieldMultilineInput');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Css');
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.FieldTextInput');
goog.require('Blockly.utils');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.KeyCodes');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.userAgent');
goog.require('Blockly.WidgetDiv');
const Css = goog.require('Blockly.Css');
const Field = goog.require('Blockly.Field');
const FieldTextInput = goog.require('Blockly.FieldTextInput');
const KeyCodes = goog.require('Blockly.utils.KeyCodes');
const Svg = goog.require('Blockly.utils.Svg');
const WidgetDiv = goog.require('Blockly.WidgetDiv');
const aria = goog.require('Blockly.utils.aria');
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 {replaceMessageReferences} = goog.require('Blockly.utils');
/**
@@ -37,14 +38,15 @@ goog.require('Blockly.WidgetDiv');
* text as an argument and returns either the accepted text, a replacement
* text, 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/multiline-text-input#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/multiline-text-input#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.FieldTextInput}
* @extends {FieldTextInput}
* @constructor
*/
Blockly.FieldMultilineInput = function(opt_value, opt_validator, opt_config) {
Blockly.FieldMultilineInput.superClass_.constructor.call(this,
opt_value, opt_validator, opt_config);
const FieldMultilineInput = function(opt_value, opt_validator, opt_config) {
FieldMultilineInput.superClass_.constructor.call(
this, opt_value, opt_validator, opt_config);
/**
* The SVG group element that will contain a text element for each text row
@@ -68,14 +70,13 @@ Blockly.FieldMultilineInput = function(opt_value, opt_validator, opt_config) {
*/
this.isOverflowedY_ = false;
};
Blockly.utils.object.inherits(Blockly.FieldMultilineInput,
Blockly.FieldTextInput);
inherits(FieldMultilineInput, FieldTextInput);
/**
* @override
*/
Blockly.FieldMultilineInput.prototype.configure_ = function(config) {
Blockly.FieldMultilineInput.superClass_.configure_.call(this, config);
FieldMultilineInput.prototype.configure_ = function(config) {
FieldMultilineInput.superClass_.configure_.call(this, config);
config.maxLines && this.setMaxLines(config.maxLines);
};
@@ -83,12 +84,12 @@ Blockly.FieldMultilineInput.prototype.configure_ = function(config) {
* Construct a FieldMultilineInput from a JSON arg object,
* dereferencing any string table references.
* @param {!Object} options A JSON object with options (text, and spellcheck).
* @return {!Blockly.FieldMultilineInput} The new field instance.
* @return {!FieldMultilineInput} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldMultilineInput.fromJson = function(options) {
var text = Blockly.utils.replaceMessageReferences(options['text']);
FieldMultilineInput.fromJson = function(options) {
const text = replaceMessageReferences(options['text']);
// `this` might be a subclass of FieldMultilineInput if that class doesn't
// override the static fromJson method.
return new this(text, undefined, options);
@@ -101,7 +102,7 @@ Blockly.FieldMultilineInput.fromJson = function(options) {
* @return {!Element} The element containing info about the field's state.
* @package
*/
Blockly.FieldMultilineInput.prototype.toXml = function(fieldElement) {
FieldMultilineInput.prototype.toXml = function(fieldElement) {
// Replace '\n' characters with HTML-escaped equivalent '&#10'. This is
// needed so the plain-text representation of the XML produced by
// `Blockly.Xml.domToText` will appear on a single line (this is a limitation
@@ -117,7 +118,7 @@ Blockly.FieldMultilineInput.prototype.toXml = function(fieldElement) {
* field's state.
* @package
*/
Blockly.FieldMultilineInput.prototype.fromXml = function(fieldElement) {
FieldMultilineInput.prototype.fromXml = function(fieldElement) {
this.setValue(fieldElement.textContent.replace(/&#10;/g, '\n'));
};
@@ -125,12 +126,13 @@ Blockly.FieldMultilineInput.prototype.fromXml = function(fieldElement) {
* Create the block UI for this field.
* @package
*/
Blockly.FieldMultilineInput.prototype.initView = function() {
FieldMultilineInput.prototype.initView = function() {
this.createBorderRect_();
this.textGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G, {
this.textGroup_ = dom.createSvgElement(
Svg.G, {
'class': 'blocklyEditableText',
}, this.fieldGroup_);
},
this.fieldGroup_);
};
/**
@@ -140,17 +142,18 @@ Blockly.FieldMultilineInput.prototype.initView = function() {
* @protected
* @override
*/
Blockly.FieldMultilineInput.prototype.getDisplayText_ = function() {
var textLines = this.getText();
FieldMultilineInput.prototype.getDisplayText_ = function() {
let textLines = this.getText();
if (!textLines) {
// Prevent the field from disappearing if empty.
return Blockly.Field.NBSP;
return Field.NBSP;
}
var lines = textLines.split('\n');
const lines = textLines.split('\n');
textLines = '';
var displayLinesNumber = this.isOverflowedY_ ? this.maxLines_ : lines.length;
for (var i = 0; i < displayLinesNumber; i++) {
var text = lines[i];
const displayLinesNumber =
this.isOverflowedY_ ? this.maxLines_ : lines.length;
for (let i = 0; i < displayLinesNumber; i++) {
let text = lines[i];
if (text.length > this.maxDisplayLength) {
// Truncate displayed string and add an ellipsis ('...').
text = text.substring(0, this.maxDisplayLength - 4) + '...';
@@ -158,7 +161,7 @@ Blockly.FieldMultilineInput.prototype.getDisplayText_ = function() {
text = text.substring(0, text.length - 3) + '...';
}
// Replace whitespace with non-breaking spaces so the text doesn't collapse.
text = text.replace(/\s/g, Blockly.Field.NBSP);
text = text.replace(/\s/g, Field.NBSP);
textLines += text;
if (i !== displayLinesNumber - 1) {
@@ -181,8 +184,8 @@ Blockly.FieldMultilineInput.prototype.getDisplayText_ = function() {
* that this is a string.
* @protected
*/
Blockly.FieldMultilineInput.prototype.doValueUpdate_ = function(newValue) {
Blockly.FieldMultilineInput.superClass_.doValueUpdate_.call(this, newValue);
FieldMultilineInput.prototype.doValueUpdate_ = function(newValue) {
FieldMultilineInput.superClass_.doValueUpdate_.call(this, newValue);
this.isOverflowedY_ = this.value_.split('\n').length > this.maxLines_;
};
@@ -190,36 +193,37 @@ Blockly.FieldMultilineInput.prototype.doValueUpdate_ = function(newValue) {
* Updates the text of the textElement.
* @protected
*/
Blockly.FieldMultilineInput.prototype.render_ = function() {
FieldMultilineInput.prototype.render_ = function() {
// Remove all text group children.
var currentChild;
let currentChild;
while ((currentChild = this.textGroup_.firstChild)) {
this.textGroup_.removeChild(currentChild);
}
// Add in text elements into the group.
var lines = this.getDisplayText_().split('\n');
var y = 0;
for (var i = 0; i < lines.length; i++) {
var lineHeight = this.getConstants().FIELD_TEXT_HEIGHT +
const lines = this.getDisplayText_().split('\n');
let y = 0;
for (let i = 0; i < lines.length; i++) {
const lineHeight = this.getConstants().FIELD_TEXT_HEIGHT +
this.getConstants().FIELD_BORDER_RECT_Y_PADDING;
var span = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.TEXT, {
const span = dom.createSvgElement(
Svg.TEXT, {
'class': 'blocklyText blocklyMultilineText',
x: this.getConstants().FIELD_BORDER_RECT_X_PADDING,
y: y + this.getConstants().FIELD_BORDER_RECT_Y_PADDING,
dy: this.getConstants().FIELD_TEXT_BASELINE
}, this.textGroup_);
},
this.textGroup_);
span.appendChild(document.createTextNode(lines[i]));
y += lineHeight;
}
if (this.isBeingEdited_) {
var htmlInput = /** @type {!HTMLElement} */(this.htmlInput_);
var htmlInput = /** @type {!HTMLElement} */ (this.htmlInput_);
if (this.isOverflowedY_) {
Blockly.utils.dom.addClass(htmlInput, 'blocklyHtmlTextAreaInputOverflowedY');
dom.addClass(htmlInput, 'blocklyHtmlTextAreaInputOverflowedY');
} else {
Blockly.utils.dom.removeClass(htmlInput, 'blocklyHtmlTextAreaInputOverflowedY');
dom.removeClass(htmlInput, 'blocklyHtmlTextAreaInputOverflowedY');
}
}
@@ -234,15 +238,13 @@ Blockly.FieldMultilineInput.prototype.render_ = function() {
} else {
this.resizeEditor_();
}
var htmlInput = /** @type {!HTMLElement} */(this.htmlInput_);
var 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);
}
}
};
@@ -251,13 +253,13 @@ Blockly.FieldMultilineInput.prototype.render_ = function() {
* Updates the size of the field based on the text.
* @protected
*/
Blockly.FieldMultilineInput.prototype.updateSize_ = function() {
var nodes = this.textGroup_.childNodes;
var totalWidth = 0;
var totalHeight = 0;
FieldMultilineInput.prototype.updateSize_ = function() {
const nodes = this.textGroup_.childNodes;
let totalWidth = 0;
let totalHeight = 0;
for (var i = 0; i < nodes.length; i++) {
var tspan = /** @type {!Element} */ (nodes[i]);
var textWidth = Blockly.utils.dom.getTextWidth(tspan);
const tspan = /** @type {!Element} */ (nodes[i]);
const textWidth = dom.getTextWidth(tspan);
if (textWidth > totalWidth) {
totalWidth = textWidth;
}
@@ -270,26 +272,28 @@ Blockly.FieldMultilineInput.prototype.updateSize_ = function() {
// absolute longest line, even if it would be truncated after editing.
// Otherwise we would get wrong editor width when there are more
// lines than this.maxLines_.
var actualEditorLines = this.value_.split('\n');
var dummyTextElement = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.TEXT,{'class': 'blocklyText blocklyMultilineText'});
var fontSize = this.getConstants().FIELD_TEXT_FONTSIZE;
var fontWeight = this.getConstants().FIELD_TEXT_FONTWEIGHT;
var fontFamily = this.getConstants().FIELD_TEXT_FONTFAMILY;
const actualEditorLines = this.value_.split('\n');
const dummyTextElement = dom.createSvgElement(
Svg.TEXT, {'class': 'blocklyText blocklyMultilineText'});
const fontSize = this.getConstants().FIELD_TEXT_FONTSIZE;
const fontWeight = this.getConstants().FIELD_TEXT_FONTWEIGHT;
const fontFamily = this.getConstants().FIELD_TEXT_FONTFAMILY;
for (var i = 0; i < actualEditorLines.length; i++) {
if (actualEditorLines[i].length > this.maxDisplayLength) {
actualEditorLines[i] = actualEditorLines[i].substring(0, this.maxDisplayLength);
actualEditorLines[i] =
actualEditorLines[i].substring(0, this.maxDisplayLength);
}
dummyTextElement.textContent = actualEditorLines[i];
var lineWidth = Blockly.utils.dom.getFastTextWidth(
const lineWidth = dom.getFastTextWidth(
dummyTextElement, fontSize, fontWeight, fontFamily);
if (lineWidth > totalWidth) {
totalWidth = lineWidth;
}
}
var scrollbarWidth = this.htmlInput_.offsetWidth - this.htmlInput_.clientWidth;
const scrollbarWidth =
this.htmlInput_.offsetWidth - this.htmlInput_.clientWidth;
totalWidth += scrollbarWidth;
}
if (this.borderRect_) {
@@ -314,8 +318,9 @@ Blockly.FieldMultilineInput.prototype.updateSize_ = function() {
* focus. Defaults to false.
* @override
*/
Blockly.FieldMultilineInput.prototype.showEditor_ = function(_opt_e, opt_quietInput) {
Blockly.FieldMultilineInput.superClass_.showEditor_.call(this, _opt_e, opt_quietInput);
FieldMultilineInput.prototype.showEditor_ = function(_opt_e, opt_quietInput) {
FieldMultilineInput.superClass_.showEditor_.call(
this, _opt_e, opt_quietInput);
this.forceRerender();
};
@@ -324,24 +329,24 @@ Blockly.FieldMultilineInput.prototype.showEditor_ = function(_opt_e, opt_quietIn
* @return {!HTMLTextAreaElement} The newly created text input editor.
* @protected
*/
Blockly.FieldMultilineInput.prototype.widgetCreate_ = function() {
var div = Blockly.WidgetDiv.DIV;
var scale = this.workspace_.getScale();
FieldMultilineInput.prototype.widgetCreate_ = function() {
const div = WidgetDiv.DIV;
const scale = this.workspace_.getScale();
var htmlInput =
/** @type {HTMLTextAreaElement} */ (document.createElement('textarea'));
const htmlInput =
/** @type {HTMLTextAreaElement} */ (document.createElement('textarea'));
htmlInput.className = 'blocklyHtmlInput blocklyHtmlTextAreaInput';
htmlInput.setAttribute('spellcheck', this.spellcheck_);
var 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;
var borderRadius = (Blockly.FieldTextInput.BORDERRADIUS * scale) + 'px';
const borderRadius = (FieldTextInput.BORDERRADIUS * scale) + 'px';
htmlInput.style.borderRadius = borderRadius;
var paddingX = this.getConstants().FIELD_BORDER_RECT_X_PADDING * scale;
var paddingY = this.getConstants().FIELD_BORDER_RECT_Y_PADDING * scale / 2;
htmlInput.style.padding = paddingY + 'px ' + paddingX + 'px ' + paddingY +
'px ' + paddingX + 'px';
var lineHeight = this.getConstants().FIELD_TEXT_HEIGHT +
const paddingX = this.getConstants().FIELD_BORDER_RECT_X_PADDING * scale;
const paddingY = this.getConstants().FIELD_BORDER_RECT_Y_PADDING * scale / 2;
htmlInput.style.padding =
paddingY + 'px ' + paddingX + 'px ' + paddingY + 'px ' + paddingX + 'px';
const lineHeight = this.getConstants().FIELD_TEXT_HEIGHT +
this.getConstants().FIELD_BORDER_RECT_Y_PADDING;
htmlInput.style.lineHeight = (lineHeight * scale) + 'px';
@@ -350,7 +355,7 @@ Blockly.FieldMultilineInput.prototype.widgetCreate_ = function() {
htmlInput.value = htmlInput.defaultValue = this.getEditorText_(this.value_);
htmlInput.untypedDefaultValue_ = this.value_;
htmlInput.oldValue_ = null;
if (Blockly.utils.userAgent.GECKO) {
if (userAgent.GECKO) {
// In FF, ensure the browser reflows before resizing to avoid issue #2777.
setTimeout(this.resizeEditor_.bind(this), 0);
} else {
@@ -367,8 +372,9 @@ Blockly.FieldMultilineInput.prototype.widgetCreate_ = function() {
* @param {number} maxLines Defines the maximum number of lines allowed,
* before scrolling functionality is enabled.
*/
Blockly.FieldMultilineInput.prototype.setMaxLines = function(maxLines) {
if (typeof maxLines === 'number' && maxLines > 0 && maxLines !== this.maxLines_) {
FieldMultilineInput.prototype.setMaxLines = function(maxLines) {
if (typeof maxLines === 'number' && maxLines > 0 &&
maxLines !== this.maxLines_) {
this.maxLines_ = maxLines;
this.forceRerender();
}
@@ -378,7 +384,7 @@ Blockly.FieldMultilineInput.prototype.setMaxLines = function(maxLines) {
* Returns the maxLines config of this field.
* @return {number} The maxLines config value.
*/
Blockly.FieldMultilineInput.prototype.getMaxLines = function() {
FieldMultilineInput.prototype.getMaxLines = function() {
return this.maxLines_;
};
@@ -388,29 +394,29 @@ Blockly.FieldMultilineInput.prototype.getMaxLines = function() {
* @param {!Event} e Keyboard event.
* @protected
*/
Blockly.FieldMultilineInput.prototype.onHtmlInputKeyDown_ = function(e) {
if (e.keyCode !== Blockly.utils.KeyCodes.ENTER) {
Blockly.FieldMultilineInput.superClass_.onHtmlInputKeyDown_.call(this, e);
FieldMultilineInput.prototype.onHtmlInputKeyDown_ = function(e) {
if (e.keyCode !== KeyCodes.ENTER) {
FieldMultilineInput.superClass_.onHtmlInputKeyDown_.call(this, e);
}
};
/**
* CSS for multiline field. See css.js for use.
*/
Blockly.Css.register([
/* eslint-disable indent */
'.blocklyHtmlTextAreaInput {',
'font-family: monospace;',
'resize: none;',
'overflow: hidden;',
'height: 100%;',
'text-align: left;',
'}',
'.blocklyHtmlTextAreaInputOverflowedY {',
'overflow-y: scroll;',
'}'
/* eslint-enable indent */
Css.register([
`.blocklyHtmlTextAreaInput {
font-family: monospace;
resize: none;
overflow: hidden;
height: 100%;
text-align: left;
}`,
`.blocklyHtmlTextAreaInputOverflowedY {
overflow-y: scroll;
}`
]);
Blockly.fieldRegistry.register('field_multilinetext', Blockly.FieldMultilineInput);
fieldRegistry.register('field_multilinetext', FieldMultilineInput);
exports = FieldMultilineInput;

View File

@@ -10,27 +10,30 @@
*/
'use strict';
goog.provide('Blockly.FieldTextInput');
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');
/**
@@ -41,12 +44,13 @@ goog.requireType('Blockly.WorkspaceSvg');
* 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 {Blockly.Field}
* @extends {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,8 +58,8 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) {
*/
this.spellcheck_ = true;
Blockly.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.
@@ -65,14 +69,14 @@ Blockly.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;
@@ -86,30 +90,30 @@ Blockly.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(Blockly.FieldTextInput, Blockly.Field);
inherits(FieldTextInput, 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) {
var text = Blockly.utils.replaceMessageReferences(options['text']);
FieldTextInput.fromJson = function(options) {
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);
@@ -120,24 +124,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,17 +150,17 @@ 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.
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++) {
nFields ++;
for (let i = 0, input; (input = this.sourceBlock_.inputList[i]); i++) {
for (let j = 0; (input.fieldRow[j]); j++) {
nFields++;
}
if (input.connection) {
nConnections++;
@@ -184,7 +188,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,15 +204,16 @@ 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;
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()) {
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))(
this.sourceBlock_, 'field', this.name || null, oldValue, this.value_));
if (this.sourceBlock_ && Events.isEnabled()) {
Events.fire(new (Events.get(Events.BLOCK_CHANGE))(
this.sourceBlock_, 'field', this.name || null, oldValue,
this.value_));
}
}
};
@@ -221,7 +226,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,14 +239,14 @@ 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',
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);
}
}
};
@@ -251,21 +256,19 @@ 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_) {
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,
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);
}
}
};
@@ -274,7 +277,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,14 +295,11 @@ Blockly.FieldTextInput.prototype.setSpellcheck = function(check) {
* focus. Defaults to false.
* @protected
*/
Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e,
opt_quietInput) {
this.workspace_ =
(/** @type {!Blockly.BlockSvg} */ (this.sourceBlock_)).workspace;
var quietInput = opt_quietInput || false;
if (!quietInput && (Blockly.utils.userAgent.MOBILE ||
Blockly.utils.userAgent.ANDROID ||
Blockly.utils.userAgent.IPAD)) {
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)) {
this.showPromptEditor_();
} else {
this.showInlineEditor_(quietInput);
@@ -311,11 +311,10 @@ Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e,
* Mobile browsers have issues with in-line textareas (focus and keyboards).
* @private
*/
Blockly.FieldTextInput.prototype.showPromptEditor_ = function() {
Blockly.prompt(Blockly.Msg['CHANGE_VALUE_TITLE'], this.getText(),
function(text) {
this.setValue(this.getValueFromEditorText_(text));
}.bind(this));
FieldTextInput.prototype.showPromptEditor_ = function() {
blocklyPrompt(Msg['CHANGE_VALUE_TITLE'], this.getText(), function(text) {
this.setValue(this.getValueFromEditorText_(text));
}.bind(this));
};
/**
@@ -324,14 +323,13 @@ Blockly.FieldTextInput.prototype.showPromptEditor_ = function() {
* focus.
* @private
*/
Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) {
Blockly.WidgetDiv.show(
this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));
FieldTextInput.prototype.showInlineEditor_ = function(quietInput) {
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();
}
};
@@ -341,38 +339,37 @@ Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) {
* @return {!HTMLElement} The newly created text input editor.
* @protected
*/
Blockly.FieldTextInput.prototype.widgetCreate_ = function() {
Blockly.Events.setGroup(true);
var div = Blockly.WidgetDiv.DIV;
FieldTextInput.prototype.widgetCreate_ = function() {
Events.setGroup(true);
const div = WidgetDiv.DIV;
Blockly.utils.dom.addClass(this.getClickTarget_(), 'editing');
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 =
(this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt';
const scale = this.workspace_.getScale();
const fontSize = (this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt';
div.style.fontSize = fontSize;
htmlInput.style.fontSize = fontSize;
var borderRadius =
(Blockly.FieldTextInput.BORDERRADIUS * scale) + 'px';
let borderRadius = (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() ?
this.sourceBlock_.getParent().style.colourTertiary :
this.sourceBlock_.style.colourTertiary;
const strokeColour = this.sourceBlock_.getParent() ?
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;
@@ -395,7 +392,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;
@@ -405,11 +402,11 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
if (this.onFinishEditing_) {
this.onFinishEditing_(this.value_);
}
Blockly.Events.setGroup(false);
Events.setGroup(false);
// Actual disposal.
this.unbindInputEvents_();
var style = Blockly.WidgetDiv.DIV.style;
const style = WidgetDiv.DIV.style;
style.width = 'auto';
style.height = 'auto';
style.fontSize = '';
@@ -417,7 +414,7 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
style.boxShadow = '';
this.htmlInput_ = null;
Blockly.utils.dom.removeClass(this.getClickTarget_(), 'editing');
dom.removeClass(this.getClickTarget_(), 'editing');
};
/**
@@ -426,12 +423,12 @@ 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(
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_);
};
@@ -439,13 +436,13 @@ 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_);
browserEvents.unbind(this.onKeyDownWrapper_);
this.onKeyDownWrapper_ = null;
}
if (this.onKeyInputWrapper_) {
Blockly.browserEvents.unbind(this.onKeyInputWrapper_);
browserEvents.unbind(this.onKeyInputWrapper_);
this.onKeyInputWrapper_ = null;
}
};
@@ -455,17 +452,17 @@ Blockly.FieldTextInput.prototype.unbindInputEvents_ = function() {
* @param {!Event} e Keyboard event.
* @protected
*/
Blockly.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) {
FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
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();
}
@@ -476,12 +473,12 @@ Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
* @param {!Event} _e Keyboard event.
* @private
*/
Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(_e) {
var text = this.htmlInput_.value;
FieldTextInput.prototype.onHtmlInputChange_ = function(_e) {
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_();
@@ -495,7 +492,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,16 +508,16 @@ Blockly.FieldTextInput.prototype.setEditorValue_ = function(newValue) {
* Resize the editor to fit the text.
* @protected
*/
Blockly.FieldTextInput.prototype.resizeEditor_ = function() {
var div = Blockly.WidgetDiv.DIV;
var bBox = this.getScaledBBox();
FieldTextInput.prototype.resizeEditor_ = function() {
const div = 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 Coordinate(x, bBox.top);
div.style.left = xy.x + 'px';
div.style.top = xy.y + 'px';
@@ -531,20 +528,20 @@ 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;
};
/**
* 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
*/
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 +558,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 +572,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);
fieldRegistry.register('field_input', FieldTextInput);
exports = FieldTextInput;

View File

@@ -13,9 +13,12 @@
goog.module('Blockly.FieldVariable');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const FieldDropdown = goog.require('Blockly.FieldDropdown');
/* eslint-disable-next-line no-unused-vars */
const Menu = goog.requireType('Blockly.Menu');
/* eslint-disable-next-line no-unused-vars */
const MenuItem = goog.requireType('Blockly.MenuItem');
const Msg = goog.require('Blockly.Msg');
const Size = goog.require('Blockly.utils.Size');

File diff suppressed because it is too large Load Diff

View File

@@ -10,36 +10,36 @@
*/
'use strict';
goog.provide('Blockly.HorizontalFlyout');
goog.module('Blockly.HorizontalFlyout');
goog.module.declareLegacyNamespace();
/** @suppress {extraRequire} */
goog.require('Blockly.Block');
goog.require('Blockly.DropDownDiv');
goog.require('Blockly.Flyout');
goog.require('Blockly.registry');
goog.require('Blockly.Scrollbar');
goog.require('Blockly.utils');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.toolbox');
goog.require('Blockly.WidgetDiv');
goog.requireType('Blockly.Options');
goog.requireType('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
const DropDownDiv = goog.require('Blockly.DropDownDiv');
const Flyout = goog.require('Blockly.Flyout');
/* eslint-disable-next-line no-unused-vars */
const Options = goog.requireType('Blockly.Options');
const Rect = goog.require('Blockly.utils.Rect');
const Scrollbar = goog.require('Blockly.Scrollbar');
const WidgetDiv = goog.require('Blockly.WidgetDiv');
const registry = goog.require('Blockly.registry');
const {Position} = goog.require('Blockly.utils.toolbox');
const {getScrollDeltaPixels} = goog.require('Blockly.utils');
const {inherits} = goog.require('Blockly.utils.object');
/**
* Class for a flyout.
* @param {!Blockly.Options} workspaceOptions Dictionary of options for the
* @param {!Options} workspaceOptions Dictionary of options for the
* workspace.
* @extends {Blockly.Flyout}
* @extends {Flyout}
* @constructor
*/
Blockly.HorizontalFlyout = function(workspaceOptions) {
Blockly.HorizontalFlyout.superClass_.constructor.call(this, workspaceOptions);
const HorizontalFlyout = function(workspaceOptions) {
HorizontalFlyout.superClass_.constructor.call(this, workspaceOptions);
this.horizontalLayout = true;
};
Blockly.utils.object.inherits(Blockly.HorizontalFlyout, Blockly.Flyout);
inherits(HorizontalFlyout, Flyout);
/**
* Sets the translation of the flyout to match the scrollbars.
@@ -48,23 +48,24 @@ Blockly.utils.object.inherits(Blockly.HorizontalFlyout, Blockly.Flyout);
* similar x property.
* @protected
*/
Blockly.HorizontalFlyout.prototype.setMetrics_ = function(xyRatio) {
HorizontalFlyout.prototype.setMetrics_ = function(xyRatio) {
if (!this.isVisible()) {
return;
}
var metricsManager = this.workspace_.getMetricsManager();
var scrollMetrics = metricsManager.getScrollMetrics();
var viewMetrics = metricsManager.getViewMetrics();
var absoluteMetrics = metricsManager.getAbsoluteMetrics();
const metricsManager = this.workspace_.getMetricsManager();
const scrollMetrics = metricsManager.getScrollMetrics();
const viewMetrics = metricsManager.getViewMetrics();
const absoluteMetrics = metricsManager.getAbsoluteMetrics();
if (typeof xyRatio.x == 'number') {
this.workspace_.scrollX =
-(scrollMetrics.left +
(scrollMetrics.width - viewMetrics.width) * xyRatio.x);
(scrollMetrics.width - viewMetrics.width) * xyRatio.x);
}
this.workspace_.translate(this.workspace_.scrollX + absoluteMetrics.left,
this.workspace_.translate(
this.workspace_.scrollX + absoluteMetrics.left,
this.workspace_.scrollY + absoluteMetrics.top);
};
@@ -72,7 +73,7 @@ Blockly.HorizontalFlyout.prototype.setMetrics_ = function(xyRatio) {
* Calculates the x coordinate for the flyout position.
* @return {number} X coordinate.
*/
Blockly.HorizontalFlyout.prototype.getX = function() {
HorizontalFlyout.prototype.getX = function() {
// X is always 0 since this is a horizontal flyout.
return 0;
};
@@ -81,17 +82,17 @@ Blockly.HorizontalFlyout.prototype.getX = function() {
* Calculates the y coordinate for the flyout position.
* @return {number} Y coordinate.
*/
Blockly.HorizontalFlyout.prototype.getY = function() {
HorizontalFlyout.prototype.getY = function() {
if (!this.isVisible()) {
return 0;
}
var metricsManager = this.targetWorkspace.getMetricsManager();
var absoluteMetrics = metricsManager.getAbsoluteMetrics();
var viewMetrics = metricsManager.getViewMetrics();
var toolboxMetrics = metricsManager.getToolboxMetrics();
const metricsManager = this.targetWorkspace.getMetricsManager();
const absoluteMetrics = metricsManager.getAbsoluteMetrics();
const viewMetrics = metricsManager.getViewMetrics();
const toolboxMetrics = metricsManager.getToolboxMetrics();
var y = 0;
var atTop = this.toolboxPosition_ == Blockly.utils.toolbox.Position.TOP;
let y = 0;
const atTop = this.toolboxPosition_ == Position.TOP;
// If this flyout is not the trashcan flyout (e.g. toolbox or mutator).
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_) {
// If there is a category toolbox.
@@ -129,22 +130,22 @@ Blockly.HorizontalFlyout.prototype.getY = function() {
/**
* Move the flyout to the edge of the workspace.
*/
Blockly.HorizontalFlyout.prototype.position = function() {
HorizontalFlyout.prototype.position = function() {
if (!this.isVisible() || !this.targetWorkspace.isVisible()) {
return;
}
var metricsManager = this.targetWorkspace.getMetricsManager();
var targetWorkspaceViewMetrics = metricsManager.getViewMetrics();
const metricsManager = this.targetWorkspace.getMetricsManager();
const targetWorkspaceViewMetrics = metricsManager.getViewMetrics();
// Record the width for workspace metrics.
this.width_ = targetWorkspaceViewMetrics.width;
var edgeWidth = targetWorkspaceViewMetrics.width - 2 * this.CORNER_RADIUS;
var edgeHeight = this.height_ - this.CORNER_RADIUS;
const edgeWidth = targetWorkspaceViewMetrics.width - 2 * this.CORNER_RADIUS;
const edgeHeight = this.height_ - this.CORNER_RADIUS;
this.setBackgroundPath_(edgeWidth, edgeHeight);
var x = this.getX();
var y = this.getY();
const x = this.getX();
const y = this.getY();
this.positionAt_(this.width_, this.height_, x, y);
};
@@ -157,11 +158,10 @@ Blockly.HorizontalFlyout.prototype.position = function() {
* rounded corners.
* @private
*/
Blockly.HorizontalFlyout.prototype.setBackgroundPath_ = function(
width, height) {
var atTop = this.toolboxPosition_ == Blockly.utils.toolbox.Position.TOP;
HorizontalFlyout.prototype.setBackgroundPath_ = function(width, height) {
const atTop = this.toolboxPosition_ == Position.TOP;
// Start at top left.
var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)];
const path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)];
if (atTop) {
// Top.
@@ -169,20 +169,24 @@ Blockly.HorizontalFlyout.prototype.setBackgroundPath_ = function(
// Right.
path.push('v', height);
// Bottom.
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
path.push(
'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
-this.CORNER_RADIUS, this.CORNER_RADIUS);
path.push('h', -width);
// Left.
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
path.push(
'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
-this.CORNER_RADIUS, -this.CORNER_RADIUS);
path.push('z');
} else {
// Top.
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
path.push(
'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
this.CORNER_RADIUS, -this.CORNER_RADIUS);
path.push('h', width);
// Right.
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
path.push(
'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
this.CORNER_RADIUS, this.CORNER_RADIUS);
path.push('v', height);
// Bottom.
@@ -196,7 +200,7 @@ Blockly.HorizontalFlyout.prototype.setBackgroundPath_ = function(
/**
* Scroll the flyout to the top.
*/
Blockly.HorizontalFlyout.prototype.scrollToStart = function() {
HorizontalFlyout.prototype.scrollToStart = function() {
this.workspace_.scrollbar.setX(this.RTL ? Infinity : 0);
};
@@ -205,20 +209,20 @@ Blockly.HorizontalFlyout.prototype.scrollToStart = function() {
* @param {!Event} e Mouse wheel scroll event.
* @protected
*/
Blockly.HorizontalFlyout.prototype.wheel_ = function(e) {
var scrollDelta = Blockly.utils.getScrollDeltaPixels(e);
var delta = scrollDelta.x || scrollDelta.y;
HorizontalFlyout.prototype.wheel_ = function(e) {
const scrollDelta = getScrollDeltaPixels(e);
const delta = scrollDelta.x || scrollDelta.y;
if (delta) {
var metricsManager = this.workspace_.getMetricsManager();
var scrollMetrics = metricsManager.getScrollMetrics();
var viewMetrics = metricsManager.getViewMetrics();
const metricsManager = this.workspace_.getMetricsManager();
const scrollMetrics = metricsManager.getScrollMetrics();
const viewMetrics = metricsManager.getViewMetrics();
var pos = (viewMetrics.left - scrollMetrics.left) + delta;
const pos = (viewMetrics.left - scrollMetrics.left) + delta;
this.workspace_.scrollbar.setX(pos);
// When the flyout moves from a wheel event, hide WidgetDiv and DropDownDiv.
Blockly.WidgetDiv.hide();
Blockly.DropDownDiv.hideWithoutAnimation();
WidgetDiv.hide();
DropDownDiv.hideWithoutAnimation();
}
// Don't scroll the page.
@@ -233,39 +237,40 @@ Blockly.HorizontalFlyout.prototype.wheel_ = function(e) {
* @param {!Array<number>} gaps The visible gaps between blocks.
* @protected
*/
Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) {
HorizontalFlyout.prototype.layout_ = function(contents, gaps) {
this.workspace_.scale = this.targetWorkspace.scale;
var margin = this.MARGIN;
var cursorX = margin + this.tabWidth_;
var cursorY = margin;
const margin = this.MARGIN;
let cursorX = margin + this.tabWidth_;
const cursorY = margin;
if (this.RTL) {
contents = contents.reverse();
}
for (var i = 0, item; (item = contents[i]); i++) {
for (let i = 0, item; (item = contents[i]); i++) {
if (item.type == 'block') {
var block = item.block;
var allBlocks = block.getDescendants(false);
for (var j = 0, child; (child = allBlocks[j]); j++) {
const block = item.block;
const allBlocks = block.getDescendants(false);
for (let j = 0, child; (child = allBlocks[j]); j++) {
// Mark blocks as being inside a flyout. This is used to detect and
// prevent the closure of the flyout if the user right-clicks on such a
// block.
child.isInFlyout = true;
}
block.render();
var root = block.getSvgRoot();
var blockHW = block.getHeightWidth();
const root = block.getSvgRoot();
const blockHW = block.getHeightWidth();
// Figure out where to place the block.
var tab = block.outputConnection ? this.tabWidth_ : 0;
const tab = block.outputConnection ? this.tabWidth_ : 0;
let moveX;
if (this.RTL) {
var moveX = cursorX + blockHW.width;
moveX = cursorX + blockHW.width;
} else {
var moveX = cursorX - tab;
moveX = cursorX - tab;
}
block.moveBy(moveX, cursorY);
var rect = this.createRect_(block, moveX, cursorY, blockHW, i);
const rect = this.createRect_(block, moveX, cursorY, blockHW, i);
cursorX += (blockHW.width + gaps[i]);
this.addBlockListeners_(root, block, rect);
@@ -280,19 +285,19 @@ Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) {
* Determine if a drag delta is toward the workspace, based on the position
* and orientation of the flyout. This is used in determineDragIntention_ to
* determine if a new block should be created or if the flyout should scroll.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at mouse down, in pixel units.
* @return {boolean} True if the drag is toward the workspace.
* @package
*/
Blockly.HorizontalFlyout.prototype.isDragTowardWorkspace = function(
HorizontalFlyout.prototype.isDragTowardWorkspace = function(
currentDragDeltaXY) {
var dx = currentDragDeltaXY.x;
var dy = currentDragDeltaXY.y;
const dx = currentDragDeltaXY.x;
const dy = currentDragDeltaXY.y;
// Direction goes from -180 to 180, with 0 toward the right and 90 on top.
var dragDirection = Math.atan2(dy, dx) / Math.PI * 180;
const dragDirection = Math.atan2(dy, dx) / Math.PI * 180;
var range = this.dragAngleRange_;
const range = this.dragAngleRange_;
// Check for up or down dragging.
if ((dragDirection < 90 + range && dragDirection > 90 - range) ||
(dragDirection > -90 - range && dragDirection < -90 + range)) {
@@ -304,28 +309,28 @@ Blockly.HorizontalFlyout.prototype.isDragTowardWorkspace = function(
/**
* Returns the bounding rectangle of the drag target area in pixel units
* relative to viewport.
* @return {?Blockly.utils.Rect} The component's bounding box. Null if drag
* @return {?Rect} The component's bounding box. Null if drag
* target area should be ignored.
*/
Blockly.HorizontalFlyout.prototype.getClientRect = function() {
HorizontalFlyout.prototype.getClientRect = function() {
if (!this.svgGroup_ || this.autoClose || !this.isVisible()) {
// The bounding rectangle won't compute correctly if the flyout is closed
// and auto-close flyouts aren't valid drag targets (or delete areas).
return null;
}
var flyoutRect = this.svgGroup_.getBoundingClientRect();
const flyoutRect = this.svgGroup_.getBoundingClientRect();
// BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout
// area are still deleted. Must be larger than the largest screen size,
// but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
var BIG_NUM = 1000000000;
var top = flyoutRect.top;
const BIG_NUM = 1000000000;
const top = flyoutRect.top;
if (this.toolboxPosition_ == Blockly.utils.toolbox.Position.TOP) {
var height = flyoutRect.height;
return new Blockly.utils.Rect(-BIG_NUM, top + height, -BIG_NUM, BIG_NUM);
if (this.toolboxPosition_ == Position.TOP) {
const height = flyoutRect.height;
return new Rect(-BIG_NUM, top + height, -BIG_NUM, BIG_NUM);
} else { // Bottom.
return new Blockly.utils.Rect(top, BIG_NUM, -BIG_NUM, BIG_NUM);
return new Rect(top, BIG_NUM, -BIG_NUM, BIG_NUM);
}
};
@@ -334,36 +339,37 @@ Blockly.HorizontalFlyout.prototype.getClientRect = function() {
* For RTL: Lay out the blocks right-aligned.
* @protected
*/
Blockly.HorizontalFlyout.prototype.reflowInternal_ = function() {
HorizontalFlyout.prototype.reflowInternal_ = function() {
this.workspace_.scale = this.getFlyoutScale();
var flyoutHeight = 0;
var blocks = this.workspace_.getTopBlocks(false);
for (var i = 0, block; (block = blocks[i]); i++) {
let flyoutHeight = 0;
const blocks = this.workspace_.getTopBlocks(false);
for (let i = 0, block; (block = blocks[i]); i++) {
flyoutHeight = Math.max(flyoutHeight, block.getHeightWidth().height);
}
var buttons = this.buttons_;
for (var i = 0, button; (button = buttons[i]); i++) {
const buttons = this.buttons_;
for (let i = 0, button; (button = buttons[i]); i++) {
flyoutHeight = Math.max(flyoutHeight, button.height);
}
flyoutHeight += this.MARGIN * 1.5;
flyoutHeight *= this.workspace_.scale;
flyoutHeight += Blockly.Scrollbar.scrollbarThickness;
flyoutHeight += Scrollbar.scrollbarThickness;
if (this.height_ != flyoutHeight) {
for (var i = 0, block; (block = blocks[i]); i++) {
for (let i = 0, block; (block = blocks[i]); i++) {
if (block.flyoutRect_) {
this.moveRectToBlock_(block.flyoutRect_, block);
}
}
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_ &&
this.toolboxPosition_ == Blockly.utils.toolbox.Position.TOP &&
this.toolboxPosition_ == Position.TOP &&
!this.targetWorkspace.getToolbox()) {
// This flyout is a simple toolbox. Reposition the workspace so that (0,0)
// is in the correct position relative to the new absolute edge (ie
// toolbox edge).
this.targetWorkspace.translate(
this.targetWorkspace.scrollX, this.targetWorkspace.scrollY + flyoutHeight);
this.targetWorkspace.scrollX,
this.targetWorkspace.scrollY + flyoutHeight);
}
// Record the height for workspace metrics and .position.
@@ -373,5 +379,8 @@ Blockly.HorizontalFlyout.prototype.reflowInternal_ = function() {
}
};
Blockly.registry.register(Blockly.registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX,
Blockly.registry.DEFAULT, Blockly.HorizontalFlyout);
registry.register(
registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX, registry.DEFAULT,
HorizontalFlyout);
exports = HorizontalFlyout;

View File

@@ -10,43 +10,45 @@
*/
'use strict';
goog.provide('Blockly.VerticalFlyout');
goog.module('Blockly.VerticalFlyout');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
const DropDownDiv = goog.require('Blockly.DropDownDiv');
const Flyout = goog.require('Blockly.Flyout');
/* eslint-disable-next-line no-unused-vars */
const Options = goog.requireType('Blockly.Options');
const Rect = goog.require('Blockly.utils.Rect');
const Scrollbar = goog.require('Blockly.Scrollbar');
const WidgetDiv = goog.require('Blockly.WidgetDiv');
const registry = goog.require('Blockly.registry');
const {Position} = goog.require('Blockly.utils.toolbox');
const {getScrollDeltaPixels} = goog.require('Blockly.utils');
const {inherits} = goog.require('Blockly.utils.object');
/** @suppress {extraRequire} */
goog.require('Blockly.Block');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.DropDownDiv');
goog.require('Blockly.Flyout');
goog.require('Blockly.registry');
goog.require('Blockly.Scrollbar');
goog.require('Blockly.utils');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.toolbox');
goog.require('Blockly.WidgetDiv');
goog.requireType('Blockly.Options');
goog.requireType('Blockly.utils.Coordinate');
/**
* Class for a flyout.
* @param {!Blockly.Options} workspaceOptions Dictionary of options for the
* @param {!Options} workspaceOptions Dictionary of options for the
* workspace.
* @extends {Blockly.Flyout}
* @extends {Flyout}
* @constructor
*/
Blockly.VerticalFlyout = function(workspaceOptions) {
Blockly.VerticalFlyout.superClass_.constructor.call(this, workspaceOptions);
const VerticalFlyout = function(workspaceOptions) {
VerticalFlyout.superClass_.constructor.call(this, workspaceOptions);
};
Blockly.utils.object.inherits(Blockly.VerticalFlyout, Blockly.Flyout);
inherits(VerticalFlyout, Flyout);
/**
* The name of the vertical flyout in the registry.
* @type {string}
*/
Blockly.VerticalFlyout.registryName = 'verticalFlyout';
VerticalFlyout.registryName = 'verticalFlyout';
/**
* Sets the translation of the flyout to match the scrollbars.
@@ -55,21 +57,22 @@ Blockly.VerticalFlyout.registryName = 'verticalFlyout';
* similar x property.
* @protected
*/
Blockly.VerticalFlyout.prototype.setMetrics_ = function(xyRatio) {
VerticalFlyout.prototype.setMetrics_ = function(xyRatio) {
if (!this.isVisible()) {
return;
}
var metricsManager = this.workspace_.getMetricsManager();
var scrollMetrics = metricsManager.getScrollMetrics();
var viewMetrics = metricsManager.getViewMetrics();
var absoluteMetrics = metricsManager.getAbsoluteMetrics();
const metricsManager = this.workspace_.getMetricsManager();
const scrollMetrics = metricsManager.getScrollMetrics();
const viewMetrics = metricsManager.getViewMetrics();
const absoluteMetrics = metricsManager.getAbsoluteMetrics();
if (typeof xyRatio.y == 'number') {
this.workspace_.scrollY =
-(scrollMetrics.top +
(scrollMetrics.height - viewMetrics.height) * xyRatio.y);
(scrollMetrics.height - viewMetrics.height) * xyRatio.y);
}
this.workspace_.translate(this.workspace_.scrollX + absoluteMetrics.left,
this.workspace_.translate(
this.workspace_.scrollX + absoluteMetrics.left,
this.workspace_.scrollY + absoluteMetrics.top);
};
@@ -77,28 +80,28 @@ Blockly.VerticalFlyout.prototype.setMetrics_ = function(xyRatio) {
* Calculates the x coordinate for the flyout position.
* @return {number} X coordinate.
*/
Blockly.VerticalFlyout.prototype.getX = function() {
VerticalFlyout.prototype.getX = function() {
if (!this.isVisible()) {
return 0;
}
var metricsManager = this.targetWorkspace.getMetricsManager();
var absoluteMetrics = metricsManager.getAbsoluteMetrics();
var viewMetrics = metricsManager.getViewMetrics();
var toolboxMetrics = metricsManager.getToolboxMetrics();
var x = 0;
const metricsManager = this.targetWorkspace.getMetricsManager();
const absoluteMetrics = metricsManager.getAbsoluteMetrics();
const viewMetrics = metricsManager.getViewMetrics();
const toolboxMetrics = metricsManager.getToolboxMetrics();
let x = 0;
// If this flyout is not the trashcan flyout (e.g. toolbox or mutator).
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_) {
// If there is a category toolbox.
if (this.targetWorkspace.getToolbox()) {
if (this.toolboxPosition_ == Blockly.utils.toolbox.Position.LEFT) {
if (this.toolboxPosition_ == Position.LEFT) {
x = toolboxMetrics.width;
} else {
x = viewMetrics.width - this.width_;
}
// Simple (flyout-only) toolbox.
} else {
if (this.toolboxPosition_ == Blockly.utils.toolbox.Position.LEFT) {
if (this.toolboxPosition_ == Position.LEFT) {
x = 0;
} else {
// The simple flyout does not cover the workspace.
@@ -107,7 +110,7 @@ Blockly.VerticalFlyout.prototype.getX = function() {
}
// Trashcan flyout is opposite the main flyout.
} else {
if (this.toolboxPosition_ == Blockly.utils.toolbox.Position.LEFT) {
if (this.toolboxPosition_ == Position.LEFT) {
x = 0;
} else {
// Because the anchor point of the flyout is on the left, but we want
@@ -125,7 +128,7 @@ Blockly.VerticalFlyout.prototype.getX = function() {
* Calculates the y coordinate for the flyout position.
* @return {number} Y coordinate.
*/
Blockly.VerticalFlyout.prototype.getY = function() {
VerticalFlyout.prototype.getY = function() {
// Y is always 0 since this is a vertical flyout.
return 0;
};
@@ -133,22 +136,22 @@ Blockly.VerticalFlyout.prototype.getY = function() {
/**
* Move the flyout to the edge of the workspace.
*/
Blockly.VerticalFlyout.prototype.position = function() {
VerticalFlyout.prototype.position = function() {
if (!this.isVisible() || !this.targetWorkspace.isVisible()) {
return;
}
var metricsManager = this.targetWorkspace.getMetricsManager();
var targetWorkspaceViewMetrics = metricsManager.getViewMetrics();
const metricsManager = this.targetWorkspace.getMetricsManager();
const targetWorkspaceViewMetrics = metricsManager.getViewMetrics();
// Record the height for workspace metrics.
this.height_ = targetWorkspaceViewMetrics.height;
var edgeWidth = this.width_ - this.CORNER_RADIUS;
var edgeHeight = targetWorkspaceViewMetrics.height - 2 * this.CORNER_RADIUS;
const edgeWidth = this.width_ - this.CORNER_RADIUS;
const edgeHeight = targetWorkspaceViewMetrics.height - 2 * this.CORNER_RADIUS;
this.setBackgroundPath_(edgeWidth, edgeHeight);
var x = this.getX();
var y = this.getY();
const x = this.getX();
const y = this.getY();
this.positionAt_(this.width_, this.height_, x, y);
};
@@ -161,26 +164,24 @@ Blockly.VerticalFlyout.prototype.position = function() {
* rounded corners.
* @private
*/
Blockly.VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) {
var atRight = this.toolboxPosition_ == Blockly.utils.toolbox.Position.RIGHT;
var totalWidth = width + this.CORNER_RADIUS;
VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) {
const atRight = this.toolboxPosition_ == Position.RIGHT;
const totalWidth = width + this.CORNER_RADIUS;
// Decide whether to start on the left or right.
var path = ['M ' + (atRight ? totalWidth : 0) + ',0'];
const path = ['M ' + (atRight ? totalWidth : 0) + ',0'];
// Top.
path.push('h', atRight ? -width : width);
// Rounded corner.
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
atRight ? 0 : 1,
atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS,
this.CORNER_RADIUS);
path.push(
'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, atRight ? 0 : 1,
atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS, this.CORNER_RADIUS);
// Side closest to workspace.
path.push('v', Math.max(0, height));
// Rounded corner.
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
atRight ? 0 : 1,
atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS,
this.CORNER_RADIUS);
path.push(
'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, atRight ? 0 : 1,
atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS, this.CORNER_RADIUS);
// Bottom.
path.push('h', atRight ? width : -width);
path.push('z');
@@ -190,7 +191,7 @@ Blockly.VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) {
/**
* Scroll the flyout to the top.
*/
Blockly.VerticalFlyout.prototype.scrollToStart = function() {
VerticalFlyout.prototype.scrollToStart = function() {
this.workspace_.scrollbar.setY(0);
};
@@ -199,19 +200,19 @@ Blockly.VerticalFlyout.prototype.scrollToStart = function() {
* @param {!Event} e Mouse wheel scroll event.
* @protected
*/
Blockly.VerticalFlyout.prototype.wheel_ = function(e) {
var scrollDelta = Blockly.utils.getScrollDeltaPixels(e);
VerticalFlyout.prototype.wheel_ = function(e) {
const scrollDelta = getScrollDeltaPixels(e);
if (scrollDelta.y) {
var metricsManager = this.workspace_.getMetricsManager();
var scrollMetrics = metricsManager.getScrollMetrics();
var viewMetrics = metricsManager.getViewMetrics();
var pos = (viewMetrics.top - scrollMetrics.top) + scrollDelta.y;
const metricsManager = this.workspace_.getMetricsManager();
const scrollMetrics = metricsManager.getScrollMetrics();
const viewMetrics = metricsManager.getViewMetrics();
const pos = (viewMetrics.top - scrollMetrics.top) + scrollDelta.y;
this.workspace_.scrollbar.setY(pos);
// When the flyout moves from a wheel event, hide WidgetDiv and DropDownDiv.
Blockly.WidgetDiv.hide();
Blockly.DropDownDiv.hideWithoutAnimation();
WidgetDiv.hide();
DropDownDiv.hideWithoutAnimation();
}
// Don't scroll the page.
@@ -226,30 +227,30 @@ Blockly.VerticalFlyout.prototype.wheel_ = function(e) {
* @param {!Array<number>} gaps The visible gaps between blocks.
* @protected
*/
Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) {
VerticalFlyout.prototype.layout_ = function(contents, gaps) {
this.workspace_.scale = this.targetWorkspace.scale;
var margin = this.MARGIN;
var cursorX = this.RTL ? margin : margin + this.tabWidth_;
var cursorY = margin;
const margin = this.MARGIN;
const cursorX = this.RTL ? margin : margin + this.tabWidth_;
let cursorY = margin;
for (var i = 0, item; (item = contents[i]); i++) {
for (let i = 0, item; (item = contents[i]); i++) {
if (item.type == 'block') {
var block = item.block;
var allBlocks = block.getDescendants(false);
for (var j = 0, child; (child = allBlocks[j]); j++) {
const block = item.block;
const allBlocks = block.getDescendants(false);
for (let j = 0, child; (child = allBlocks[j]); j++) {
// Mark blocks as being inside a flyout. This is used to detect and
// prevent the closure of the flyout if the user right-clicks on such a
// block.
child.isInFlyout = true;
}
block.render();
var root = block.getSvgRoot();
var blockHW = block.getHeightWidth();
var moveX = block.outputConnection ? cursorX - this.tabWidth_ : cursorX;
const root = block.getSvgRoot();
const blockHW = block.getHeightWidth();
const moveX = block.outputConnection ? cursorX - this.tabWidth_ : cursorX;
block.moveBy(moveX, cursorY);
var rect = this.createRect_(block,
this.RTL ? moveX - blockHW.width : moveX, cursorY, blockHW, i);
const rect = this.createRect_(
block, this.RTL ? moveX - blockHW.width : moveX, cursorY, blockHW, i);
this.addBlockListeners_(root, block, rect);
@@ -265,19 +266,18 @@ Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) {
* Determine if a drag delta is toward the workspace, based on the position
* and orientation of the flyout. This is used in determineDragIntention_ to
* determine if a new block should be created or if the flyout should scroll.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at mouse down, in pixel units.
* @return {boolean} True if the drag is toward the workspace.
* @package
*/
Blockly.VerticalFlyout.prototype.isDragTowardWorkspace = function(
currentDragDeltaXY) {
var dx = currentDragDeltaXY.x;
var dy = currentDragDeltaXY.y;
VerticalFlyout.prototype.isDragTowardWorkspace = function(currentDragDeltaXY) {
const dx = currentDragDeltaXY.x;
const dy = currentDragDeltaXY.y;
// Direction goes from -180 to 180, with 0 toward the right and 90 on top.
var dragDirection = Math.atan2(dy, dx) / Math.PI * 180;
const dragDirection = Math.atan2(dy, dx) / Math.PI * 180;
var range = this.dragAngleRange_;
const range = this.dragAngleRange_;
// Check for left or right dragging.
if ((dragDirection < range && dragDirection > -range) ||
(dragDirection < -180 + range || dragDirection > 180 - range)) {
@@ -289,28 +289,28 @@ Blockly.VerticalFlyout.prototype.isDragTowardWorkspace = function(
/**
* Returns the bounding rectangle of the drag target area in pixel units
* relative to viewport.
* @return {?Blockly.utils.Rect} The component's bounding box. Null if drag
* @return {?Rect} The component's bounding box. Null if drag
* target area should be ignored.
*/
Blockly.VerticalFlyout.prototype.getClientRect = function() {
VerticalFlyout.prototype.getClientRect = function() {
if (!this.svgGroup_ || this.autoClose || !this.isVisible()) {
// The bounding rectangle won't compute correctly if the flyout is closed
// and auto-close flyouts aren't valid drag targets (or delete areas).
return null;
}
var flyoutRect = this.svgGroup_.getBoundingClientRect();
const flyoutRect = this.svgGroup_.getBoundingClientRect();
// BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout
// area are still deleted. Must be larger than the largest screen size,
// but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
var BIG_NUM = 1000000000;
var left = flyoutRect.left;
const BIG_NUM = 1000000000;
const left = flyoutRect.left;
if (this.toolboxPosition_ == Blockly.utils.toolbox.Position.LEFT) {
var width = flyoutRect.width;
return new Blockly.utils.Rect(-BIG_NUM, BIG_NUM, -BIG_NUM, left + width);
if (this.toolboxPosition_ == Position.LEFT) {
const width = flyoutRect.width;
return new Rect(-BIG_NUM, BIG_NUM, -BIG_NUM, left + width);
} else { // Right
return new Blockly.utils.Rect(-BIG_NUM, BIG_NUM, left, BIG_NUM);
return new Rect(-BIG_NUM, BIG_NUM, left, BIG_NUM);
}
};
@@ -319,30 +319,30 @@ Blockly.VerticalFlyout.prototype.getClientRect = function() {
* For RTL: Lay out the blocks and buttons to be right-aligned.
* @protected
*/
Blockly.VerticalFlyout.prototype.reflowInternal_ = function() {
VerticalFlyout.prototype.reflowInternal_ = function() {
this.workspace_.scale = this.getFlyoutScale();
var flyoutWidth = 0;
var blocks = this.workspace_.getTopBlocks(false);
for (var i = 0, block; (block = blocks[i]); i++) {
var width = block.getHeightWidth().width;
let flyoutWidth = 0;
const blocks = this.workspace_.getTopBlocks(false);
for (let i = 0, block; (block = blocks[i]); i++) {
let width = block.getHeightWidth().width;
if (block.outputConnection) {
width -= this.tabWidth_;
}
flyoutWidth = Math.max(flyoutWidth, width);
}
for (var i = 0, button; (button = this.buttons_[i]); i++) {
for (let i = 0, button; (button = this.buttons_[i]); i++) {
flyoutWidth = Math.max(flyoutWidth, button.width);
}
flyoutWidth += this.MARGIN * 1.5 + this.tabWidth_;
flyoutWidth *= this.workspace_.scale;
flyoutWidth += Blockly.Scrollbar.scrollbarThickness;
flyoutWidth += Scrollbar.scrollbarThickness;
if (this.width_ != flyoutWidth) {
for (var i = 0, block; (block = blocks[i]); i++) {
for (let i = 0, block; (block = blocks[i]); i++) {
if (this.RTL) {
// With the flyoutWidth known, right-align the blocks.
var oldX = block.getRelativeToSurfaceXY().x;
var newX = flyoutWidth / this.workspace_.scale - this.MARGIN;
const oldX = block.getRelativeToSurfaceXY().x;
let newX = flyoutWidth / this.workspace_.scale - this.MARGIN;
if (!block.outputConnection) {
newX -= this.tabWidth_;
}
@@ -354,22 +354,23 @@ Blockly.VerticalFlyout.prototype.reflowInternal_ = function() {
}
if (this.RTL) {
// With the flyoutWidth known, right-align the buttons.
for (var i = 0, button; (button = this.buttons_[i]); i++) {
var y = button.getPosition().y;
var x = flyoutWidth / this.workspace_.scale - button.width -
for (let i = 0, button; (button = this.buttons_[i]); i++) {
const y = button.getPosition().y;
const x = flyoutWidth / this.workspace_.scale - button.width -
this.MARGIN - this.tabWidth_;
button.moveTo(x, y);
}
}
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_ &&
this.toolboxPosition_ == Blockly.utils.toolbox.Position.LEFT &&
this.toolboxPosition_ == Position.LEFT &&
!this.targetWorkspace.getToolbox()) {
// This flyout is a simple toolbox. Reposition the workspace so that (0,0)
// is in the correct position relative to the new absolute edge (ie
// toolbox edge).
this.targetWorkspace.translate(
this.targetWorkspace.scrollX + flyoutWidth, this.targetWorkspace.scrollY);
this.targetWorkspace.scrollX + flyoutWidth,
this.targetWorkspace.scrollY);
}
// Record the width for workspace metrics and .position.
@@ -379,5 +380,7 @@ Blockly.VerticalFlyout.prototype.reflowInternal_ = function() {
}
};
Blockly.registry.register(Blockly.registry.Type.FLYOUTS_VERTICAL_TOOLBOX,
Blockly.registry.DEFAULT, Blockly.VerticalFlyout);
registry.register(
registry.Type.FLYOUTS_VERTICAL_TOOLBOX, registry.DEFAULT, VerticalFlyout);
exports = VerticalFlyout;

View File

@@ -11,14 +11,18 @@
*/
'use strict';
goog.provide('Blockly.Generator');
goog.module('Blockly.Generator');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Block');
goog.require('Blockly.internalConstants');
goog.require('Blockly.utils.deprecation');
goog.requireType('Blockly.Names');
goog.requireType('Blockly.Workspace');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const Names = goog.requireType('Blockly.Names');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const internalConstants = goog.require('Blockly.internalConstants');
const deprecation = goog.require('Blockly.utils.deprecation');
const {getMainWorkspace} = goog.require('Blockly');
/**
@@ -26,7 +30,7 @@ goog.requireType('Blockly.Workspace');
* @param {string} name Language name of this generator.
* @constructor
*/
Blockly.Generator = function(name) {
const Generator = function(name) {
this.name_ = name;
this.FUNCTION_NAME_PLACEHOLDER_REGEXP_ =
new RegExp(this.FUNCTION_NAME_PLACEHOLDER_, 'g');
@@ -38,7 +42,7 @@ Blockly.Generator = function(name) {
* E.g. ' checkTimeout(%1);\n'
* @type {?string}
*/
Blockly.Generator.prototype.INFINITE_LOOP_TRAP = null;
Generator.prototype.INFINITE_LOOP_TRAP = null;
/**
* Arbitrary code to inject before every statement.
@@ -46,7 +50,7 @@ Blockly.Generator.prototype.INFINITE_LOOP_TRAP = null;
* E.g. 'highlight(%1);\n'
* @type {?string}
*/
Blockly.Generator.prototype.STATEMENT_PREFIX = null;
Generator.prototype.STATEMENT_PREFIX = null;
/**
* Arbitrary code to inject after every statement.
@@ -54,27 +58,27 @@ Blockly.Generator.prototype.STATEMENT_PREFIX = null;
* E.g. 'highlight(%1);\n'
* @type {?string}
*/
Blockly.Generator.prototype.STATEMENT_SUFFIX = null;
Generator.prototype.STATEMENT_SUFFIX = null;
/**
* The method of indenting. Defaults to two spaces, but language generators
* may override this to increase indent or change to tabs.
* @type {string}
*/
Blockly.Generator.prototype.INDENT = ' ';
Generator.prototype.INDENT = ' ';
/**
* Maximum length for a comment before wrapping. Does not account for
* indenting level.
* @type {number}
*/
Blockly.Generator.prototype.COMMENT_WRAP = 60;
Generator.prototype.COMMENT_WRAP = 60;
/**
* List of outer-inner pairings that do NOT require parentheses.
* @type {!Array<!Array<number>>}
*/
Blockly.Generator.prototype.ORDER_OVERRIDES = [];
Generator.prototype.ORDER_OVERRIDES = [];
/**
* Whether the init method has been called.
@@ -83,24 +87,24 @@ Blockly.Generator.prototype.ORDER_OVERRIDES = [];
* initialized. If this flag is untouched, it will have no effect.
* @type {?boolean}
*/
Blockly.Generator.prototype.isInitialized = null;
Generator.prototype.isInitialized = null;
/**
* Generate code for all blocks in the workspace to the specified language.
* @param {!Blockly.Workspace=} workspace Workspace to generate code from.
* @param {!Workspace=} workspace Workspace to generate code from.
* @return {string} Generated code.
*/
Blockly.Generator.prototype.workspaceToCode = function(workspace) {
Generator.prototype.workspaceToCode = function(workspace) {
if (!workspace) {
// Backwards compatibility from before there could be multiple workspaces.
console.warn('No workspace specified in workspaceToCode call. Guessing.');
workspace = Blockly.getMainWorkspace();
workspace = getMainWorkspace();
}
var code = [];
let code = [];
this.init(workspace);
var blocks = workspace.getTopBlocks(true);
for (var i = 0, block; (block = blocks[i]); i++) {
var line = this.blockToCode(block);
const blocks = workspace.getTopBlocks(true);
for (let i = 0, block; (block = blocks[i]); i++) {
let line = this.blockToCode(block);
if (Array.isArray(line)) {
// Value blocks return tuples of code and operator order.
// Top-level blocks don't care about operator order.
@@ -140,20 +144,20 @@ Blockly.Generator.prototype.workspaceToCode = function(workspace) {
* @param {string} prefix The common prefix.
* @return {string} The prefixed lines of code.
*/
Blockly.Generator.prototype.prefixLines = function(text, prefix) {
Generator.prototype.prefixLines = function(text, prefix) {
return prefix + text.replace(/(?!\n$)\n/g, '\n' + prefix);
};
/**
* Recursively spider a tree of blocks, returning all their comments.
* @param {!Blockly.Block} block The block from which to start spidering.
* @param {!Block} block The block from which to start spidering.
* @return {string} Concatenated list of comments.
*/
Blockly.Generator.prototype.allNestedComments = function(block) {
var comments = [];
var blocks = block.getDescendants(true);
for (var i = 0; i < blocks.length; i++) {
var comment = blocks[i].getCommentText();
Generator.prototype.allNestedComments = function(block) {
const comments = [];
const blocks = block.getDescendants(true);
for (let i = 0; i < blocks.length; i++) {
const comment = blocks[i].getCommentText();
if (comment) {
comments.push(comment);
}
@@ -168,13 +172,13 @@ Blockly.Generator.prototype.allNestedComments = function(block) {
/**
* Generate code for the specified block (and attached blocks).
* The generator must be initialized before calling this function.
* @param {Blockly.Block} block The block to generate code for.
* @param {Block} block The block to generate code for.
* @param {boolean=} opt_thisOnly True to generate code for only this statement.
* @return {string|!Array} For statement blocks, the generated code.
* For value blocks, an array containing the generated code and an
* operator order value. Returns '' if block is null.
*/
Blockly.Generator.prototype.blockToCode = function(block, opt_thisOnly) {
Generator.prototype.blockToCode = function(block, opt_thisOnly) {
if (this.isInitialized === false) {
console.warn(
'Generator init was not called before blockToCode was called.');
@@ -191,16 +195,17 @@ Blockly.Generator.prototype.blockToCode = function(block, opt_thisOnly) {
return opt_thisOnly ? '' : this.blockToCode(block.getChildren(false)[0]);
}
var func = this[block.type];
const func = this[block.type];
if (typeof func != 'function') {
throw Error('Language "' + this.name_ + '" does not know how to generate ' +
throw Error(
'Language "' + this.name_ + '" does not know how to generate ' +
'code for block type "' + block.type + '".');
}
// First argument to func.call is the value of 'this' in the generator.
// Prior to 24 September 2013 'this' was the only way to access the block.
// The current preferred method of accessing the block is through the second
// argument to func.call, which becomes the first parameter to the generator.
var code = func.call(block, block);
let code = func.call(block, block);
if (Array.isArray(code)) {
// Value blocks return tuples of code and operator order.
if (!block.outputConnection) {
@@ -224,22 +229,22 @@ Blockly.Generator.prototype.blockToCode = function(block, opt_thisOnly) {
/**
* Generate code representing the specified value input.
* @param {!Blockly.Block} block The block containing the input.
* @param {!Block} block The block containing the input.
* @param {string} name The name of the input.
* @param {number} outerOrder The maximum binding strength (minimum order value)
* of any operators adjacent to "block".
* @return {string} Generated code or '' if no blocks are connected or the
* specified input does not exist.
*/
Blockly.Generator.prototype.valueToCode = function(block, name, outerOrder) {
Generator.prototype.valueToCode = function(block, name, outerOrder) {
if (isNaN(outerOrder)) {
throw TypeError('Expecting valid order from block: ' + block.type);
}
var targetBlock = block.getInputTargetBlock(name);
const targetBlock = block.getInputTargetBlock(name);
if (!targetBlock) {
return '';
}
var tuple = this.blockToCode(targetBlock);
const tuple = this.blockToCode(targetBlock);
if (tuple === '') {
// Disabled block.
return '';
@@ -249,20 +254,20 @@ Blockly.Generator.prototype.valueToCode = function(block, name, outerOrder) {
if (!Array.isArray(tuple)) {
throw TypeError('Expecting tuple from value block: ' + targetBlock.type);
}
var code = tuple[0];
var innerOrder = tuple[1];
let code = tuple[0];
const innerOrder = tuple[1];
if (isNaN(innerOrder)) {
throw TypeError('Expecting valid order from value block: ' +
targetBlock.type);
throw TypeError(
'Expecting valid order from value block: ' + targetBlock.type);
}
if (!code) {
return '';
}
// Add parentheses if needed.
var parensNeeded = false;
var outerOrderClass = Math.floor(outerOrder);
var innerOrderClass = Math.floor(innerOrder);
let parensNeeded = false;
const outerOrderClass = Math.floor(outerOrder);
const innerOrderClass = Math.floor(innerOrder);
if (outerOrderClass <= innerOrderClass) {
if (outerOrderClass == innerOrderClass &&
(outerOrderClass == 0 || outerOrderClass == 99)) {
@@ -276,7 +281,7 @@ Blockly.Generator.prototype.valueToCode = function(block, name, outerOrder) {
// wrap the code in parentheses.
parensNeeded = true;
// Check for special exceptions.
for (var i = 0; i < this.ORDER_OVERRIDES.length; i++) {
for (let i = 0; i < this.ORDER_OVERRIDES.length; i++) {
if (this.ORDER_OVERRIDES[i][0] == outerOrder &&
this.ORDER_OVERRIDES[i][1] == innerOrder) {
parensNeeded = false;
@@ -298,17 +303,18 @@ Blockly.Generator.prototype.valueToCode = function(block, name, outerOrder) {
* statement input. Indent the code.
* This is mainly used in generators. When trying to generate code to evaluate
* look at using workspaceToCode or blockToCode.
* @param {!Blockly.Block} block The block containing the input.
* @param {!Block} block The block containing the input.
* @param {string} name The name of the input.
* @return {string} Generated code or '' if no blocks are connected.
*/
Blockly.Generator.prototype.statementToCode = function(block, name) {
var targetBlock = block.getInputTargetBlock(name);
var code = this.blockToCode(targetBlock);
Generator.prototype.statementToCode = function(block, name) {
const targetBlock = block.getInputTargetBlock(name);
let code = this.blockToCode(targetBlock);
// Value blocks must return code and order of operations info.
// Statement blocks must only return code.
if (typeof code != 'string') {
throw TypeError('Expecting code from statement block: ' +
throw TypeError(
'Expecting code from statement block: ' +
(targetBlock && targetBlock.type));
}
if (code) {
@@ -323,21 +329,24 @@ Blockly.Generator.prototype.statementToCode = function(block, name) {
* statement executes), and a statement prefix to the end of the loop block
* (right before the loop statement executes).
* @param {string} branch Code for loop contents.
* @param {!Blockly.Block} block Enclosing block.
* @param {!Block} block Enclosing block.
* @return {string} Loop contents, with infinite loop trap added.
*/
Blockly.Generator.prototype.addLoopTrap = function(branch, block) {
Generator.prototype.addLoopTrap = function(branch, block) {
if (this.INFINITE_LOOP_TRAP) {
branch = this.prefixLines(this.injectId(this.INFINITE_LOOP_TRAP, block),
this.INDENT) + branch;
branch = this.prefixLines(
this.injectId(this.INFINITE_LOOP_TRAP, block), this.INDENT) +
branch;
}
if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) {
branch = this.prefixLines(this.injectId(this.STATEMENT_SUFFIX, block),
this.INDENT) + branch;
branch = this.prefixLines(
this.injectId(this.STATEMENT_SUFFIX, block), this.INDENT) +
branch;
}
if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) {
branch = branch + this.prefixLines(this.injectId(this.STATEMENT_PREFIX,
block), this.INDENT);
branch = branch +
this.prefixLines(
this.injectId(this.STATEMENT_PREFIX, block), this.INDENT);
}
return branch;
};
@@ -346,11 +355,11 @@ Blockly.Generator.prototype.addLoopTrap = function(branch, block) {
* Inject a block ID into a message to replace '%1'.
* Used for STATEMENT_PREFIX, STATEMENT_SUFFIX, and INFINITE_LOOP_TRAP.
* @param {string} msg Code snippet with '%1'.
* @param {!Blockly.Block} block Block which has an ID.
* @param {!Block} block Block which has an ID.
* @return {string} Code snippet with ID.
*/
Blockly.Generator.prototype.injectId = function(msg, block) {
var id = block.id.replace(/\$/g, '$$$$'); // Issue 251.
Generator.prototype.injectId = function(msg, block) {
const id = block.id.replace(/\$/g, '$$$$'); // Issue 251.
return msg.replace(/%1/g, '\'' + id + '\'');
};
@@ -359,33 +368,33 @@ Blockly.Generator.prototype.injectId = function(msg, block) {
* @type {string}
* @protected
*/
Blockly.Generator.prototype.RESERVED_WORDS_ = '';
Generator.prototype.RESERVED_WORDS_ = '';
/**
* Add one or more words to the list of reserved words for this language.
* @param {string} words Comma-separated list of words to add to the list.
* No spaces. Duplicates are ok.
*/
Blockly.Generator.prototype.addReservedWords = function(words) {
Generator.prototype.addReservedWords = function(words) {
this.RESERVED_WORDS_ += words + ',';
};
/**
* This is used as a placeholder in functions defined using
* Blockly.Generator.provideFunction_. It must not be legal code that could
* Generator.provideFunction_. It must not be legal code that could
* legitimately appear in a function definition (or comment), and it must
* not confuse the regular expression parser.
* @type {string}
* @protected
*/
Blockly.Generator.prototype.FUNCTION_NAME_PLACEHOLDER_ = '{leCUI8hutHZI4480Dc}';
Generator.prototype.FUNCTION_NAME_PLACEHOLDER_ = '{leCUI8hutHZI4480Dc}';
/**
* A dictionary of definitions to be printed before the code.
* @type {!Object|undefined}
* @protected
*/
Blockly.Generator.prototype.definitions_;
Generator.prototype.definitions_;
/**
* A dictionary mapping desired function names in definitions_ to actual
@@ -393,36 +402,34 @@ Blockly.Generator.prototype.definitions_;
* @type {!Object|undefined}
* @protected
*/
Blockly.Generator.prototype.functionNames_;
Generator.prototype.functionNames_;
/**
* A database of variable and procedure names.
* @type {!Blockly.Names|undefined}
* @type {!Names|undefined}
* @protected
*/
Blockly.Generator.prototype.nameDB_;
Generator.prototype.nameDB_;
Object.defineProperty(Blockly.Generator.prototype, 'variableDB_', {
Object.defineProperty(Generator.prototype, 'variableDB_', {
/**
* Getter.
* @deprecated 'variableDB_' was renamed to 'nameDB_' (May 2021).
* @this {Blockly.Generator}
* @return {!Blockly.Names|undefined} Name database.
* @this {Generator}
* @return {!Names|undefined} Name database.
*/
get: function() {
Blockly.utils.deprecation.warn(
'variableDB_', 'May 2021', 'May 2026', 'nameDB_');
deprecation.warn('variableDB_', 'May 2021', 'May 2026', 'nameDB_');
return this.nameDB_;
},
/**
* Setter.
* @deprecated 'variableDB_' was renamed to 'nameDB_' (May 2021).
* @this {Blockly.Generator}
* @param {!Blockly.Names|undefined} nameDb New name database.
* @this {Generator}
* @param {!Names|undefined} nameDb New name database.
*/
set: function(nameDb) {
Blockly.utils.deprecation.warn(
'variableDB_', 'May 2021', 'May 2026', 'nameDB_');
deprecation.warn('variableDB_', 'May 2021', 'May 2026', 'nameDB_');
this.nameDB_ = nameDb;
}
});
@@ -439,7 +446,7 @@ Object.defineProperty(Blockly.Generator.prototype, 'variableDB_', {
* "listRandom", not "random"). There is no danger of colliding with reserved
* words, or user-defined variable or procedure names.
*
* The code gets output when Blockly.Generator.finish() is called.
* The code gets output when Generator.finish() is called.
*
* @param {string} desiredName The desired name of the function
* (e.g. mathIsPrime).
@@ -448,18 +455,18 @@ Object.defineProperty(Blockly.Generator.prototype, 'variableDB_', {
* from desiredName if the former has already been taken by the user.
* @protected
*/
Blockly.Generator.prototype.provideFunction_ = function(desiredName, code) {
Generator.prototype.provideFunction_ = function(desiredName, code) {
if (!this.definitions_[desiredName]) {
var functionName = this.nameDB_.getDistinctName(
desiredName, Blockly.internalConstants.PROCEDURE_CATEGORY_NAME);
const functionName = this.nameDB_.getDistinctName(
desiredName, internalConstants.PROCEDURE_CATEGORY_NAME);
this.functionNames_[desiredName] = functionName;
var codeText = code.join('\n').replace(
let codeText = code.join('\n').replace(
this.FUNCTION_NAME_PLACEHOLDER_REGEXP_, functionName);
// Change all ' ' indents into the desired indent.
// To avoid an infinite loop of replacements, change all indents to '\0'
// character first, then replace them all with the indent.
// We are assuming that no provided functions contain a literal null char.
var oldCodeText;
let oldCodeText;
while (oldCodeText != codeText) {
oldCodeText = codeText;
codeText = codeText.replace(/^(( {2})*) {2}/gm, '$1\0');
@@ -474,9 +481,9 @@ Blockly.Generator.prototype.provideFunction_ = function(desiredName, code) {
* Hook for code to run before code generation starts.
* Subclasses may override this, e.g. to initialise the database of variable
* names.
* @param {!Blockly.Workspace} _workspace Workspace to generate code from.
* @param {!Workspace} _workspace Workspace to generate code from.
*/
Blockly.Generator.prototype.init = function(_workspace) {
Generator.prototype.init = function(_workspace) {
// Optionally override
// Create a dictionary of definitions to be printed before the code.
this.definitions_ = Object.create(null);
@@ -492,14 +499,14 @@ Blockly.Generator.prototype.init = function(_workspace) {
* Subclasses may override this, e.g. to generate code for statements following
* the block, or to handle comments for the specified block and any connected
* value blocks.
* @param {!Blockly.Block} _block The current block.
* @param {!Block} _block The current block.
* @param {string} code The code created for this block.
* @param {boolean=} _opt_thisOnly True to generate code for only this
* statement.
* @return {string} Code with comments and subsequent blocks added.
* @protected
*/
Blockly.Generator.prototype.scrub_ = function(_block, code, _opt_thisOnly) {
Generator.prototype.scrub_ = function(_block, code, _opt_thisOnly) {
// Optionally override
return code;
};
@@ -511,7 +518,7 @@ Blockly.Generator.prototype.scrub_ = function(_block, code, _opt_thisOnly) {
* @param {string} code Generated code.
* @return {string} Completed code.
*/
Blockly.Generator.prototype.finish = function(code) {
Generator.prototype.finish = function(code) {
// Optionally override
// Clean up temporary data.
delete this.definitions_;
@@ -527,7 +534,9 @@ Blockly.Generator.prototype.finish = function(code) {
* @param {string} line Line of generated code.
* @return {string} Legal line of code.
*/
Blockly.Generator.prototype.scrubNakedValue = function(line) {
Generator.prototype.scrubNakedValue = function(line) {
// Optionally override
return line;
};
exports = Generator;

View File

@@ -11,30 +11,41 @@
*/
'use strict';
goog.provide('Blockly.Gesture');
goog.module('Blockly.Gesture');
goog.module.declareLegacyNamespace();
goog.require('Blockly.blockAnimations');
// TODO(#5073): Add Blockly require after fixing circular dependency.
// goog.require('Blockly');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const BubbleDragger = goog.require('Blockly.BubbleDragger');
const Coordinate = goog.require('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
/* eslint-disable-next-line no-unused-vars */
const IBlockDragger = goog.requireType('Blockly.IBlockDragger');
/* eslint-disable-next-line no-unused-vars */
const IBubble = goog.requireType('Blockly.IBubble');
/* eslint-disable-next-line no-unused-vars */
const IFlyout = goog.requireType('Blockly.IFlyout');
const Tooltip = goog.require('Blockly.Tooltip');
const Touch = goog.require('Blockly.Touch');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.require('Blockly.Workspace');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const WorkspaceDragger = goog.require('Blockly.WorkspaceDragger');
const blockAnimations = goog.require('Blockly.blockAnimations');
const browserEvents = goog.require('Blockly.browserEvents');
const internalConstants = goog.require('Blockly.internalConstants');
const registry = goog.require('Blockly.registry');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.BlockDragger');
goog.require('Blockly.browserEvents');
goog.require('Blockly.BubbleDragger');
goog.require('Blockly.Events');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.Click');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Tooltip');
goog.require('Blockly.Touch');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.Workspace');
goog.require('Blockly.WorkspaceDragger');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.Field');
goog.requireType('Blockly.IBlockDragger');
goog.requireType('Blockly.IBubble');
goog.requireType('Blockly.IFlyout');
goog.requireType('Blockly.WorkspaceSvg');
/**
@@ -46,15 +57,15 @@ goog.requireType('Blockly.WorkspaceSvg');
/**
* Class for one gesture.
* @param {!Event} e The event that kicked off this gesture.
* @param {!Blockly.WorkspaceSvg} creatorWorkspace The workspace that created
* @param {!WorkspaceSvg} creatorWorkspace The workspace that created
* this gesture and has a reference to it.
* @constructor
*/
Blockly.Gesture = function(e, creatorWorkspace) {
const Gesture = function(e, creatorWorkspace) {
/**
* The position of the mouse when the gesture started. Units are CSS pixels,
* with (0, 0) at the top left of the browser window (mouseEvent clientX/Y).
* @type {Blockly.utils.Coordinate}
* @type {Coordinate}
* @private
*/
this.mouseDownXY_ = null;
@@ -62,15 +73,15 @@ Blockly.Gesture = function(e, creatorWorkspace) {
/**
* How far the mouse has moved during this drag, in pixel units.
* (0, 0) is at this.mouseDownXY_.
* @type {!Blockly.utils.Coordinate}
* @type {!Coordinate}
* @private
*/
this.currentDragDeltaXY_ = new Blockly.utils.Coordinate(0, 0);
this.currentDragDeltaXY_ = new Coordinate(0, 0);
/**
* The bubble that the gesture started on, or null if it did not start on a
* bubble.
* @type {Blockly.IBubble}
* @type {IBubble}
* @private
*/
this.startBubble_ = null;
@@ -78,7 +89,7 @@ Blockly.Gesture = function(e, creatorWorkspace) {
/**
* The field that the gesture started on, or null if it did not start on a
* field.
* @type {Blockly.Field}
* @type {Field}
* @private
*/
this.startField_ = null;
@@ -86,7 +97,7 @@ Blockly.Gesture = function(e, creatorWorkspace) {
/**
* The block that the gesture started on, or null if it did not start on a
* block.
* @type {Blockly.BlockSvg}
* @type {BlockSvg}
* @private
*/
this.startBlock_ = null;
@@ -96,7 +107,7 @@ Blockly.Gesture = function(e, creatorWorkspace) {
* shadow block, this is the first non-shadow parent of the block. If the
* gesture started in the flyout, this is the root block of the block group
* that was clicked or dragged.
* @type {Blockly.BlockSvg}
* @type {BlockSvg}
* @private
*/
this.targetBlock_ = null;
@@ -105,7 +116,7 @@ Blockly.Gesture = function(e, creatorWorkspace) {
* The workspace that the gesture started on. There may be multiple
* workspaces on a page; this is more accurate than using
* Blockly.getMainWorkspace().
* @type {Blockly.WorkspaceSvg}
* @type {WorkspaceSvg}
* @protected
*/
this.startWorkspace_ = null;
@@ -115,7 +126,7 @@ Blockly.Gesture = function(e, creatorWorkspace) {
* to the gesture, which will need to be cleared at deletion.
* This may be different from the start workspace. For instance, a flyout is
* a workspace, but its parent workspace manages gestures for it.
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @private
*/
this.creatorWorkspace_ = creatorWorkspace;
@@ -160,7 +171,7 @@ Blockly.Gesture = function(e, creatorWorkspace) {
/**
* A handle to use to unbind a mouse move listener at the end of a drag.
* Opaque data returned from Blockly.bindEventWithChecks_.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @protected
*/
this.onMoveWrapper_ = null;
@@ -168,21 +179,21 @@ Blockly.Gesture = function(e, creatorWorkspace) {
/**
* A handle to use to unbind a mouse up listener at the end of a drag.
* Opaque data returned from Blockly.bindEventWithChecks_.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @protected
*/
this.onUpWrapper_ = null;
/**
* The object tracking a bubble drag, or null if none is in progress.
* @type {Blockly.BubbleDragger}
* @type {BubbleDragger}
* @private
*/
this.bubbleDragger_ = null;
/**
* The object tracking a block drag, or null if none is in progress.
* @type {?Blockly.IBlockDragger}
* @type {?IBlockDragger}
* @private
*/
this.blockDragger_ = null;
@@ -190,14 +201,14 @@ Blockly.Gesture = function(e, creatorWorkspace) {
/**
* The object tracking a workspace or flyout workspace drag, or null if none
* is in progress.
* @type {Blockly.WorkspaceDragger}
* @type {WorkspaceDragger}
* @private
*/
this.workspaceDragger_ = null;
/**
* The flyout a gesture started in, if any.
* @type {Blockly.IFlyout}
* @type {IFlyout}
* @private
*/
this.flyout_ = null;
@@ -229,24 +240,24 @@ Blockly.Gesture = function(e, creatorWorkspace) {
* @type {boolean}
* @private
*/
this.healStack_ = !Blockly.internalConstants.DRAG_STACK;
this.healStack_ = !internalConstants.DRAG_STACK;
};
/**
* Sever all links from this object.
* @package
*/
Blockly.Gesture.prototype.dispose = function() {
Blockly.Touch.clearTouchIdentifier();
Blockly.Tooltip.unblock();
Gesture.prototype.dispose = function() {
Touch.clearTouchIdentifier();
Tooltip.unblock();
// Clear the owner's reference to this gesture.
this.creatorWorkspace_.clearGesture();
if (this.onMoveWrapper_) {
Blockly.browserEvents.unbind(this.onMoveWrapper_);
browserEvents.unbind(this.onMoveWrapper_);
}
if (this.onUpWrapper_) {
Blockly.browserEvents.unbind(this.onUpWrapper_);
browserEvents.unbind(this.onUpWrapper_);
}
if (this.blockDragger_) {
@@ -265,9 +276,9 @@ Blockly.Gesture.prototype.dispose = function() {
* @param {!Event} e The most recent mouse or touch event.
* @private
*/
Blockly.Gesture.prototype.updateFromEvent_ = function(e) {
var currentXY = new Blockly.utils.Coordinate(e.clientX, e.clientY);
var changed = this.updateDragDelta_(currentXY);
Gesture.prototype.updateFromEvent_ = function(e) {
const currentXY = new Coordinate(e.clientX, e.clientY);
const changed = this.updateDragDelta_(currentXY);
// Exceeded the drag radius for the first time.
if (changed) {
this.updateIsDragging_();
@@ -278,25 +289,23 @@ Blockly.Gesture.prototype.updateFromEvent_ = function(e) {
/**
* DO MATH to set currentDragDeltaXY_ based on the most recent mouse position.
* @param {!Blockly.utils.Coordinate} currentXY The most recent mouse/pointer
* @param {!Coordinate} currentXY The most recent mouse/pointer
* position, in pixel units, with (0, 0) at the window's top left corner.
* @return {boolean} True if the drag just exceeded the drag radius for the
* first time.
* @private
*/
Blockly.Gesture.prototype.updateDragDelta_ = function(currentXY) {
this.currentDragDeltaXY_ = Blockly.utils.Coordinate.difference(
Gesture.prototype.updateDragDelta_ = function(currentXY) {
this.currentDragDeltaXY_ = Coordinate.difference(
currentXY,
/** @type {!Blockly.utils.Coordinate} */ (this.mouseDownXY_));
/** @type {!Coordinate} */ (this.mouseDownXY_));
if (!this.hasExceededDragRadius_) {
var currentDragDelta =
Blockly.utils.Coordinate.magnitude(this.currentDragDeltaXY_);
const currentDragDelta = Coordinate.magnitude(this.currentDragDeltaXY_);
// The flyout has a different drag radius from the rest of Blockly.
var limitRadius = this.flyout_ ?
Blockly.internalConstants.FLYOUT_DRAG_RADIUS :
Blockly.internalConstants.DRAG_RADIUS;
const limitRadius = this.flyout_ ? internalConstants.FLYOUT_DRAG_RADIUS :
internalConstants.DRAG_RADIUS;
this.hasExceededDragRadius_ = currentDragDelta > limitRadius;
return this.hasExceededDragRadius_;
@@ -314,7 +323,7 @@ Blockly.Gesture.prototype.updateDragDelta_ = function(currentXY) {
* @return {boolean} True if a block is being dragged from the flyout.
* @private
*/
Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() {
Gesture.prototype.updateIsDraggingFromFlyout_ = function() {
if (!this.targetBlock_) {
return false;
}
@@ -327,8 +336,8 @@ Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() {
this.startWorkspace_.updateScreenCalculationsIfScrolled();
// Start the event group now, so that the same event group is used for block
// creation and block dragging.
if (!Blockly.Events.getGroup()) {
Blockly.Events.setGroup(true);
if (!Events.getGroup()) {
Events.setGroup(true);
}
// The start block is no longer relevant, because this is a drag.
this.startBlock_ = null;
@@ -348,7 +357,7 @@ Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() {
* @return {boolean} True if a bubble is being dragged.
* @private
*/
Blockly.Gesture.prototype.updateIsDraggingBubble_ = function() {
Gesture.prototype.updateIsDraggingBubble_ = function() {
if (!this.startBubble_) {
return false;
}
@@ -367,7 +376,7 @@ Blockly.Gesture.prototype.updateIsDraggingBubble_ = function() {
* @return {boolean} True if a block is being dragged.
* @private
*/
Blockly.Gesture.prototype.updateIsDraggingBlock_ = function() {
Gesture.prototype.updateIsDraggingBlock_ = function() {
if (!this.targetBlock_) {
return false;
}
@@ -393,8 +402,8 @@ Blockly.Gesture.prototype.updateIsDraggingBlock_ = function() {
* WorkspaceDragger and starts the drag.
* @private
*/
Blockly.Gesture.prototype.updateIsDraggingWorkspace_ = function() {
var wsMovable = this.flyout_ ?
Gesture.prototype.updateIsDraggingWorkspace_ = function() {
const wsMovable = this.flyout_ ?
this.flyout_.isScrollable() :
this.startWorkspace_ && this.startWorkspace_.isDraggable();
@@ -402,8 +411,8 @@ Blockly.Gesture.prototype.updateIsDraggingWorkspace_ = function() {
return;
}
this.workspaceDragger_ = new Blockly.WorkspaceDragger(
/** @type {!Blockly.WorkspaceSvg} */ (this.startWorkspace_));
this.workspaceDragger_ = new WorkspaceDragger(
/** @type {!WorkspaceSvg} */ (this.startWorkspace_));
this.isDraggingWorkspace_ = true;
this.workspaceDragger_.startDrag();
@@ -415,7 +424,7 @@ Blockly.Gesture.prototype.updateIsDraggingWorkspace_ = function() {
* drag radius is exceeded. It should be called no more than once per gesture.
* @private
*/
Blockly.Gesture.prototype.updateIsDragging_ = function() {
Gesture.prototype.updateIsDragging_ = function() {
// Sanity check.
if (this.calledUpdateIsDragging_) {
throw Error('updateIsDragging_ should only be called once per gesture.');
@@ -438,13 +447,13 @@ Blockly.Gesture.prototype.updateIsDragging_ = function() {
* Create a block dragger and start dragging the selected block.
* @private
*/
Blockly.Gesture.prototype.startDraggingBlock_ = function() {
var BlockDraggerClass = Blockly.registry.getClassFromOptions(
Blockly.registry.Type.BLOCK_DRAGGER, this.creatorWorkspace_.options, true);
Gesture.prototype.startDraggingBlock_ = function() {
const BlockDraggerClass = registry.getClassFromOptions(
registry.Type.BLOCK_DRAGGER, this.creatorWorkspace_.options, true);
this.blockDragger_ = new BlockDraggerClass(
/** @type {!Blockly.BlockSvg} */ (this.targetBlock_),
/** @type {!Blockly.WorkspaceSvg} */ (this.startWorkspace_));
/** @type {!BlockSvg} */ (this.targetBlock_),
/** @type {!WorkspaceSvg} */ (this.startWorkspace_));
this.blockDragger_.startDrag(this.currentDragDeltaXY_, this.healStack_);
this.blockDragger_.drag(this.mostRecentEvent_, this.currentDragDeltaXY_);
};
@@ -454,10 +463,10 @@ Blockly.Gesture.prototype.startDraggingBlock_ = function() {
* @private
*/
// TODO (fenichel): Possibly combine this and startDraggingBlock_.
Blockly.Gesture.prototype.startDraggingBubble_ = function() {
this.bubbleDragger_ = new Blockly.BubbleDragger(
/** @type {!Blockly.IBubble} */ (this.startBubble_),
/** @type {!Blockly.WorkspaceSvg} */ (this.startWorkspace_));
Gesture.prototype.startDraggingBubble_ = function() {
this.bubbleDragger_ = new BubbleDragger(
/** @type {!IBubble} */ (this.startBubble_),
/** @type {!WorkspaceSvg} */ (this.startWorkspace_));
this.bubbleDragger_.startBubbleDrag();
this.bubbleDragger_.dragBubble(
this.mostRecentEvent_, this.currentDragDeltaXY_);
@@ -468,14 +477,14 @@ Blockly.Gesture.prototype.startDraggingBubble_ = function() {
* @param {!Event} e A mouse down or touch start event.
* @package
*/
Blockly.Gesture.prototype.doStart = function(e) {
if (Blockly.utils.isTargetInput(e)) {
Gesture.prototype.doStart = function(e) {
if (utils.isTargetInput(e)) {
this.cancel();
return;
}
this.hasStarted_ = true;
Blockly.blockAnimations.disconnectUiStop();
blockAnimations.disconnectUiStop();
this.startWorkspace_.updateScreenCalculationsIfScrolled();
if (this.startWorkspace_.isMutator) {
// Mutator's coordinate system could be out of date because the bubble was
@@ -490,13 +499,13 @@ Blockly.Gesture.prototype.doStart = function(e) {
this.startWorkspace_.markFocused();
this.mostRecentEvent_ = e;
Blockly.Tooltip.block();
Tooltip.block();
if (this.targetBlock_) {
this.targetBlock_.select();
}
if (Blockly.utils.isRightButton(e)) {
if (utils.isRightButton(e)) {
this.handleRightClick(e);
return;
}
@@ -507,7 +516,7 @@ Blockly.Gesture.prototype.doStart = function(e) {
Blockly.longStart(e, this);
}
this.mouseDownXY_ = new Blockly.utils.Coordinate(e.clientX, e.clientY);
this.mouseDownXY_ = new Coordinate(e.clientX, e.clientY);
this.healStack_ = e.altKey || e.ctrlKey || e.metaKey;
this.bindMouseEvents(e);
@@ -518,10 +527,10 @@ Blockly.Gesture.prototype.doStart = function(e) {
* @param {!Event} e A mouse down or touch start event.
* @package
*/
Blockly.Gesture.prototype.bindMouseEvents = function(e) {
this.onMoveWrapper_ = Blockly.browserEvents.conditionalBind(
Gesture.prototype.bindMouseEvents = function(e) {
this.onMoveWrapper_ = browserEvents.conditionalBind(
document, 'mousemove', null, this.handleMove.bind(this));
this.onUpWrapper_ = Blockly.browserEvents.conditionalBind(
this.onUpWrapper_ = browserEvents.conditionalBind(
document, 'mouseup', null, this.handleUp.bind(this));
e.preventDefault();
@@ -533,13 +542,12 @@ Blockly.Gesture.prototype.bindMouseEvents = function(e) {
* @param {!Event} e A mouse move or touch move event.
* @package
*/
Blockly.Gesture.prototype.handleMove = function(e) {
Gesture.prototype.handleMove = function(e) {
this.updateFromEvent_(e);
if (this.isDraggingWorkspace_) {
this.workspaceDragger_.drag(this.currentDragDeltaXY_);
} else if (this.isDraggingBlock_) {
this.blockDragger_.drag(
this.mostRecentEvent_, this.currentDragDeltaXY_);
this.blockDragger_.drag(this.mostRecentEvent_, this.currentDragDeltaXY_);
} else if (this.isDraggingBubble_) {
this.bubbleDragger_.dragBubble(
this.mostRecentEvent_, this.currentDragDeltaXY_);
@@ -553,7 +561,7 @@ Blockly.Gesture.prototype.handleMove = function(e) {
* @param {!Event} e A mouse up or touch end event.
* @package
*/
Blockly.Gesture.prototype.handleUp = function(e) {
Gesture.prototype.handleUp = function(e) {
this.updateFromEvent_(e);
Blockly.longStop_();
@@ -595,7 +603,7 @@ Blockly.Gesture.prototype.handleUp = function(e) {
* end the drag at the most recent location.
* @package
*/
Blockly.Gesture.prototype.cancel = function() {
Gesture.prototype.cancel = function() {
// Disposing of a block cancels in-progress drags, but dragging to a delete
// area disposes of a block and leads to recursive disposal. Break that cycle.
if (this.isEnding_) {
@@ -606,8 +614,7 @@ Blockly.Gesture.prototype.cancel = function() {
this.bubbleDragger_.endBubbleDrag(
this.mostRecentEvent_, this.currentDragDeltaXY_);
} else if (this.isDraggingBlock_) {
this.blockDragger_.endDrag(
this.mostRecentEvent_, this.currentDragDeltaXY_);
this.blockDragger_.endDrag(this.mostRecentEvent_, this.currentDragDeltaXY_);
} else if (this.isDraggingWorkspace_) {
this.workspaceDragger_.endDrag(this.currentDragDeltaXY_);
}
@@ -619,7 +626,7 @@ Blockly.Gesture.prototype.cancel = function() {
* @param {!Event} e A mouse move or touch move event.
* @package
*/
Blockly.Gesture.prototype.handleRightClick = function(e) {
Gesture.prototype.handleRightClick = function(e) {
if (this.targetBlock_) {
this.bringBlockToFront_();
Blockly.hideChaff(!!this.flyout_);
@@ -641,10 +648,10 @@ Blockly.Gesture.prototype.handleRightClick = function(e) {
/**
* Handle a mousedown/touchstart event on a workspace.
* @param {!Event} e A mouse down or touch start event.
* @param {!Blockly.WorkspaceSvg} ws The workspace the event hit.
* @param {!WorkspaceSvg} ws The workspace the event hit.
* @package
*/
Blockly.Gesture.prototype.handleWsStart = function(e, ws) {
Gesture.prototype.handleWsStart = function(e, ws) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.handleWsStart, ' +
@@ -657,21 +664,20 @@ Blockly.Gesture.prototype.handleWsStart = function(e, ws) {
/**
* Fires a workspace click event.
* @param {!Blockly.WorkspaceSvg} ws The workspace that a user clicks on.
* @param {!WorkspaceSvg} ws The workspace that a user clicks on.
* @private
*/
Blockly.Gesture.prototype.fireWorkspaceClick_ = function(ws) {
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.CLICK))(
null, ws.id, 'workspace'));
Gesture.prototype.fireWorkspaceClick_ = function(ws) {
Events.fire(new (Events.get(Events.CLICK))(null, ws.id, 'workspace'));
};
/**
* Handle a mousedown/touchstart event on a flyout.
* @param {!Event} e A mouse down or touch start event.
* @param {!Blockly.IFlyout} flyout The flyout the event hit.
* @param {!IFlyout} flyout The flyout the event hit.
* @package
*/
Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) {
Gesture.prototype.handleFlyoutStart = function(e, flyout) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.handleFlyoutStart, ' +
@@ -684,10 +690,10 @@ Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) {
/**
* Handle a mousedown/touchstart event on a block.
* @param {!Event} e A mouse down or touch start event.
* @param {!Blockly.BlockSvg} block The block the event hit.
* @param {!BlockSvg} block The block the event hit.
* @package
*/
Blockly.Gesture.prototype.handleBlockStart = function(e, block) {
Gesture.prototype.handleBlockStart = function(e, block) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.handleBlockStart, ' +
@@ -700,10 +706,10 @@ Blockly.Gesture.prototype.handleBlockStart = function(e, block) {
/**
* Handle a mousedown/touchstart event on a bubble.
* @param {!Event} e A mouse down or touch start event.
* @param {!Blockly.IBubble} bubble The bubble the event hit.
* @param {!IBubble} bubble The bubble the event hit.
* @package
*/
Blockly.Gesture.prototype.handleBubbleStart = function(e, bubble) {
Gesture.prototype.handleBubbleStart = function(e, bubble) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.handleBubbleStart, ' +
@@ -721,7 +727,7 @@ Blockly.Gesture.prototype.handleBubbleStart = function(e, bubble) {
* Execute a bubble click.
* @private
*/
Blockly.Gesture.prototype.doBubbleClick_ = function() {
Gesture.prototype.doBubbleClick_ = function() {
// TODO (#1673): Consistent handling of single clicks.
this.startBubble_.setFocus && this.startBubble_.setFocus();
this.startBubble_.select && this.startBubble_.select();
@@ -731,7 +737,7 @@ Blockly.Gesture.prototype.doBubbleClick_ = function() {
* Execute a field click.
* @private
*/
Blockly.Gesture.prototype.doFieldClick_ = function() {
Gesture.prototype.doFieldClick_ = function() {
this.startField_.showEditor(this.mostRecentEvent_);
this.bringBlockToFront_();
};
@@ -740,24 +746,24 @@ Blockly.Gesture.prototype.doFieldClick_ = function() {
* Execute a block click.
* @private
*/
Blockly.Gesture.prototype.doBlockClick_ = function() {
Gesture.prototype.doBlockClick_ = function() {
// Block click in an autoclosing flyout.
if (this.flyout_ && this.flyout_.autoClose) {
if (this.targetBlock_.isEnabled()) {
if (!Blockly.Events.getGroup()) {
Blockly.Events.setGroup(true);
if (!Events.getGroup()) {
Events.setGroup(true);
}
var newBlock = this.flyout_.createBlock(this.targetBlock_);
const newBlock = this.flyout_.createBlock(this.targetBlock_);
newBlock.scheduleSnapAndBump();
}
} else {
// Clicks events are on the start block, even if it was a shadow.
var event = new (Blockly.Events.get(Blockly.Events.CLICK))(
const event = new (Events.get(Events.CLICK))(
this.startBlock_, this.startWorkspace_.id, 'block');
Blockly.Events.fire(event);
Events.fire(event);
}
this.bringBlockToFront_();
Blockly.Events.setGroup(false);
Events.setGroup(false);
};
/**
@@ -766,8 +772,8 @@ Blockly.Gesture.prototype.doBlockClick_ = function() {
* @param {!Event} _e A mouse up or touch end event.
* @private
*/
Blockly.Gesture.prototype.doWorkspaceClick_ = function(_e) {
var ws = this.creatorWorkspace_;
Gesture.prototype.doWorkspaceClick_ = function(_e) {
const ws = this.creatorWorkspace_;
if (Blockly.selected) {
Blockly.selected.unselect();
}
@@ -783,7 +789,7 @@ Blockly.Gesture.prototype.doWorkspaceClick_ = function(_e) {
* not occluded by other blocks.
* @private
*/
Blockly.Gesture.prototype.bringBlockToFront_ = function() {
Gesture.prototype.bringBlockToFront_ = function() {
// Blocks in the flyout don't overlap, so skip the work.
if (this.targetBlock_ && !this.flyout_) {
this.targetBlock_.bringToFront();
@@ -794,10 +800,10 @@ Blockly.Gesture.prototype.bringBlockToFront_ = function() {
/**
* Record the field that a gesture started on.
* @param {Blockly.Field} field The field the gesture started on.
* @param {Field} field The field the gesture started on.
* @package
*/
Blockly.Gesture.prototype.setStartField = function(field) {
Gesture.prototype.setStartField = function(field) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.setStartField, ' +
@@ -810,10 +816,10 @@ Blockly.Gesture.prototype.setStartField = function(field) {
/**
* Record the bubble that a gesture started on
* @param {Blockly.IBubble} bubble The bubble the gesture started on.
* @param {IBubble} bubble The bubble the gesture started on.
* @package
*/
Blockly.Gesture.prototype.setStartBubble = function(bubble) {
Gesture.prototype.setStartBubble = function(bubble) {
if (!this.startBubble_) {
this.startBubble_ = bubble;
}
@@ -822,10 +828,10 @@ Blockly.Gesture.prototype.setStartBubble = function(bubble) {
/**
* Record the block that a gesture started on, and set the target block
* appropriately.
* @param {Blockly.BlockSvg} block The block the gesture started on.
* @param {BlockSvg} block The block the gesture started on.
* @package
*/
Blockly.Gesture.prototype.setStartBlock = function(block) {
Gesture.prototype.setStartBlock = function(block) {
// If the gesture already went through a bubble, don't set the start block.
if (!this.startBlock_ && !this.startBubble_) {
this.startBlock_ = block;
@@ -841,10 +847,10 @@ Blockly.Gesture.prototype.setStartBlock = function(block) {
* Record the block that a gesture targets, meaning the block that will be
* dragged if this turns into a drag. If this block is a shadow, that will be
* its first non-shadow parent.
* @param {Blockly.BlockSvg} block The block the gesture targets.
* @param {BlockSvg} block The block the gesture targets.
* @private
*/
Blockly.Gesture.prototype.setTargetBlock_ = function(block) {
Gesture.prototype.setTargetBlock_ = function(block) {
if (block.isShadow()) {
this.setTargetBlock_(block.getParent());
} else {
@@ -854,10 +860,10 @@ Blockly.Gesture.prototype.setTargetBlock_ = function(block) {
/**
* Record the workspace that a gesture started on.
* @param {Blockly.WorkspaceSvg} ws The workspace the gesture started on.
* @param {WorkspaceSvg} ws The workspace the gesture started on.
* @private
*/
Blockly.Gesture.prototype.setStartWorkspace_ = function(ws) {
Gesture.prototype.setStartWorkspace_ = function(ws) {
if (!this.startWorkspace_) {
this.startWorkspace_ = ws;
}
@@ -865,10 +871,10 @@ Blockly.Gesture.prototype.setStartWorkspace_ = function(ws) {
/**
* Record the flyout that a gesture started on.
* @param {Blockly.IFlyout} flyout The flyout the gesture started on.
* @param {IFlyout} flyout The flyout the gesture started on.
* @private
*/
Blockly.Gesture.prototype.setStartFlyout_ = function(flyout) {
Gesture.prototype.setStartFlyout_ = function(flyout) {
if (!this.flyout_) {
this.flyout_ = flyout;
}
@@ -886,9 +892,9 @@ Blockly.Gesture.prototype.setStartFlyout_ = function(flyout) {
* @return {boolean} Whether this gesture was a click on a bubble.
* @private
*/
Blockly.Gesture.prototype.isBubbleClick_ = function() {
Gesture.prototype.isBubbleClick_ = function() {
// A bubble click starts on a bubble and never escapes the drag radius.
var hasStartBubble = !!this.startBubble_;
const hasStartBubble = !!this.startBubble_;
return hasStartBubble && !this.hasExceededDragRadius_;
};
@@ -898,10 +904,10 @@ Blockly.Gesture.prototype.isBubbleClick_ = function() {
* @return {boolean} Whether this gesture was a click on a block.
* @private
*/
Blockly.Gesture.prototype.isBlockClick_ = function() {
Gesture.prototype.isBlockClick_ = function() {
// A block click starts on a block, never escapes the drag radius, and is not
// a field click.
var hasStartBlock = !!this.startBlock_;
const hasStartBlock = !!this.startBlock_;
return hasStartBlock && !this.hasExceededDragRadius_ && !this.isFieldClick_();
};
@@ -911,8 +917,8 @@ Blockly.Gesture.prototype.isBlockClick_ = function() {
* @return {boolean} Whether this gesture was a click on a field.
* @private
*/
Blockly.Gesture.prototype.isFieldClick_ = function() {
var fieldClickable =
Gesture.prototype.isFieldClick_ = function() {
const fieldClickable =
this.startField_ ? this.startField_.isClickable() : false;
return fieldClickable && !this.hasExceededDragRadius_ &&
(!this.flyout_ || !this.flyout_.autoClose);
@@ -924,8 +930,8 @@ Blockly.Gesture.prototype.isFieldClick_ = function() {
* @return {boolean} Whether this gesture was a click on a workspace.
* @private
*/
Blockly.Gesture.prototype.isWorkspaceClick_ = function() {
var onlyTouchedWorkspace =
Gesture.prototype.isWorkspaceClick_ = function() {
const onlyTouchedWorkspace =
!this.startBlock_ && !this.startBubble_ && !this.startField_;
return onlyTouchedWorkspace && !this.hasExceededDragRadius_;
};
@@ -939,7 +945,7 @@ Blockly.Gesture.prototype.isWorkspaceClick_ = function() {
* @return {boolean} True if this gesture is a drag of a workspace or block.
* @package
*/
Blockly.Gesture.prototype.isDragging = function() {
Gesture.prototype.isDragging = function() {
return this.isDraggingWorkspace_ || this.isDraggingBlock_ ||
this.isDraggingBubble_;
};
@@ -951,18 +957,18 @@ Blockly.Gesture.prototype.isDragging = function() {
* @return {boolean} Whether this gesture was a click on a workspace.
* @package
*/
Blockly.Gesture.prototype.hasStarted = function() {
Gesture.prototype.hasStarted = function() {
return this.hasStarted_;
};
/**
* Get a list of the insertion markers that currently exist. Block drags have
* 0, 1, or 2 insertion markers.
* @return {!Array<!Blockly.BlockSvg>} A possibly empty list of insertion
* @return {!Array<!BlockSvg>} A possibly empty list of insertion
* marker blocks.
* @package
*/
Blockly.Gesture.prototype.getInsertionMarkers = function() {
Gesture.prototype.getInsertionMarkers = function() {
if (this.blockDragger_) {
return this.blockDragger_.getInsertionMarkers();
}
@@ -972,10 +978,10 @@ Blockly.Gesture.prototype.getInsertionMarkers = function() {
/**
* Gets the current dragger if an item is being dragged. Null if nothing is
* being dragged.
* @return {!Blockly.WorkspaceDragger|!Blockly.BubbleDragger|!Blockly.IBlockDragger|null}
* @return {!WorkspaceDragger|!BubbleDragger|!IBlockDragger|null}
* The dragger that is currently in use or null if no drag is in progress.
*/
Blockly.Gesture.prototype.getCurrentDragger = function() {
Gesture.prototype.getCurrentDragger = function() {
if (this.isDraggingBlock_) {
return this.blockDragger_;
} else if (this.isDraggingWorkspace_) {
@@ -990,12 +996,14 @@ Blockly.Gesture.prototype.getCurrentDragger = function() {
* Is a drag or other gesture currently in progress on any workspace?
* @return {boolean} True if gesture is occurring.
*/
Blockly.Gesture.inProgress = function() {
var workspaces = Blockly.Workspace.getAll();
for (var i = 0, workspace; (workspace = workspaces[i]); i++) {
Gesture.inProgress = function() {
const workspaces = Workspace.getAll();
for (let i = 0, workspace; (workspace = workspaces[i]); i++) {
if (workspace.currentGesture_) {
return true;
}
}
return false;
};
exports = Gesture;

View File

@@ -11,11 +11,12 @@
*/
'use strict';
goog.provide('Blockly.Grid');
goog.module('Blockly.Grid');
goog.module.declareLegacyNamespace();
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.userAgent');
const Svg = goog.require('Blockly.utils.Svg');
const dom = goog.require('Blockly.utils.dom');
const userAgent = goog.require('Blockly.utils.userAgent');
/**
@@ -27,7 +28,7 @@ goog.require('Blockly.utils.userAgent');
* https://developers.google.com/blockly/guides/configure/web/grid
* @constructor
*/
Blockly.Grid = function(pattern, options) {
const Grid = function(pattern, options) {
/**
* The grid's SVG pattern, created during injection.
* @type {!SVGElement}
@@ -61,8 +62,8 @@ Blockly.Grid = function(pattern, options) {
* @type {SVGElement}
* @private
*/
this.line2_ = this.line1_ &&
(/** @type {SVGElement} */ (this.line1_.nextSibling));
this.line2_ =
this.line1_ && (/** @type {SVGElement} */ (this.line1_.nextSibling));
/**
* Whether blocks should snap to the grid.
@@ -78,14 +79,14 @@ Blockly.Grid = function(pattern, options) {
* @type {number}
* @private
*/
Blockly.Grid.prototype.scale_ = 1;
Grid.prototype.scale_ = 1;
/**
* Dispose of this grid and unlink from the DOM.
* @package
* @suppress {checkTypes}
*/
Blockly.Grid.prototype.dispose = function() {
Grid.prototype.dispose = function() {
this.gridPattern_ = null;
};
@@ -94,7 +95,7 @@ Blockly.Grid.prototype.dispose = function() {
* @return {boolean} True if blocks should snap, false otherwise.
* @package
*/
Blockly.Grid.prototype.shouldSnap = function() {
Grid.prototype.shouldSnap = function() {
return this.snapToGrid_;
};
@@ -103,7 +104,7 @@ Blockly.Grid.prototype.shouldSnap = function() {
* @return {number} The spacing of the grid points.
* @package
*/
Blockly.Grid.prototype.getSpacing = function() {
Grid.prototype.getSpacing = function() {
return this.spacing_;
};
@@ -113,7 +114,7 @@ Blockly.Grid.prototype.getSpacing = function() {
* @return {string} The pattern ID.
* @package
*/
Blockly.Grid.prototype.getPatternId = function() {
Grid.prototype.getPatternId = function() {
return this.gridPattern_.id;
};
@@ -122,17 +123,17 @@ Blockly.Grid.prototype.getPatternId = function() {
* @param {number} scale The new workspace scale.
* @package
*/
Blockly.Grid.prototype.update = function(scale) {
Grid.prototype.update = function(scale) {
this.scale_ = scale;
// MSIE freaks if it sees a 0x0 pattern, so set empty patterns to 100x100.
var safeSpacing = (this.spacing_ * scale) || 100;
const safeSpacing = (this.spacing_ * scale) || 100;
this.gridPattern_.setAttribute('width', safeSpacing);
this.gridPattern_.setAttribute('height', safeSpacing);
var half = Math.floor(this.spacing_ / 2) + 0.5;
var start = half - this.length_ / 2;
var end = half + this.length_ / 2;
let half = Math.floor(this.spacing_ / 2) + 0.5;
let start = half - this.length_ / 2;
let end = half + this.length_ / 2;
half *= scale;
start *= scale;
@@ -153,8 +154,7 @@ Blockly.Grid.prototype.update = function(scale) {
* @param {number} y2 The new y end position of the line (in px).
* @private
*/
Blockly.Grid.prototype.setLineAttributes_ = function(line, width,
x1, x2, y1, y2) {
Grid.prototype.setLineAttributes_ = function(line, width, x1, x2, y1, y2) {
if (line) {
line.setAttribute('stroke-width', width);
line.setAttribute('x1', x1);
@@ -171,11 +171,11 @@ Blockly.Grid.prototype.setLineAttributes_ = function(line, width,
* @param {number} y The new y position of the grid (in px).
* @package
*/
Blockly.Grid.prototype.moveTo = function(x, y) {
Grid.prototype.moveTo = function(x, y) {
this.gridPattern_.setAttribute('x', x);
this.gridPattern_.setAttribute('y', y);
if (Blockly.utils.userAgent.IE || Blockly.utils.userAgent.EDGE) {
if (userAgent.IE || userAgent.EDGE) {
// IE/Edge doesn't notice that the x/y offsets have changed.
// Force an update.
this.update(this.scale_);
@@ -190,33 +190,30 @@ Blockly.Grid.prototype.moveTo = function(x, y) {
* @return {!SVGElement} The SVG element for the grid pattern.
* @package
*/
Blockly.Grid.createDom = function(rnd, gridOptions, defs) {
Grid.createDom = function(rnd, gridOptions, defs) {
/*
<pattern id="blocklyGridPattern837493" patternUnits="userSpaceOnUse">
<rect stroke="#888" />
<rect stroke="#888" />
</pattern>
*/
var gridPattern = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATTERN,
{
'id': 'blocklyGridPattern' + rnd,
'patternUnits': 'userSpaceOnUse'
}, defs);
const gridPattern = dom.createSvgElement(
Svg.PATTERN,
{'id': 'blocklyGridPattern' + rnd, 'patternUnits': 'userSpaceOnUse'},
defs);
if (gridOptions['length'] > 0 && gridOptions['spacing'] > 0) {
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE,
{'stroke': gridOptions['colour']}, gridPattern);
dom.createSvgElement(
Svg.LINE, {'stroke': gridOptions['colour']}, gridPattern);
if (gridOptions['length'] > 1) {
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE,
{'stroke': gridOptions['colour']}, gridPattern);
dom.createSvgElement(
Svg.LINE, {'stroke': gridOptions['colour']}, gridPattern);
}
// x1, y1, x1, x2 properties will be set later in update.
} else {
// Edge 16 doesn't handle empty patterns
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE, {}, gridPattern);
dom.createSvgElement(Svg.LINE, {}, gridPattern);
}
return gridPattern;
};
exports = Grid;

View File

@@ -10,29 +10,31 @@
*/
'use strict';
goog.provide('Blockly.Icon');
goog.module('Blockly.Icon');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Size');
goog.require('Blockly.utils.Svg');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.Bubble');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const Bubble = goog.requireType('Blockly.Bubble');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Size = goog.require('Blockly.utils.Size');
const Svg = goog.require('Blockly.utils.Svg');
const browserEvents = goog.require('Blockly.browserEvents');
const dom = goog.require('Blockly.utils.dom');
const {getRelativeXY, isRightButton} = goog.require('Blockly.utils');
/**
* Class for an icon.
* @param {Blockly.BlockSvg} block The block associated with this icon.
* @param {BlockSvg} block The block associated with this icon.
* @constructor
* @abstract
*/
Blockly.Icon = function(block) {
const Icon = function(block) {
/**
* The block this icon is attached to.
* @type {Blockly.BlockSvg}
* @type {BlockSvg}
* @protected
*/
this.block_ = block;
@@ -47,31 +49,32 @@ Blockly.Icon = function(block) {
/**
* Does this icon get hidden when the block is collapsed.
*/
Blockly.Icon.prototype.collapseHidden = true;
Icon.prototype.collapseHidden = true;
/**
* Height and width of icons.
* @const
*/
Blockly.Icon.prototype.SIZE = 17;
Icon.prototype.SIZE = 17;
/**
* Bubble UI (if visible).
* @type {?Blockly.Bubble}
* @type {?Bubble}
* @protected
*/
Blockly.Icon.prototype.bubble_ = null;
Icon.prototype.bubble_ = null;
/**
* Absolute coordinate of icon's center.
* @type {?Blockly.utils.Coordinate}
* @type {?Coordinate}
* @protected
*/
Blockly.Icon.prototype.iconXY_ = null;
Icon.prototype.iconXY_ = null;
/**
* Create the icon on the block.
*/
Blockly.Icon.prototype.createIcon = function() {
Icon.prototype.createIcon = function() {
if (this.iconGroup_) {
// Icon already exists.
return;
@@ -81,17 +84,16 @@ Blockly.Icon.prototype.createIcon = function() {
...
</g>
*/
this.iconGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G,
{'class': 'blocklyIconGroup'}, null);
this.iconGroup_ =
dom.createSvgElement(Svg.G, {'class': 'blocklyIconGroup'}, null);
if (this.block_.isInFlyout) {
Blockly.utils.dom.addClass(
dom.addClass(
/** @type {!Element} */ (this.iconGroup_), 'blocklyIconGroupReadonly');
}
this.drawIcon_(this.iconGroup_);
this.block_.getSvgRoot().appendChild(this.iconGroup_);
Blockly.browserEvents.conditionalBind(
browserEvents.conditionalBind(
this.iconGroup_, 'mouseup', this, this.iconClick_);
this.updateEditable();
};
@@ -99,9 +101,9 @@ Blockly.Icon.prototype.createIcon = function() {
/**
* Dispose of this icon.
*/
Blockly.Icon.prototype.dispose = function() {
Icon.prototype.dispose = function() {
// Dispose of and unlink the icon.
Blockly.utils.dom.removeNode(this.iconGroup_);
dom.removeNode(this.iconGroup_);
this.iconGroup_ = null;
// Dispose of and unlink the bubble.
this.setVisible(false);
@@ -111,7 +113,7 @@ Blockly.Icon.prototype.dispose = function() {
/**
* Add or remove the UI indicating if this icon may be clicked or not.
*/
Blockly.Icon.prototype.updateEditable = function() {
Icon.prototype.updateEditable = function() {
// No-op on the base class.
};
@@ -119,7 +121,7 @@ Blockly.Icon.prototype.updateEditable = function() {
* Is the associated bubble visible?
* @return {boolean} True if the bubble is visible.
*/
Blockly.Icon.prototype.isVisible = function() {
Icon.prototype.isVisible = function() {
return !!this.bubble_;
};
@@ -128,12 +130,12 @@ Blockly.Icon.prototype.isVisible = function() {
* @param {!Event} e Mouse click event.
* @protected
*/
Blockly.Icon.prototype.iconClick_ = function(e) {
Icon.prototype.iconClick_ = function(e) {
if (this.block_.workspace.isDragging()) {
// Drag operation is concluding. Don't open the editor.
return;
}
if (!this.block_.isInFlyout && !Blockly.utils.isRightButton(e)) {
if (!this.block_.isInFlyout && !isRightButton(e)) {
this.setVisible(!this.isVisible());
}
};
@@ -141,7 +143,7 @@ Blockly.Icon.prototype.iconClick_ = function(e) {
/**
* Change the colour of the associated bubble to match its block.
*/
Blockly.Icon.prototype.applyColour = function() {
Icon.prototype.applyColour = function() {
if (this.isVisible()) {
this.bubble_.setColour(this.block_.style.colourPrimary);
}
@@ -149,9 +151,9 @@ Blockly.Icon.prototype.applyColour = function() {
/**
* Notification that the icon has moved. Update the arrow accordingly.
* @param {!Blockly.utils.Coordinate} xy Absolute location in workspace coordinates.
* @param {!Coordinate} xy Absolute location in workspace coordinates.
*/
Blockly.Icon.prototype.setIconLocation = function(xy) {
Icon.prototype.setIconLocation = function(xy) {
this.iconXY_ = xy;
if (this.isVisible()) {
this.bubble_.setAnchorLocation(xy);
@@ -162,25 +164,25 @@ Blockly.Icon.prototype.setIconLocation = function(xy) {
* Notification that the icon has moved, but we don't really know where.
* Recompute the icon's location from scratch.
*/
Blockly.Icon.prototype.computeIconLocation = function() {
Icon.prototype.computeIconLocation = function() {
// Find coordinates for the centre of the icon and update the arrow.
var blockXY = this.block_.getRelativeToSurfaceXY();
var iconXY = Blockly.utils.getRelativeXY(
const blockXY = this.block_.getRelativeToSurfaceXY();
const iconXY = getRelativeXY(
/** @type {!SVGElement} */ (this.iconGroup_));
var newXY = new Blockly.utils.Coordinate(
const newXY = new Coordinate(
blockXY.x + iconXY.x + this.SIZE / 2,
blockXY.y + iconXY.y + this.SIZE / 2);
if (!Blockly.utils.Coordinate.equals(this.getIconLocation(), newXY)) {
if (!Coordinate.equals(this.getIconLocation(), newXY)) {
this.setIconLocation(newXY);
}
};
/**
* Returns the center of the block's icon relative to the surface.
* @return {?Blockly.utils.Coordinate} Object with x and y properties in
* @return {?Coordinate} Object with x and y properties in
* workspace coordinates.
*/
Blockly.Icon.prototype.getIconLocation = function() {
Icon.prototype.getIconLocation = function() {
return this.iconXY_;
};
@@ -188,12 +190,11 @@ Blockly.Icon.prototype.getIconLocation = function() {
* Get the size of the icon as used for rendering.
* This differs from the actual size of the icon, because it bulges slightly
* out of its row rather than increasing the height of its row.
* @return {!Blockly.utils.Size} Height and width.
* @return {!Size} Height and width.
*/
// TODO (#2562): Remove getCorrectedSize.
Blockly.Icon.prototype.getCorrectedSize = function() {
return new Blockly.utils.Size(
Blockly.Icon.prototype.SIZE, Blockly.Icon.prototype.SIZE - 2);
Icon.prototype.getCorrectedSize = function() {
return new Size(Icon.prototype.SIZE, Icon.prototype.SIZE - 2);
};
/**
@@ -201,10 +202,12 @@ Blockly.Icon.prototype.getCorrectedSize = function() {
* @param {!Element} group The icon group.
* @protected
*/
Blockly.Icon.prototype.drawIcon_;
Icon.prototype.drawIcon_;
/**
* Show or hide the icon.
* @param {boolean} visible True if the icon should be visible.
*/
Blockly.Icon.prototype.setVisible;
Icon.prototype.setVisible;
exports = Icon;

View File

@@ -10,31 +10,36 @@
*/
'use strict';
goog.provide('Blockly.Input');
goog.module('Blockly.Input');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Connection');
goog.require('Blockly.fieldRegistry');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const Connection = goog.require('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
const constants = goog.require('Blockly.constants');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const inputTypes = goog.require('Blockly.inputTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.FieldLabel');
goog.require('Blockly.inputTypes');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.Field');
goog.requireType('Blockly.RenderedConnection');
/**
* Class for an input with an optional field.
* @param {number} type The type of the input.
* @param {string} name Language-neutral identifier which may used to find this
* input again.
* @param {!Blockly.Block} block The block containing this input.
* @param {Blockly.Connection} connection Optional connection for this input.
* @param {!Block} block The block containing this input.
* @param {Connection} connection Optional connection for this input.
* @constructor
*/
Blockly.Input = function(type, name, block, connection) {
if (type != Blockly.inputTypes.DUMMY && !name) {
const Input = function(type, name, block, connection) {
if (type != inputTypes.DUMMY && !name) {
throw Error('Value inputs and statement inputs must have non-empty name.');
}
/** @type {number} */
@@ -42,13 +47,13 @@ Blockly.Input = function(type, name, block, connection) {
/** @type {string} */
this.name = name;
/**
* @type {!Blockly.Block}
* @type {!Block}
* @private
*/
this.sourceBlock_ = block;
/** @type {Blockly.Connection} */
/** @type {Connection} */
this.connection = connection;
/** @type {!Array<!Blockly.Field>} */
/** @type {!Array<!Field>} */
this.fieldRow = [];
};
@@ -56,32 +61,32 @@ Blockly.Input = function(type, name, block, connection) {
* Alignment of input's fields (left, right or centre).
* @type {number}
*/
Blockly.Input.prototype.align = Blockly.constants.ALIGN.LEFT;
Input.prototype.align = constants.ALIGN.LEFT;
/**
* Is the input visible?
* @type {boolean}
* @private
*/
Blockly.Input.prototype.visible_ = true;
Input.prototype.visible_ = true;
/**
* Get the source block for this input.
* @return {?Blockly.Block} The source block, or null if there is none.
* @return {?Block} The source block, or null if there is none.
*/
Blockly.Input.prototype.getSourceBlock = function() {
Input.prototype.getSourceBlock = function() {
return this.sourceBlock_;
};
/**
* Add a field (or label from string), and all prefix and suffix fields, to the
* end of the input's field row.
* @param {string|!Blockly.Field} field Something to add as a field.
* @param {string|!Field} field Something to add as a field.
* @param {string=} opt_name Language-neutral identifier which may used to find
* this field again. Should be unique to the host block.
* @return {!Blockly.Input} The input being append to (to allow chaining).
* @return {!Input} The input being append to (to allow chaining).
*/
Blockly.Input.prototype.appendField = function(field, opt_name) {
Input.prototype.appendField = function(field, opt_name) {
this.insertFieldAt(this.fieldRow.length, field, opt_name);
return this;
};
@@ -90,12 +95,12 @@ Blockly.Input.prototype.appendField = function(field, opt_name) {
* Inserts a field (or label from string), and all prefix and suffix fields, at
* the location of the input's field row.
* @param {number} index The index at which to insert field.
* @param {string|!Blockly.Field} field Something to add as a field.
* @param {string|!Field} field Something to add as a field.
* @param {string=} opt_name Language-neutral identifier which may used to find
* this field again. Should be unique to the host block.
* @return {number} The index following the last inserted field.
*/
Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) {
Input.prototype.insertFieldAt = function(index, field, opt_name) {
if (index < 0 || index > this.fieldRow.length) {
throw Error('index ' + index + ' out of bounds.');
}
@@ -107,7 +112,7 @@ Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) {
// Generate a FieldLabel when given a plain text field.
if (typeof field == 'string') {
field = /** @type {!Blockly.Field} **/ (Blockly.fieldRegistry.fromJson({
field = /** @type {!Field} **/ (fieldRegistry.fromJson({
'type': 'field_label',
'text': field,
}));
@@ -134,7 +139,7 @@ Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) {
}
if (this.sourceBlock_.rendered) {
this.sourceBlock_ = /** @type {!Blockly.BlockSvg} */ (this.sourceBlock_);
this.sourceBlock_ = /** @type {!BlockSvg} */ (this.sourceBlock_);
this.sourceBlock_.render();
// Adding a field will cause the block to change shape.
this.sourceBlock_.bumpNeighbours();
@@ -150,13 +155,13 @@ Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) {
* and opt_quiet is true.
* @throws {Error} if the field is not present and opt_quiet is false.
*/
Blockly.Input.prototype.removeField = function(name, opt_quiet) {
for (var i = 0, field; (field = this.fieldRow[i]); i++) {
Input.prototype.removeField = function(name, opt_quiet) {
for (let i = 0, field; (field = this.fieldRow[i]); i++) {
if (field.name === name) {
field.dispose();
this.fieldRow.splice(i, 1);
if (this.sourceBlock_.rendered) {
this.sourceBlock_ = /** @type {!Blockly.BlockSvg} */ (this.sourceBlock_);
this.sourceBlock_ = /** @type {!BlockSvg} */ (this.sourceBlock_);
this.sourceBlock_.render();
// Removing a field will cause the block to change shape.
this.sourceBlock_.bumpNeighbours();
@@ -174,7 +179,7 @@ Blockly.Input.prototype.removeField = function(name, opt_quiet) {
* Gets whether this input is visible or not.
* @return {boolean} True if visible.
*/
Blockly.Input.prototype.isVisible = function() {
Input.prototype.isVisible = function() {
return this.visible_;
};
@@ -182,32 +187,32 @@ Blockly.Input.prototype.isVisible = function() {
* Sets whether this input is visible or not.
* Should only be used to collapse/uncollapse a block.
* @param {boolean} visible True if visible.
* @return {!Array<!Blockly.BlockSvg>} List of blocks to render.
* @return {!Array<!BlockSvg>} List of blocks to render.
* @package
*/
Blockly.Input.prototype.setVisible = function(visible) {
Input.prototype.setVisible = function(visible) {
// Note: Currently there are only unit tests for block.setCollapsed()
// because this function is package. If this function goes back to being a
// public API tests (lots of tests) should be added.
var renderList = [];
let renderList = [];
if (this.visible_ == visible) {
return renderList;
}
this.visible_ = visible;
for (var y = 0, field; (field = this.fieldRow[y]); y++) {
for (let y = 0, field; (field = this.fieldRow[y]); y++) {
field.setVisible(visible);
}
if (this.connection) {
this.connection =
/** @type {!Blockly.RenderedConnection} */ (this.connection);
/** @type {!RenderedConnection} */ (this.connection);
// Has a connection.
if (visible) {
renderList = this.connection.startTrackingAll();
} else {
this.connection.stopTrackingAll();
}
var child = this.connection.targetBlock();
const child = this.connection.targetBlock();
if (child) {
child.getSvgRoot().style.display = visible ? 'block' : 'none';
}
@@ -219,8 +224,8 @@ Blockly.Input.prototype.setVisible = function(visible) {
* Mark all fields on this input as dirty.
* @package
*/
Blockly.Input.prototype.markDirty = function() {
for (var y = 0, field; (field = this.fieldRow[y]); y++) {
Input.prototype.markDirty = function() {
for (let y = 0, field; (field = this.fieldRow[y]); y++) {
field.markDirty();
}
};
@@ -229,9 +234,9 @@ Blockly.Input.prototype.markDirty = function() {
* Change a connection's compatibility.
* @param {string|Array<string>|null} check Compatible value type or
* list of value types. Null if all types are compatible.
* @return {!Blockly.Input} The input being modified (to allow chaining).
* @return {!Input} The input being modified (to allow chaining).
*/
Blockly.Input.prototype.setCheck = function(check) {
Input.prototype.setCheck = function(check) {
if (!this.connection) {
throw Error('This input does not have a connection.');
}
@@ -241,14 +246,14 @@ Blockly.Input.prototype.setCheck = function(check) {
/**
* Change the alignment of the connection's field(s).
* @param {number} align One of the values of Blockly.constants.ALIGN.
* @param {number} align One of the values of constants.ALIGN.
* In RTL mode directions are reversed, and ALIGN.RIGHT aligns to the left.
* @return {!Blockly.Input} The input being modified (to allow chaining).
* @return {!Input} The input being modified (to allow chaining).
*/
Blockly.Input.prototype.setAlign = function(align) {
Input.prototype.setAlign = function(align) {
this.align = align;
if (this.sourceBlock_.rendered) {
this.sourceBlock_ = /** @type {!Blockly.BlockSvg} */ (this.sourceBlock_);
this.sourceBlock_ = /** @type {!BlockSvg} */ (this.sourceBlock_);
this.sourceBlock_.render();
}
return this;
@@ -257,9 +262,9 @@ Blockly.Input.prototype.setAlign = function(align) {
/**
* Changes the connection's shadow block.
* @param {?Element} shadow DOM representation of a block or null.
* @return {!Blockly.Input} The input being modified (to allow chaining).
* @return {!Input} The input being modified (to allow chaining).
*/
Blockly.Input.prototype.setShadowDom = function(shadow) {
Input.prototype.setShadowDom = function(shadow) {
if (!this.connection) {
throw Error('This input does not have a connection.');
}
@@ -271,7 +276,7 @@ Blockly.Input.prototype.setShadowDom = function(shadow) {
* Returns the XML representation of the connection's shadow block.
* @return {?Element} Shadow DOM representation of a block or null.
*/
Blockly.Input.prototype.getShadowDom = function() {
Input.prototype.getShadowDom = function() {
if (!this.connection) {
throw Error('This input does not have a connection.');
}
@@ -281,11 +286,11 @@ Blockly.Input.prototype.getShadowDom = function() {
/**
* Initialize the fields on this input.
*/
Blockly.Input.prototype.init = function() {
Input.prototype.init = function() {
if (!this.sourceBlock_.workspace.rendered) {
return; // Headless blocks don't need fields initialized.
}
for (var i = 0; i < this.fieldRow.length; i++) {
for (let i = 0; i < this.fieldRow.length; i++) {
this.fieldRow[i].init();
}
};
@@ -294,8 +299,8 @@ Blockly.Input.prototype.init = function() {
* Sever all links to this input.
* @suppress {checkTypes}
*/
Blockly.Input.prototype.dispose = function() {
for (var i = 0, field; (field = this.fieldRow[i]); i++) {
Input.prototype.dispose = function() {
for (let i = 0, field; (field = this.fieldRow[i]); i++) {
field.dispose();
}
if (this.connection) {
@@ -303,3 +308,5 @@ Blockly.Input.prototype.dispose = function() {
}
this.sourceBlock_ = null;
};
exports = Input;

View File

@@ -14,7 +14,9 @@
goog.module('Blockly.ICollapsibleToolboxItem');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const ISelectableToolboxItem = goog.require('Blockly.ISelectableToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const IToolboxItem = goog.requireType('Blockly.IToolboxItem');

View File

@@ -15,7 +15,7 @@ goog.module('Blockly.IKeyboardAccessible');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const {KeyboardShortcut} = goog.requireType('Blockly.ShortcutRegistry');
const ShortcutRegistry = goog.requireType('Blockly.ShortcutRegistry');
/**
@@ -26,7 +26,8 @@ const IKeyboardAccessible = function() {};
/**
* Handles the given keyboard shortcut.
* @param {!KeyboardShortcut} shortcut The shortcut to be handled.
* @param {!ShortcutRegistry.KeyboardShortcut} shortcut The shortcut to be
* handled.
* @return {boolean} True if the shortcut has been handled, false otherwise.
*/
IKeyboardAccessible.prototype.onShortcut;

View File

@@ -11,92 +11,96 @@
'use strict';
goog.provide('Blockly.IMetricsManager');
goog.module('Blockly.IMetricsManager');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.MetricsManager');
goog.requireType('Blockly.utils.Metrics');
goog.requireType('Blockly.utils.Size');
/* eslint-disable-next-line no-unused-vars */
const Metrics = goog.requireType('Blockly.utils.Metrics');
/* eslint-disable-next-line no-unused-vars */
const Size = goog.requireType('Blockly.utils.Size');
/* eslint-disable-next-line no-unused-vars */
const {AbsoluteMetrics, ContainerRegion, ToolboxMetrics} = goog.requireType('Blockly.MetricsManager');
/**
* Interface for a metrics manager.
* @interface
*/
Blockly.IMetricsManager = function() {};
const IMetricsManager = function() {};
/**
* Returns whether the scroll area has fixed edges.
* @return {boolean} Whether the scroll area has fixed edges.
* @package
*/
Blockly.IMetricsManager.prototype.hasFixedEdges;
IMetricsManager.prototype.hasFixedEdges;
/**
* Returns the metrics for the scroll area of the workspace.
* @param {boolean=} opt_getWorkspaceCoordinates True to get the scroll metrics
* in workspace coordinates, false to get them in pixel coordinates.
* @param {!Blockly.MetricsManager.ContainerRegion=} opt_viewMetrics The view
* @param {!ContainerRegion=} opt_viewMetrics The view
* metrics if they have been previously computed. Passing in null may cause
* the view metrics to be computed again, if it is needed.
* @param {!Blockly.MetricsManager.ContainerRegion=} opt_contentMetrics The
* @param {!ContainerRegion=} opt_contentMetrics The
* content metrics if they have been previously computed. Passing in null
* may cause the content metrics to be computed again, if it is needed.
* @return {!Blockly.MetricsManager.ContainerRegion} The metrics for the scroll
* @return {!ContainerRegion} The metrics for the scroll
* container
*/
Blockly.IMetricsManager.prototype.getScrollMetrics;
IMetricsManager.prototype.getScrollMetrics;
/**
* Gets the width and the height of the flyout on the workspace in pixel
* coordinates. Returns 0 for the width and height if the workspace has a
* category toolbox instead of a simple toolbox.
* @param {boolean=} opt_own Whether to only return the workspace's own flyout.
* @return {!Blockly.MetricsManager.ToolboxMetrics} The width and height of the
* @return {!ToolboxMetrics} The width and height of the
* flyout.
* @public
*/
Blockly.IMetricsManager.prototype.getFlyoutMetrics;
IMetricsManager.prototype.getFlyoutMetrics;
/**
* Gets the width, height and position of the toolbox on the workspace in pixel
* coordinates. Returns 0 for the width and height if the workspace has a simple
* toolbox instead of a category toolbox. To get the width and height of a
* simple toolbox @see {@link getFlyoutMetrics}.
* @return {!Blockly.MetricsManager.ToolboxMetrics} The object with the width,
* @return {!ToolboxMetrics} The object with the width,
* height and position of the toolbox.
* @public
*/
Blockly.IMetricsManager.prototype.getToolboxMetrics;
IMetricsManager.prototype.getToolboxMetrics;
/**
* Gets the width and height of the workspace's parent SVG element in pixel
* coordinates. This area includes the toolbox and the visible workspace area.
* @return {!Blockly.utils.Size} The width and height of the workspace's parent
* @return {!Size} The width and height of the workspace's parent
* SVG element.
* @public
*/
Blockly.IMetricsManager.prototype.getSvgMetrics;
IMetricsManager.prototype.getSvgMetrics;
/**
* Gets the absolute left and absolute top in pixel coordinates.
* This is where the visible workspace starts in relation to the SVG container.
* @return {!Blockly.MetricsManager.AbsoluteMetrics} The absolute metrics for
* @return {!AbsoluteMetrics} The absolute metrics for
* the workspace.
* @public
*/
Blockly.IMetricsManager.prototype.getAbsoluteMetrics;
IMetricsManager.prototype.getAbsoluteMetrics;
/**
* Gets the metrics for the visible workspace in either pixel or workspace
* coordinates. The visible workspace does not include the toolbox or flyout.
* @param {boolean=} opt_getWorkspaceCoordinates True to get the view metrics in
* workspace coordinates, false to get them in pixel coordinates.
* @return {!Blockly.MetricsManager.ContainerRegion} The width, height, top and
* @return {!ContainerRegion} The width, height, top and
* left of the viewport in either workspace coordinates or pixel
* coordinates.
* @public
*/
Blockly.IMetricsManager.prototype.getViewMetrics;
IMetricsManager.prototype.getViewMetrics;
/**
* Gets content metrics in either pixel or workspace coordinates.
@@ -104,11 +108,11 @@ Blockly.IMetricsManager.prototype.getViewMetrics;
* workspace (workspace comments and blocks).
* @param {boolean=} opt_getWorkspaceCoordinates True to get the content metrics
* in workspace coordinates, false to get them in pixel coordinates.
* @return {!Blockly.MetricsManager.ContainerRegion} The
* @return {!ContainerRegion} The
* metrics for the content container.
* @public
*/
Blockly.IMetricsManager.prototype.getContentMetrics;
IMetricsManager.prototype.getContentMetrics;
/**
* Returns an object with all the metrics required to size scrollbars for a
@@ -138,8 +142,10 @@ Blockly.IMetricsManager.prototype.getContentMetrics;
* .flyoutHeight: Height of the flyout if it is always open. Otherwise zero.
* .toolboxPosition: Top, bottom, left or right. Use TOOLBOX_AT constants to
* compare.
* @return {!Blockly.utils.Metrics} Contains size and position metrics of a top
* @return {!Metrics} Contains size and position metrics of a top
* level workspace.
* @public
*/
Blockly.IMetricsManager.prototype.getMetrics;
IMetricsManager.prototype.getMetrics;
exports = IMetricsManager;

View File

@@ -14,6 +14,7 @@
goog.module('Blockly.IRegistrableField');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');

View File

@@ -14,7 +14,9 @@
goog.module('Blockly.ISelectableToolboxItem');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IToolboxItem = goog.require('Blockly.IToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const {FlyoutItemInfoArray} = goog.requireType('Blockly.utils.toolbox');

View File

@@ -10,18 +10,25 @@
*/
'use strict';
goog.provide('Blockly.ASTNode');
goog.module('Blockly.ASTNode');
goog.module.declareLegacyNamespace();
goog.require('Blockly.connectionTypes');
goog.require('Blockly.utils.Coordinate');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.Connection');
goog.requireType('Blockly.Field');
goog.requireType('Blockly.IASTNodeLocation');
goog.requireType('Blockly.IASTNodeLocationWithBlock');
goog.requireType('Blockly.Input');
goog.requireType('Blockly.Workspace');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const Connection = goog.requireType('Blockly.Connection');
const Coordinate = goog.require('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
/* eslint-disable-next-line no-unused-vars */
const IASTNodeLocation = goog.requireType('Blockly.IASTNodeLocation');
/* eslint-disable-next-line no-unused-vars */
const IASTNodeLocationWithBlock = goog.requireType('Blockly.IASTNodeLocationWithBlock');
/* eslint-disable-next-line no-unused-vars */
const Input = goog.requireType('Blockly.Input');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const connectionTypes = goog.require('Blockly.connectionTypes');
/**
@@ -29,19 +36,19 @@ goog.requireType('Blockly.Workspace');
* It is recommended that you use one of the createNode methods instead of
* creating a node directly.
* @param {string} type The type of the location.
* Must be in Blockly.ASTNode.types.
* @param {!Blockly.IASTNodeLocation} location The position in the AST.
* @param {!Blockly.ASTNode.Params=} opt_params Optional dictionary of options.
* Must be in ASTNode.types.
* @param {!IASTNodeLocation} location The position in the AST.
* @param {!ASTNode.Params=} opt_params Optional dictionary of options.
* @constructor
*/
Blockly.ASTNode = function(type, location, opt_params) {
const ASTNode = function(type, location, opt_params) {
if (!location) {
throw Error('Cannot create a node without a location.');
}
/**
* The type of the location.
* One of Blockly.ASTNode.types
* One of ASTNode.types
* @type {string}
* @private
*/
@@ -52,18 +59,18 @@ Blockly.ASTNode = function(type, location, opt_params) {
* @type {boolean}
* @private
*/
this.isConnection_ = Blockly.ASTNode.isConnectionType_(type);
this.isConnection_ = ASTNode.isConnectionType_(type);
/**
* The location of the AST node.
* @type {!Blockly.IASTNodeLocation}
* @type {!IASTNodeLocation}
* @private
*/
this.location_ = location;
/**
* The coordinate on the workspace.
* @type {Blockly.utils.Coordinate}
* @type {Coordinate}
* @private
*/
this.wsCoordinate_ = null;
@@ -73,16 +80,16 @@ Blockly.ASTNode = function(type, location, opt_params) {
/**
* @typedef {{
* wsCoordinate: Blockly.utils.Coordinate
* wsCoordinate: Coordinate
* }}
*/
Blockly.ASTNode.Params;
ASTNode.Params;
/**
* Object holding different types for an AST node.
* @enum {string}
*/
Blockly.ASTNode.types = {
ASTNode.types = {
FIELD: 'field',
BLOCK: 'block',
INPUT: 'input',
@@ -97,7 +104,7 @@ Blockly.ASTNode.types = {
* True to navigate to all fields. False to only navigate to clickable fields.
* @type {boolean}
*/
Blockly.ASTNode.NAVIGATE_ALL_FIELDS = false;
ASTNode.NAVIGATE_ALL_FIELDS = false;
/**
* The default y offset to use when moving the cursor from a stack to the
@@ -105,20 +112,20 @@ Blockly.ASTNode.NAVIGATE_ALL_FIELDS = false;
* @type {number}
* @private
*/
Blockly.ASTNode.DEFAULT_OFFSET_Y = -20;
ASTNode.DEFAULT_OFFSET_Y = -20;
/**
* Whether an AST node of the given type points to a connection.
* @param {string} type The type to check. One of Blockly.ASTNode.types.
* @param {string} type The type to check. One of ASTNode.types.
* @return {boolean} True if a node of the given type points to a connection.
* @private
*/
Blockly.ASTNode.isConnectionType_ = function(type) {
ASTNode.isConnectionType_ = function(type) {
switch (type) {
case Blockly.ASTNode.types.PREVIOUS:
case Blockly.ASTNode.types.NEXT:
case Blockly.ASTNode.types.INPUT:
case Blockly.ASTNode.types.OUTPUT:
case ASTNode.types.PREVIOUS:
case ASTNode.types.NEXT:
case ASTNode.types.INPUT:
case ASTNode.types.OUTPUT:
return true;
}
return false;
@@ -126,39 +133,39 @@ Blockly.ASTNode.isConnectionType_ = function(type) {
/**
* Create an AST node pointing to a field.
* @param {Blockly.Field} field The location of the AST node.
* @return {Blockly.ASTNode} An AST node pointing to a field.
* @param {Field} field The location of the AST node.
* @return {ASTNode} An AST node pointing to a field.
*/
Blockly.ASTNode.createFieldNode = function(field) {
ASTNode.createFieldNode = function(field) {
if (!field) {
return null;
}
return new Blockly.ASTNode(Blockly.ASTNode.types.FIELD, field);
return new ASTNode(ASTNode.types.FIELD, field);
};
/**
* Creates an AST node pointing to a connection. If the connection has a parent
* input then create an AST node of type input that will hold the connection.
* @param {Blockly.Connection} connection This is the connection the node will
* @param {Connection} connection This is the connection the node will
* point to.
* @return {Blockly.ASTNode} An AST node pointing to a connection.
* @return {ASTNode} An AST node pointing to a connection.
*/
Blockly.ASTNode.createConnectionNode = function(connection) {
ASTNode.createConnectionNode = function(connection) {
if (!connection) {
return null;
}
var type = connection.type;
if (type == Blockly.connectionTypes.INPUT_VALUE) {
return Blockly.ASTNode.createInputNode(connection.getParentInput());
} else if (type == Blockly.connectionTypes.NEXT_STATEMENT &&
connection.getParentInput()) {
return Blockly.ASTNode.createInputNode(connection.getParentInput());
} else if (type == Blockly.connectionTypes.NEXT_STATEMENT) {
return new Blockly.ASTNode(Blockly.ASTNode.types.NEXT, connection);
} else if (type == Blockly.connectionTypes.OUTPUT_VALUE) {
return new Blockly.ASTNode(Blockly.ASTNode.types.OUTPUT, connection);
} else if (type == Blockly.connectionTypes.PREVIOUS_STATEMENT) {
return new Blockly.ASTNode(Blockly.ASTNode.types.PREVIOUS, connection);
const type = connection.type;
if (type == connectionTypes.INPUT_VALUE) {
return ASTNode.createInputNode(connection.getParentInput());
} else if (
type == connectionTypes.NEXT_STATEMENT && connection.getParentInput()) {
return ASTNode.createInputNode(connection.getParentInput());
} else if (type == connectionTypes.NEXT_STATEMENT) {
return new ASTNode(ASTNode.types.NEXT, connection);
} else if (type == connectionTypes.OUTPUT_VALUE) {
return new ASTNode(ASTNode.types.OUTPUT, connection);
} else if (type == connectionTypes.PREVIOUS_STATEMENT) {
return new ASTNode(ASTNode.types.PREVIOUS, connection);
}
return null;
};
@@ -166,86 +173,83 @@ Blockly.ASTNode.createConnectionNode = function(connection) {
/**
* Creates an AST node pointing to an input. Stores the input connection as the
* location.
* @param {Blockly.Input} input The input used to create an AST node.
* @return {Blockly.ASTNode} An AST node pointing to a input.
* @param {Input} input The input used to create an AST node.
* @return {ASTNode} An AST node pointing to a input.
*/
Blockly.ASTNode.createInputNode = function(input) {
ASTNode.createInputNode = function(input) {
if (!input || !input.connection) {
return null;
}
return new Blockly.ASTNode(Blockly.ASTNode.types.INPUT, input.connection);
return new ASTNode(ASTNode.types.INPUT, input.connection);
};
/**
* Creates an AST node pointing to a block.
* @param {Blockly.Block} block The block used to create an AST node.
* @return {Blockly.ASTNode} An AST node pointing to a block.
* @param {Block} block The block used to create an AST node.
* @return {ASTNode} An AST node pointing to a block.
*/
Blockly.ASTNode.createBlockNode = function(block) {
ASTNode.createBlockNode = function(block) {
if (!block) {
return null;
}
return new Blockly.ASTNode(Blockly.ASTNode.types.BLOCK, block);
return new ASTNode(ASTNode.types.BLOCK, block);
};
/**
* Create an AST node of type stack. A stack, represented by its top block, is
* the set of all blocks connected to a top block, including the top block.
* @param {Blockly.Block} topBlock A top block has no parent and can be found
* @param {Block} topBlock A top block has no parent and can be found
* in the list returned by workspace.getTopBlocks().
* @return {Blockly.ASTNode} An AST node of type stack that points to the top
* @return {ASTNode} An AST node of type stack that points to the top
* block on the stack.
*/
Blockly.ASTNode.createStackNode = function(topBlock) {
ASTNode.createStackNode = function(topBlock) {
if (!topBlock) {
return null;
}
return new Blockly.ASTNode(Blockly.ASTNode.types.STACK, topBlock);
return new ASTNode(ASTNode.types.STACK, topBlock);
};
/**
* Creates an AST node pointing to a workspace.
* @param {!Blockly.Workspace} workspace The workspace that we are on.
* @param {Blockly.utils.Coordinate} wsCoordinate The position on the workspace
* @param {!Workspace} workspace The workspace that we are on.
* @param {Coordinate} wsCoordinate The position on the workspace
* for this node.
* @return {Blockly.ASTNode} An AST node pointing to a workspace and a position
* @return {ASTNode} An AST node pointing to a workspace and a position
* on the workspace.
*/
Blockly.ASTNode.createWorkspaceNode = function(workspace, wsCoordinate) {
ASTNode.createWorkspaceNode = function(workspace, wsCoordinate) {
if (!wsCoordinate || !workspace) {
return null;
}
var params = {
wsCoordinate: wsCoordinate
};
return new Blockly.ASTNode(
Blockly.ASTNode.types.WORKSPACE, workspace, params);
const params = {wsCoordinate: wsCoordinate};
return new ASTNode(ASTNode.types.WORKSPACE, workspace, params);
};
/**
* Creates an AST node for the top position on a block.
* This is either an output connection, previous connection, or block.
* @param {!Blockly.Block} block The block to find the top most AST node on.
* @return {Blockly.ASTNode} The AST node holding the top most position on the
* @param {!Block} block The block to find the top most AST node on.
* @return {ASTNode} The AST node holding the top most position on the
* block.
*/
Blockly.ASTNode.createTopNode = function(block) {
var astNode;
var topConnection = block.previousConnection || block.outputConnection;
ASTNode.createTopNode = function(block) {
let astNode;
const topConnection = block.previousConnection || block.outputConnection;
if (topConnection) {
astNode = Blockly.ASTNode.createConnectionNode(topConnection);
astNode = ASTNode.createConnectionNode(topConnection);
} else {
astNode = Blockly.ASTNode.createBlockNode(block);
astNode = ASTNode.createBlockNode(block);
}
return astNode;
};
/**
* Parse the optional parameters.
* @param {?Blockly.ASTNode.Params} params The user specified parameters.
* @param {?ASTNode.Params} params The user specified parameters.
* @private
*/
Blockly.ASTNode.prototype.processParams_ = function(params) {
ASTNode.prototype.processParams_ = function(params) {
if (!params) {
return;
}
@@ -258,28 +262,28 @@ Blockly.ASTNode.prototype.processParams_ = function(params) {
* Gets the value pointed to by this node.
* It is the callers responsibility to check the node type to figure out what
* type of object they get back from this.
* @return {!Blockly.IASTNodeLocation} The current field, connection, workspace, or
* @return {!IASTNodeLocation} The current field, connection, workspace, or
* block the cursor is on.
*/
Blockly.ASTNode.prototype.getLocation = function() {
ASTNode.prototype.getLocation = function() {
return this.location_;
};
/**
* The type of the current location.
* One of Blockly.ASTNode.types
* One of ASTNode.types
* @return {string} The type of the location.
*/
Blockly.ASTNode.prototype.getType = function() {
ASTNode.prototype.getType = function() {
return this.type_;
};
/**
* The coordinate on the workspace.
* @return {Blockly.utils.Coordinate} The workspace coordinate or null if the
* @return {Coordinate} The workspace coordinate or null if the
* location is not a workspace.
*/
Blockly.ASTNode.prototype.getWsCoordinate = function() {
ASTNode.prototype.getWsCoordinate = function() {
return this.wsCoordinate_;
};
@@ -288,7 +292,7 @@ Blockly.ASTNode.prototype.getWsCoordinate = function() {
* @return {boolean} [description]
* @package
*/
Blockly.ASTNode.prototype.isConnection = function() {
ASTNode.prototype.isConnection = function() {
return this.isConnection_;
};
@@ -296,25 +300,27 @@ Blockly.ASTNode.prototype.isConnection = function() {
* Given an input find the next editable field or an input with a non null
* connection in the same block. The current location must be an input
* connection.
* @return {Blockly.ASTNode} The AST node holding the next field or connection
* @return {ASTNode} The AST node holding the next field or connection
* or null if there is no editable field or input connection after the given
* input.
* @private
*/
Blockly.ASTNode.prototype.findNextForInput_ = function() {
var location = /** @type {!Blockly.Connection} */ (this.location_);
var parentInput = location.getParentInput();
var block = parentInput.getSourceBlock();
var curIdx = block.inputList.indexOf(parentInput);
for (var i = curIdx + 1, input; (input = block.inputList[i]); i++) {
var fieldRow = input.fieldRow;
for (var j = 0, field; (field = fieldRow[j]); j++) {
if (field.isClickable() || Blockly.ASTNode.NAVIGATE_ALL_FIELDS) {
return Blockly.ASTNode.createFieldNode(field);
ASTNode.prototype.findNextForInput_ = function() {
const location = /** @type {!Connection} */ (this.location_);
const parentInput = location.getParentInput();
const block = parentInput.getSourceBlock();
const curIdx = block.inputList.indexOf(parentInput);
for (let i = curIdx + 1; i < block.inputList.length; i++) {
const input = block.inputList[i];
const fieldRow = input.fieldRow;
for (let j = 0; j < fieldRow.length; j++) {
const field = fieldRow[j];
if (field.isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
return ASTNode.createFieldNode(field);
}
}
if (input.connection) {
return Blockly.ASTNode.createInputNode(input);
return ASTNode.createInputNode(input);
}
}
return null;
@@ -323,28 +329,29 @@ Blockly.ASTNode.prototype.findNextForInput_ = function() {
/**
* Given a field find the next editable field or an input with a non null
* connection in the same block. The current location must be a field.
* @return {Blockly.ASTNode} The AST node pointing to the next field or
* @return {ASTNode} The AST node pointing to the next field or
* connection or null if there is no editable field or input connection
* after the given input.
* @private
*/
Blockly.ASTNode.prototype.findNextForField_ = function() {
var location = /** @type {!Blockly.Field} */ (this.location_);
var input = location.getParentInput();
var block = location.getSourceBlock();
var curIdx = block.inputList.indexOf(/** @type {!Blockly.Input} */ (input));
var fieldIdx = input.fieldRow.indexOf(location) + 1;
for (var i = curIdx, newInput; (newInput = block.inputList[i]); i++) {
var fieldRow = newInput.fieldRow;
ASTNode.prototype.findNextForField_ = function() {
const location = /** @type {!Field} */ (this.location_);
const input = location.getParentInput();
const block = location.getSourceBlock();
const curIdx = block.inputList.indexOf(/** @type {!Input} */ (input));
let fieldIdx = input.fieldRow.indexOf(location) + 1;
for (let i = curIdx; i < block.inputList.length; i++) {
const newInput = block.inputList[i];
const fieldRow = newInput.fieldRow;
while (fieldIdx < fieldRow.length) {
if (fieldRow[fieldIdx].isClickable() || Blockly.ASTNode.NAVIGATE_ALL_FIELDS) {
return Blockly.ASTNode.createFieldNode(fieldRow[fieldIdx]);
if (fieldRow[fieldIdx].isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
return ASTNode.createFieldNode(fieldRow[fieldIdx]);
}
fieldIdx++;
}
fieldIdx = 0;
if (newInput.connection) {
return Blockly.ASTNode.createInputNode(newInput);
return ASTNode.createInputNode(newInput);
}
}
return null;
@@ -354,23 +361,25 @@ Blockly.ASTNode.prototype.findNextForField_ = function() {
* Given an input find the previous editable field or an input with a non null
* connection in the same block. The current location must be an input
* connection.
* @return {Blockly.ASTNode} The AST node holding the previous field or
* @return {ASTNode} The AST node holding the previous field or
* connection.
* @private
*/
Blockly.ASTNode.prototype.findPrevForInput_ = function() {
var location = /** @type {!Blockly.Connection} */ (this.location_);
var parentInput = location.getParentInput();
var block = parentInput.getSourceBlock();
var curIdx = block.inputList.indexOf(parentInput);
for (var i = curIdx, input; (input = block.inputList[i]); i--) {
ASTNode.prototype.findPrevForInput_ = function() {
const location = /** @type {!Connection} */ (this.location_);
const parentInput = location.getParentInput();
const block = parentInput.getSourceBlock();
const curIdx = block.inputList.indexOf(parentInput);
for (let i = curIdx; i >= 0; i--) {
const input = block.inputList[i];
if (input.connection && input !== parentInput) {
return Blockly.ASTNode.createInputNode(input);
return ASTNode.createInputNode(input);
}
var fieldRow = input.fieldRow;
for (var j = fieldRow.length - 1, field; (field = fieldRow[j]); j--) {
if (field.isClickable() || Blockly.ASTNode.NAVIGATE_ALL_FIELDS) {
return Blockly.ASTNode.createFieldNode(field);
const fieldRow = input.fieldRow;
for (let j = fieldRow.length - 1; j >= 0; j--) {
const field = fieldRow[j];
if (field.isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
return ASTNode.createFieldNode(field);
}
}
}
@@ -380,24 +389,25 @@ Blockly.ASTNode.prototype.findPrevForInput_ = function() {
/**
* Given a field find the previous editable field or an input with a non null
* connection in the same block. The current location must be a field.
* @return {Blockly.ASTNode} The AST node holding the previous input or field.
* @return {ASTNode} The AST node holding the previous input or field.
* @private
*/
Blockly.ASTNode.prototype.findPrevForField_ = function() {
var location = /** @type {!Blockly.Field} */ (this.location_);
var parentInput = location.getParentInput();
var block = location.getSourceBlock();
var curIdx = block.inputList.indexOf(
/** @type {!Blockly.Input} */ (parentInput));
var fieldIdx = parentInput.fieldRow.indexOf(location) - 1;
for (var i = curIdx, input; (input = block.inputList[i]); i--) {
ASTNode.prototype.findPrevForField_ = function() {
const location = /** @type {!Field} */ (this.location_);
const parentInput = location.getParentInput();
const block = location.getSourceBlock();
const curIdx = block.inputList.indexOf(
/** @type {!Input} */ (parentInput));
let fieldIdx = parentInput.fieldRow.indexOf(location) - 1;
for (let i = curIdx; i >= 0; i--) {
const input = block.inputList[i];
if (input.connection && input !== parentInput) {
return Blockly.ASTNode.createInputNode(input);
return ASTNode.createInputNode(input);
}
var fieldRow = input.fieldRow;
const fieldRow = input.fieldRow;
while (fieldIdx > -1) {
if (fieldRow[fieldIdx].isClickable() || Blockly.ASTNode.NAVIGATE_ALL_FIELDS) {
return Blockly.ASTNode.createFieldNode(fieldRow[fieldIdx]);
if (fieldRow[fieldIdx].isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
return ASTNode.createFieldNode(fieldRow[fieldIdx]);
}
fieldIdx--;
}
@@ -412,29 +422,30 @@ Blockly.ASTNode.prototype.findPrevForField_ = function() {
/**
* Navigate between stacks of blocks on the workspace.
* @param {boolean} forward True to go forward. False to go backwards.
* @return {Blockly.ASTNode} The first block of the next stack or null if there
* @return {ASTNode} The first block of the next stack or null if there
* are no blocks on the workspace.
* @private
*/
Blockly.ASTNode.prototype.navigateBetweenStacks_ = function(forward) {
ASTNode.prototype.navigateBetweenStacks_ = function(forward) {
var curLocation = this.getLocation();
if (curLocation.getSourceBlock) {
curLocation = /** @type {!Blockly.IASTNodeLocationWithBlock} */ (
curLocation).getSourceBlock();
curLocation = /** @type {!IASTNodeLocationWithBlock} */ (curLocation)
.getSourceBlock();
}
if (!curLocation || !curLocation.workspace) {
return null;
}
var curRoot = curLocation.getRootBlock();
var topBlocks = curRoot.workspace.getTopBlocks(true);
for (var i = 0, topBlock; (topBlock = topBlocks[i]); i++) {
const curRoot = curLocation.getRootBlock();
const topBlocks = curRoot.workspace.getTopBlocks(true);
for (let i = 0; i < topBlocks.length; i++) {
const topBlock = topBlocks[i];
if (curRoot.id == topBlock.id) {
var offset = forward ? 1 : -1;
var resultIndex = i + offset;
const offset = forward ? 1 : -1;
const resultIndex = i + offset;
if (resultIndex == -1 || resultIndex == topBlocks.length) {
return null;
}
return Blockly.ASTNode.createStackNode(topBlocks[resultIndex]);
return ASTNode.createStackNode(topBlocks[resultIndex]);
}
}
throw Error('Couldn\'t find ' + (forward ? 'next' : 'previous') + ' stack?!');
@@ -444,69 +455,71 @@ Blockly.ASTNode.prototype.navigateBetweenStacks_ = function(forward) {
* Finds the top most AST node for a given block.
* This is either the previous connection, output connection or block depending
* on what kind of connections the block has.
* @param {!Blockly.Block} block The block that we want to find the top
* @param {!Block} block The block that we want to find the top
* connection on.
* @return {!Blockly.ASTNode} The AST node containing the top connection.
* @return {!ASTNode} The AST node containing the top connection.
* @private
*/
Blockly.ASTNode.prototype.findTopASTNodeForBlock_ = function(block) {
var topConnection = block.previousConnection || block.outputConnection;
ASTNode.prototype.findTopASTNodeForBlock_ = function(block) {
const topConnection = block.previousConnection || block.outputConnection;
if (topConnection) {
return /** @type {!Blockly.ASTNode} */ (Blockly.ASTNode.createConnectionNode(
topConnection));
return /** @type {!ASTNode} */ (
ASTNode.createConnectionNode(topConnection));
} else {
return /** @type {!Blockly.ASTNode} */ (Blockly.ASTNode.createBlockNode(
block));
return /** @type {!ASTNode} */ (ASTNode.createBlockNode(block));
}
};
/**
* Get the AST node pointing to the input that the block is nested under or if
* the block is not nested then get the stack AST node.
* @param {Blockly.Block} block The source block of the current location.
* @return {Blockly.ASTNode} The AST node pointing to the input connection or
* @param {Block} block The source block of the current location.
* @return {ASTNode} The AST node pointing to the input connection or
* the top block of the stack this block is in.
* @private
*/
Blockly.ASTNode.prototype.getOutAstNodeForBlock_ = function(block) {
ASTNode.prototype.getOutAstNodeForBlock_ = function(block) {
if (!block) {
return null;
}
var topBlock;
let topBlock;
// If the block doesn't have a previous connection then it is the top of the
// substack.
topBlock = block.getTopStackBlock();
var topConnection = topBlock.previousConnection || topBlock.outputConnection;
const topConnection =
topBlock.previousConnection || topBlock.outputConnection;
// If the top connection has a parentInput, create an AST node pointing to
// that input.
if (topConnection && topConnection.targetConnection &&
topConnection.targetConnection.getParentInput()) {
return Blockly.ASTNode.createInputNode(
return ASTNode.createInputNode(
topConnection.targetConnection.getParentInput());
} else {
// Go to stack level if you are not underneath an input.
return Blockly.ASTNode.createStackNode(topBlock);
return ASTNode.createStackNode(topBlock);
}
};
/**
* Find the first editable field or input with a connection on a given block.
* @param {!Blockly.Block} block The source block of the current location.
* @return {Blockly.ASTNode} An AST node pointing to the first field or input.
* @param {!Block} block The source block of the current location.
* @return {ASTNode} An AST node pointing to the first field or input.
* Null if there are no editable fields or inputs with connections on the block.
* @private
*/
Blockly.ASTNode.prototype.findFirstFieldOrInput_ = function(block) {
var inputs = block.inputList;
for (var i = 0, input; (input = inputs[i]); i++) {
var fieldRow = input.fieldRow;
for (var j = 0, field; (field = fieldRow[j]); j++) {
if (field.isClickable() || Blockly.ASTNode.NAVIGATE_ALL_FIELDS) {
return Blockly.ASTNode.createFieldNode(field);
ASTNode.prototype.findFirstFieldOrInput_ = function(block) {
const inputs = block.inputList;
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i];
const fieldRow = input.fieldRow;
for (let j = 0; j < fieldRow.length; j++) {
const field = fieldRow[j];
if (field.isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
return ASTNode.createFieldNode(field);
}
}
if (input.connection) {
return Blockly.ASTNode.createInputNode(input);
return ASTNode.createInputNode(input);
}
}
return null;
@@ -514,55 +527,56 @@ Blockly.ASTNode.prototype.findFirstFieldOrInput_ = function(block) {
/**
* Finds the source block of the location of this node.
* @return {Blockly.Block} The source block of the location, or null if the node
* @return {Block} The source block of the location, or null if the node
* is of type workspace.
*/
Blockly.ASTNode.prototype.getSourceBlock = function() {
if (this.getType() === Blockly.ASTNode.types.BLOCK) {
return /** @type {Blockly.Block} */ (this.getLocation());
} else if (this.getType() === Blockly.ASTNode.types.STACK) {
return /** @type {Blockly.Block} */ (this.getLocation());
} else if (this.getType() === Blockly.ASTNode.types.WORKSPACE) {
ASTNode.prototype.getSourceBlock = function() {
if (this.getType() === ASTNode.types.BLOCK) {
return /** @type {Block} */ (this.getLocation());
} else if (this.getType() === ASTNode.types.STACK) {
return /** @type {Block} */ (this.getLocation());
} else if (this.getType() === ASTNode.types.WORKSPACE) {
return null;
} else {
return /** @type {Blockly.IASTNodeLocationWithBlock} */ (
this.getLocation()).getSourceBlock();
return /** @type {IASTNodeLocationWithBlock} */ (this.getLocation())
.getSourceBlock();
}
};
/**
* Find the element to the right of the current element in the AST.
* @return {Blockly.ASTNode} An AST node that wraps the next field, connection,
* @return {ASTNode} An AST node that wraps the next field, connection,
* block, or workspace. Or null if there is no node to the right.
*/
Blockly.ASTNode.prototype.next = function() {
ASTNode.prototype.next = function() {
switch (this.type_) {
case Blockly.ASTNode.types.STACK:
case ASTNode.types.STACK:
return this.navigateBetweenStacks_(true);
case Blockly.ASTNode.types.OUTPUT:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
return Blockly.ASTNode.createBlockNode(connection.getSourceBlock());
case Blockly.ASTNode.types.FIELD:
case ASTNode.types.OUTPUT: {
const connection = /** @type {!Connection} */ (this.location_);
return ASTNode.createBlockNode(connection.getSourceBlock());
}
case ASTNode.types.FIELD:
return this.findNextForField_();
case Blockly.ASTNode.types.INPUT:
case ASTNode.types.INPUT:
return this.findNextForInput_();
case Blockly.ASTNode.types.BLOCK:
var block = /** @type {!Blockly.Block} */ (this.location_);
var nextConnection = block.nextConnection;
return Blockly.ASTNode.createConnectionNode(nextConnection);
case Blockly.ASTNode.types.PREVIOUS:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
return Blockly.ASTNode.createBlockNode(connection.getSourceBlock());
case Blockly.ASTNode.types.NEXT:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
var targetConnection = connection.targetConnection;
return Blockly.ASTNode.createConnectionNode(targetConnection);
case ASTNode.types.BLOCK: {
const block = /** @type {!Block} */ (this.location_);
const nextConnection = block.nextConnection;
return ASTNode.createConnectionNode(nextConnection);
}
case ASTNode.types.PREVIOUS: {
const connection = /** @type {!Connection} */ (this.location_);
return ASTNode.createBlockNode(connection.getSourceBlock());
}
case ASTNode.types.NEXT: {
const connection = /** @type {!Connection} */ (this.location_);
const targetConnection = connection.targetConnection;
return ASTNode.createConnectionNode(targetConnection);
}
}
return null;
@@ -571,31 +585,32 @@ Blockly.ASTNode.prototype.next = function() {
/**
* Find the element one level below and all the way to the left of the current
* location.
* @return {Blockly.ASTNode} An AST node that wraps the next field, connection,
* @return {ASTNode} An AST node that wraps the next field, connection,
* workspace, or block. Or null if there is nothing below this node.
*/
Blockly.ASTNode.prototype.in = function() {
ASTNode.prototype.in = function() {
switch (this.type_) {
case Blockly.ASTNode.types.WORKSPACE:
var workspace = /** @type {!Blockly.Workspace} */ (this.location_);
var topBlocks = workspace.getTopBlocks(true);
case ASTNode.types.WORKSPACE: {
const workspace = /** @type {!Workspace} */ (this.location_);
const topBlocks = workspace.getTopBlocks(true);
if (topBlocks.length > 0) {
return Blockly.ASTNode.createStackNode(topBlocks[0]);
return ASTNode.createStackNode(topBlocks[0]);
}
break;
case Blockly.ASTNode.types.STACK:
var block = /** @type {!Blockly.Block} */ (this.location_);
}
case ASTNode.types.STACK: {
const block = /** @type {!Block} */ (this.location_);
return this.findTopASTNodeForBlock_(block);
case Blockly.ASTNode.types.BLOCK:
var block = /** @type {!Blockly.Block} */ (this.location_);
}
case ASTNode.types.BLOCK: {
const block = /** @type {!Block} */ (this.location_);
return this.findFirstFieldOrInput_(block);
case Blockly.ASTNode.types.INPUT:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
var targetConnection = connection.targetConnection;
return Blockly.ASTNode.createConnectionNode(targetConnection);
}
case ASTNode.types.INPUT: {
const connection = /** @type {!Connection} */ (this.location_);
const targetConnection = connection.targetConnection;
return ASTNode.createConnectionNode(targetConnection);
}
}
return null;
@@ -603,40 +618,41 @@ Blockly.ASTNode.prototype.in = function() {
/**
* Find the element to the left of the current element in the AST.
* @return {Blockly.ASTNode} An AST node that wraps the previous field,
* @return {ASTNode} An AST node that wraps the previous field,
* connection, workspace or block. Or null if no node exists to the left.
* null.
*/
Blockly.ASTNode.prototype.prev = function() {
ASTNode.prototype.prev = function() {
switch (this.type_) {
case Blockly.ASTNode.types.STACK:
case ASTNode.types.STACK:
return this.navigateBetweenStacks_(false);
case Blockly.ASTNode.types.OUTPUT:
case ASTNode.types.OUTPUT:
return null;
case Blockly.ASTNode.types.FIELD:
case ASTNode.types.FIELD:
return this.findPrevForField_();
case Blockly.ASTNode.types.INPUT:
case ASTNode.types.INPUT:
return this.findPrevForInput_();
case Blockly.ASTNode.types.BLOCK:
var block = /** @type {!Blockly.Block} */ (this.location_);
var topConnection = block.previousConnection || block.outputConnection;
return Blockly.ASTNode.createConnectionNode(topConnection);
case Blockly.ASTNode.types.PREVIOUS:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
var targetConnection = connection.targetConnection;
case ASTNode.types.BLOCK: {
const block = /** @type {!Block} */ (this.location_);
const topConnection = block.previousConnection || block.outputConnection;
return ASTNode.createConnectionNode(topConnection);
}
case ASTNode.types.PREVIOUS: {
const connection = /** @type {!Connection} */ (this.location_);
const targetConnection = connection.targetConnection;
if (targetConnection && !targetConnection.getParentInput()) {
return Blockly.ASTNode.createConnectionNode(targetConnection);
return ASTNode.createConnectionNode(targetConnection);
}
break;
case Blockly.ASTNode.types.NEXT:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
return Blockly.ASTNode.createBlockNode(connection.getSourceBlock());
}
case ASTNode.types.NEXT: {
const connection = /** @type {!Connection} */ (this.location_);
return ASTNode.createBlockNode(connection.getSourceBlock());
}
}
return null;
@@ -645,47 +661,50 @@ Blockly.ASTNode.prototype.prev = function() {
/**
* Find the next element that is one position above and all the way to the left
* of the current location.
* @return {Blockly.ASTNode} An AST node that wraps the next field, connection,
* @return {ASTNode} An AST node that wraps the next field, connection,
* workspace or block. Or null if we are at the workspace level.
*/
Blockly.ASTNode.prototype.out = function() {
ASTNode.prototype.out = function() {
switch (this.type_) {
case Blockly.ASTNode.types.STACK:
var block = /** @type {!Blockly.Block} */ (this.location_);
var blockPos = block.getRelativeToSurfaceXY();
case ASTNode.types.STACK: {
const block = /** @type {!Block} */ (this.location_);
const blockPos = block.getRelativeToSurfaceXY();
// TODO: Make sure this is in the bounds of the workspace.
var wsCoordinate = new Blockly.utils.Coordinate(
blockPos.x, blockPos.y + Blockly.ASTNode.DEFAULT_OFFSET_Y);
return Blockly.ASTNode.createWorkspaceNode(block.workspace, wsCoordinate);
case Blockly.ASTNode.types.OUTPUT:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
var target = connection.targetConnection;
const wsCoordinate =
new Coordinate(blockPos.x, blockPos.y + ASTNode.DEFAULT_OFFSET_Y);
return ASTNode.createWorkspaceNode(block.workspace, wsCoordinate);
}
case ASTNode.types.OUTPUT: {
const connection = /** @type {!Connection} */ (this.location_);
const target = connection.targetConnection;
if (target) {
return Blockly.ASTNode.createConnectionNode(target);
return ASTNode.createConnectionNode(target);
}
return Blockly.ASTNode.createStackNode(connection.getSourceBlock());
case Blockly.ASTNode.types.FIELD:
var field = /** @type {!Blockly.Field} */ (this.location_);
return Blockly.ASTNode.createBlockNode(field.getSourceBlock());
case Blockly.ASTNode.types.INPUT:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
return Blockly.ASTNode.createBlockNode(connection.getSourceBlock());
case Blockly.ASTNode.types.BLOCK:
var block = /** @type {!Blockly.Block} */ (this.location_);
return ASTNode.createStackNode(connection.getSourceBlock());
}
case ASTNode.types.FIELD: {
const field = /** @type {!Field} */ (this.location_);
return ASTNode.createBlockNode(field.getSourceBlock());
}
case ASTNode.types.INPUT: {
const connection = /** @type {!Connection} */ (this.location_);
return ASTNode.createBlockNode(connection.getSourceBlock());
}
case ASTNode.types.BLOCK: {
const block = /** @type {!Block} */ (this.location_);
return this.getOutAstNodeForBlock_(block);
case Blockly.ASTNode.types.PREVIOUS:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
}
case ASTNode.types.PREVIOUS: {
const connection = /** @type {!Connection} */ (this.location_);
return this.getOutAstNodeForBlock_(connection.getSourceBlock());
case Blockly.ASTNode.types.NEXT:
var connection = /** @type {!Blockly.Connection} */ (this.location_);
}
case ASTNode.types.NEXT: {
const connection = /** @type {!Connection} */ (this.location_);
return this.getOutAstNodeForBlock_(connection.getSourceBlock());
}
}
return null;
};
exports = ASTNode;

View File

@@ -11,11 +11,13 @@
*/
'use strict';
goog.provide('Blockly.Marker');
goog.module('Blockly.Marker');
goog.module.declareLegacyNamespace();
goog.require('Blockly.ASTNode');
goog.requireType('Blockly.blockRendering.MarkerSvg');
/* eslint-disable-next-line no-unused-vars */
const ASTNode = goog.requireType('Blockly.ASTNode');
/* eslint-disable-next-line no-unused-vars */
const MarkerSvg = goog.requireType('Blockly.blockRendering.MarkerSvg');
/**
@@ -23,7 +25,7 @@ goog.requireType('Blockly.blockRendering.MarkerSvg');
* This is used in keyboard navigation to save a location in the Blockly AST.
* @constructor
*/
Blockly.Marker = function() {
const Marker = function() {
/**
* The colour of the marker.
* @type {?string}
@@ -32,14 +34,15 @@ Blockly.Marker = function() {
/**
* The current location of the marker.
* @type {Blockly.ASTNode}
* @type {ASTNode}
* @private
*/
this.curNode_ = null;
/**
* The object in charge of drawing the visual representation of the current node.
* @type {Blockly.blockRendering.MarkerSvg}
* The object in charge of drawing the visual representation of the current
* node.
* @type {MarkerSvg}
* @private
*/
this.drawer_ = null;
@@ -53,28 +56,28 @@ Blockly.Marker = function() {
/**
* Sets the object in charge of drawing the marker.
* @param {Blockly.blockRendering.MarkerSvg} drawer The object in charge of
* @param {MarkerSvg} drawer The object in charge of
* drawing the marker.
*/
Blockly.Marker.prototype.setDrawer = function(drawer) {
Marker.prototype.setDrawer = function(drawer) {
this.drawer_ = drawer;
};
/**
* Get the current drawer for the marker.
* @return {Blockly.blockRendering.MarkerSvg} The object in charge of drawing
* @return {MarkerSvg} The object in charge of drawing
* the marker.
*/
Blockly.Marker.prototype.getDrawer = function() {
Marker.prototype.getDrawer = function() {
return this.drawer_;
};
/**
* Gets the current location of the marker.
* @return {Blockly.ASTNode} The current field, connection, or block the marker
* @return {ASTNode} The current field, connection, or block the marker
* is on.
*/
Blockly.Marker.prototype.getCurNode = function() {
Marker.prototype.getCurNode = function() {
return this.curNode_;
};
@@ -82,10 +85,10 @@ Blockly.Marker.prototype.getCurNode = function() {
* Set the location of the marker and call the update method.
* Setting isStack to true will only work if the newLocation is the top most
* output or previous connection on a stack.
* @param {Blockly.ASTNode} newNode The new location of the marker.
* @param {ASTNode} newNode The new location of the marker.
*/
Blockly.Marker.prototype.setCurNode = function(newNode) {
var oldNode = this.curNode_;
Marker.prototype.setCurNode = function(newNode) {
const oldNode = this.curNode_;
this.curNode_ = newNode;
if (this.drawer_) {
this.drawer_.draw(oldNode, this.curNode_);
@@ -96,7 +99,7 @@ Blockly.Marker.prototype.setCurNode = function(newNode) {
* Redraw the current marker.
* @package
*/
Blockly.Marker.prototype.draw = function() {
Marker.prototype.draw = function() {
if (this.drawer_) {
this.drawer_.draw(this.curNode_, this.curNode_);
}
@@ -105,7 +108,7 @@ Blockly.Marker.prototype.draw = function() {
/**
* Hide the marker SVG.
*/
Blockly.Marker.prototype.hide = function() {
Marker.prototype.hide = function() {
if (this.drawer_) {
this.drawer_.hide();
}
@@ -114,8 +117,10 @@ Blockly.Marker.prototype.hide = function() {
/**
* Dispose of this marker.
*/
Blockly.Marker.prototype.dispose = function() {
Marker.prototype.dispose = function() {
if (this.getDrawer()) {
this.getDrawer().dispose();
}
};
exports = Marker;

View File

@@ -16,6 +16,7 @@ goog.module.declareLegacyNamespace();
const ASTNode = goog.require('Blockly.ASTNode');
const BasicCursor = goog.require('Blockly.BasicCursor');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
const {inherits} = goog.require('Blockly.utils.object');

View File

@@ -10,24 +10,26 @@
*/
'use strict';
goog.provide('Blockly.MarkerManager');
goog.module('Blockly.MarkerManager');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Cursor');
goog.require('Blockly.Marker');
goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const Cursor = goog.requireType('Blockly.Cursor');
/* eslint-disable-next-line no-unused-vars */
const Marker = goog.requireType('Blockly.Marker');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
/**
* Class to manage the multiple markers and the cursor on a workspace.
* @param {!Blockly.WorkspaceSvg} workspace The workspace for the marker manager.
* @param {!WorkspaceSvg} workspace The workspace for the marker manager.
* @constructor
* @package
*/
Blockly.MarkerManager = function(workspace){
const MarkerManager = function(workspace) {
/**
* The cursor.
* @type {?Blockly.Cursor}
* @type {?Cursor}
* @private
*/
this.cursor_ = null;
@@ -41,14 +43,14 @@ Blockly.MarkerManager = function(workspace){
/**
* The map of markers for the workspace.
* @type {!Object<string, !Blockly.Marker>}
* @type {!Object<string, !Marker>}
* @private
*/
this.markers_ = Object.create(null);
/**
* The workspace this marker manager is associated with.
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @private
*/
this.workspace_ = workspace;
@@ -59,19 +61,19 @@ Blockly.MarkerManager = function(workspace){
* @type {string}
* @const
*/
Blockly.MarkerManager.LOCAL_MARKER = 'local_marker_1';
MarkerManager.LOCAL_MARKER = 'local_marker_1';
/**
* Register the marker by adding it to the map of markers.
* @param {string} id A unique identifier for the marker.
* @param {!Blockly.Marker} marker The marker to register.
* @param {!Marker} marker The marker to register.
*/
Blockly.MarkerManager.prototype.registerMarker = function(id, marker) {
MarkerManager.prototype.registerMarker = function(id, marker) {
if (this.markers_[id]) {
this.unregisterMarker(id);
}
marker.setDrawer(this.workspace_.getRenderer()
.makeMarkerDrawer(this.workspace_, marker));
marker.setDrawer(
this.workspace_.getRenderer().makeMarkerDrawer(this.workspace_, marker));
this.setMarkerSvg(marker.getDrawer().createDom());
this.markers_[id] = marker;
};
@@ -80,47 +82,48 @@ Blockly.MarkerManager.prototype.registerMarker = function(id, marker) {
* Unregister the marker by removing it from the map of markers.
* @param {string} id The ID of the marker to unregister.
*/
Blockly.MarkerManager.prototype.unregisterMarker = function(id) {
var marker = this.markers_[id];
MarkerManager.prototype.unregisterMarker = function(id) {
const marker = this.markers_[id];
if (marker) {
marker.dispose();
delete this.markers_[id];
} else {
throw Error('Marker with ID ' + id + ' does not exist. ' +
throw Error(
'Marker with ID ' + id + ' does not exist. ' +
'Can only unregister markers that exist.');
}
};
/**
* Get the cursor for the workspace.
* @return {?Blockly.Cursor} The cursor for this workspace.
* @return {?Cursor} The cursor for this workspace.
*/
Blockly.MarkerManager.prototype.getCursor = function() {
MarkerManager.prototype.getCursor = function() {
return this.cursor_;
};
/**
* Get a single marker that corresponds to the given ID.
* @param {string} id A unique identifier for the marker.
* @return {?Blockly.Marker} The marker that corresponds to the given ID,
* @return {?Marker} The marker that corresponds to the given ID,
* or null if none exists.
*/
Blockly.MarkerManager.prototype.getMarker = function(id) {
MarkerManager.prototype.getMarker = function(id) {
return this.markers_[id] || null;
};
/**
* Sets the cursor and initializes the drawer for use with keyboard navigation.
* @param {Blockly.Cursor} cursor The cursor used to move around this workspace.
* @param {Cursor} cursor The cursor used to move around this workspace.
*/
Blockly.MarkerManager.prototype.setCursor = function(cursor) {
MarkerManager.prototype.setCursor = function(cursor) {
if (this.cursor_ && this.cursor_.getDrawer()) {
this.cursor_.getDrawer().dispose();
}
this.cursor_ = cursor;
if (this.cursor_) {
var drawer = this.workspace_.getRenderer()
.makeMarkerDrawer(this.workspace_, this.cursor_);
const drawer = this.workspace_.getRenderer().makeMarkerDrawer(
this.workspace_, this.cursor_);
this.cursor_.setDrawer(drawer);
this.setCursorSvg(this.cursor_.getDrawer().createDom());
}
@@ -132,7 +135,7 @@ Blockly.MarkerManager.prototype.setCursor = function(cursor) {
* workspace SVG group.
* @package
*/
Blockly.MarkerManager.prototype.setCursorSvg = function(cursorSvg) {
MarkerManager.prototype.setCursorSvg = function(cursorSvg) {
if (!cursorSvg) {
this.cursorSvg_ = null;
return;
@@ -148,7 +151,7 @@ Blockly.MarkerManager.prototype.setCursorSvg = function(cursorSvg) {
* workspace SVG group.
* @package
*/
Blockly.MarkerManager.prototype.setMarkerSvg = function(markerSvg) {
MarkerManager.prototype.setMarkerSvg = function(markerSvg) {
if (!markerSvg) {
this.markerSvg_ = null;
return;
@@ -167,7 +170,7 @@ Blockly.MarkerManager.prototype.setMarkerSvg = function(markerSvg) {
* Redraw the attached cursor SVG if needed.
* @package
*/
Blockly.MarkerManager.prototype.updateMarkers = function() {
MarkerManager.prototype.updateMarkers = function() {
if (this.workspace_.keyboardAccessibilityMode && this.cursorSvg_) {
this.workspace_.getCursor().draw();
}
@@ -179,9 +182,9 @@ Blockly.MarkerManager.prototype.updateMarkers = function() {
* @suppress {checkTypes}
* @package
*/
Blockly.MarkerManager.prototype.dispose = function() {
var markerIds = Object.keys(this.markers_);
for (var i = 0, markerId; (markerId = markerIds[i]); i++) {
MarkerManager.prototype.dispose = function() {
const markerIds = Object.keys(this.markers_);
for (let i = 0, markerId; (markerId = markerIds[i]); i++) {
this.unregisterMarker(markerId);
}
this.markers_ = null;
@@ -190,3 +193,6 @@ Blockly.MarkerManager.prototype.dispose = function() {
this.cursor_ = null;
}
};
/** @package */
exports = MarkerManager;

View File

@@ -10,29 +10,31 @@
*/
'use strict';
goog.provide('Blockly.Menu');
goog.module('Blockly.Menu');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.KeyCodes');
goog.require('Blockly.utils.style');
goog.requireType('Blockly.MenuItem');
goog.requireType('Blockly.utils.Size');
const Coordinate = goog.require('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const MenuItem = goog.requireType('Blockly.MenuItem');
const KeyCodes = goog.require('Blockly.utils.KeyCodes');
/* eslint-disable-next-line no-unused-vars */
const Size = goog.requireType('Blockly.utils.Size');
const aria = goog.require('Blockly.utils.aria');
const browserEvents = goog.require('Blockly.browserEvents');
const dom = goog.require('Blockly.utils.dom');
const style = goog.require('Blockly.utils.style');
/**
* A basic menu class.
* @constructor
*/
Blockly.Menu = function() {
const Menu = function() {
/**
* Array of menu items.
* (Nulls are never in the array, but typing the array as nullable prevents
* the compiler from objecting to .indexOf(null))
* @type {!Array<Blockly.MenuItem>}
* @type {!Array<MenuItem>}
* @private
*/
this.menuItems_ = [];
@@ -41,7 +43,7 @@ Blockly.Menu = function() {
* Coordinates of the mousedown event that caused this menu to open. Used to
* prevent the consequent mouseup event due to a simple click from activating
* a menu item immediately.
* @type {?Blockly.utils.Coordinate}
* @type {?Coordinate}
* @package
*/
this.openingCoords = null;
@@ -49,42 +51,42 @@ Blockly.Menu = function() {
/**
* This is the element that we will listen to the real focus events on.
* A value of null means no menu item is highlighted.
* @type {?Blockly.MenuItem}
* @type {?MenuItem}
* @private
*/
this.highlightedItem_ = null;
/**
* Mouse over event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.mouseOverHandler_ = null;
/**
* Click event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.clickHandler_ = null;
/**
* Mouse enter event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.mouseEnterHandler_ = null;
/**
* Mouse leave event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.mouseLeaveHandler_ = null;
/**
* Key down event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.onKeyDownHandler_ = null;
@@ -98,7 +100,7 @@ Blockly.Menu = function() {
/**
* ARIA name for this menu.
* @type {?Blockly.utils.aria.Role}
* @type {?aria.Role}
* @private
*/
this.roleName_ = null;
@@ -107,9 +109,9 @@ Blockly.Menu = function() {
/**
* Add a new menu item to the bottom of this menu.
* @param {!Blockly.MenuItem} menuItem Menu item to append.
* @param {!MenuItem} menuItem Menu item to append.
*/
Blockly.Menu.prototype.addChild = function(menuItem) {
Menu.prototype.addChild = function(menuItem) {
this.menuItems_.push(menuItem);
};
@@ -117,32 +119,33 @@ Blockly.Menu.prototype.addChild = function(menuItem) {
* Creates the menu DOM.
* @param {!Element} container Element upon which to append this menu.
*/
Blockly.Menu.prototype.render = function(container) {
var element = /** @type {!HTMLDivElement} */ (document.createElement('div'));
Menu.prototype.render = function(container) {
const element =
/** @type {!HTMLDivElement} */ (document.createElement('div'));
// goog-menu is deprecated, use blocklyMenu. May 2020.
element.className = 'blocklyMenu goog-menu blocklyNonSelectable';
element.tabIndex = 0;
if (this.roleName_) {
Blockly.utils.aria.setRole(element, this.roleName_);
aria.setRole(element, this.roleName_);
}
this.element_ = element;
// Add menu items.
for (var i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
for (let i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
element.appendChild(menuItem.createDom());
}
// Add event handlers.
this.mouseOverHandler_ = Blockly.browserEvents.conditionalBind(
element, 'mouseover', this, this.handleMouseOver_, true);
this.clickHandler_ = Blockly.browserEvents.conditionalBind(
element, 'click', this, this.handleClick_, true);
this.mouseEnterHandler_ = Blockly.browserEvents.conditionalBind(
this.mouseOverHandler_ =
browserEvents.conditionalBind(element, 'mouseover', this, this.handleMouseOver_, true);
this.clickHandler_ =
browserEvents.conditionalBind(element, 'click', this, this.handleClick_, true);
this.mouseEnterHandler_ = browserEvents.conditionalBind(
element, 'mouseenter', this, this.handleMouseEnter_, true);
this.mouseLeaveHandler_ = Blockly.browserEvents.conditionalBind(
this.mouseLeaveHandler_ = browserEvents.conditionalBind(
element, 'mouseleave', this, this.handleMouseLeave_, true);
this.onKeyDownHandler_ = Blockly.browserEvents.conditionalBind(
element, 'keydown', this, this.handleKeyEvent_);
this.onKeyDownHandler_ =
browserEvents.conditionalBind(element, 'keydown', this, this.handleKeyEvent_);
container.appendChild(element);
};
@@ -152,7 +155,7 @@ Blockly.Menu.prototype.render = function(container) {
* @return {?Element} The DOM element.
* @package
*/
Blockly.Menu.prototype.getElement = function() {
Menu.prototype.getElement = function() {
return this.element_;
};
@@ -160,11 +163,11 @@ Blockly.Menu.prototype.getElement = function() {
* Focus the menu element.
* @package
*/
Blockly.Menu.prototype.focus = function() {
var el = this.getElement();
Menu.prototype.focus = function() {
const el = this.getElement();
if (el) {
el.focus({preventScroll:true});
Blockly.utils.dom.addClass(el, 'blocklyFocused');
el.focus({preventScroll: true});
dom.addClass(el, 'blocklyFocused');
}
};
@@ -172,51 +175,51 @@ Blockly.Menu.prototype.focus = function() {
* Blur the menu element.
* @private
*/
Blockly.Menu.prototype.blur_ = function() {
var el = this.getElement();
Menu.prototype.blur_ = function() {
const el = this.getElement();
if (el) {
el.blur();
Blockly.utils.dom.removeClass(el, 'blocklyFocused');
dom.removeClass(el, 'blocklyFocused');
}
};
/**
* Set the menu accessibility role.
* @param {!Blockly.utils.aria.Role} roleName role name.
* @param {!aria.Role} roleName role name.
* @package
*/
Blockly.Menu.prototype.setRole = function(roleName) {
Menu.prototype.setRole = function(roleName) {
this.roleName_ = roleName;
};
/**
* Dispose of this menu.
*/
Blockly.Menu.prototype.dispose = function() {
Menu.prototype.dispose = function() {
// Remove event handlers.
if (this.mouseOverHandler_) {
Blockly.browserEvents.unbind(this.mouseOverHandler_);
browserEvents.unbind(this.mouseOverHandler_);
this.mouseOverHandler_ = null;
}
if (this.clickHandler_) {
Blockly.browserEvents.unbind(this.clickHandler_);
browserEvents.unbind(this.clickHandler_);
this.clickHandler_ = null;
}
if (this.mouseEnterHandler_) {
Blockly.browserEvents.unbind(this.mouseEnterHandler_);
browserEvents.unbind(this.mouseEnterHandler_);
this.mouseEnterHandler_ = null;
}
if (this.mouseLeaveHandler_) {
Blockly.browserEvents.unbind(this.mouseLeaveHandler_);
browserEvents.unbind(this.mouseLeaveHandler_);
this.mouseLeaveHandler_ = null;
}
if (this.onKeyDownHandler_) {
Blockly.browserEvents.unbind(this.onKeyDownHandler_);
browserEvents.unbind(this.onKeyDownHandler_);
this.onKeyDownHandler_ = null;
}
// Remove menu items.
for (var i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
for (let i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
menuItem.dispose();
}
this.element_ = null;
@@ -228,19 +231,19 @@ Blockly.Menu.prototype.dispose = function() {
* Returns the child menu item that owns the given DOM element,
* or null if no such menu item is found.
* @param {Element} elem DOM element whose owner is to be returned.
* @return {?Blockly.MenuItem} Menu item for which the DOM element belongs to.
* @return {?MenuItem} Menu item for which the DOM element belongs to.
* @private
*/
Blockly.Menu.prototype.getMenuItem_ = function(elem) {
var menuElem = this.getElement();
Menu.prototype.getMenuItem_ = function(elem) {
const menuElem = this.getElement();
// Node might be the menu border (resulting in no associated menu item), or
// a menu item's div, or some element within the menu item.
// Walk up parents until one meets either the menu's root element, or
// a menu item's div.
while (elem && elem != menuElem) {
if (Blockly.utils.dom.hasClass(elem, 'blocklyMenuItem')) {
if (dom.hasClass(elem, 'blocklyMenuItem')) {
// Having found a menu item's div, locate that menu item in this menu.
for (var i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
for (let i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
if (menuItem.getElement() == elem) {
return menuItem;
}
@@ -255,11 +258,11 @@ Blockly.Menu.prototype.getMenuItem_ = function(elem) {
/**
* Highlights the given menu item, or clears highlighting if null.
* @param {?Blockly.MenuItem} item Item to highlight, or null.
* @param {?MenuItem} item Item to highlight, or null.
* @package
*/
Blockly.Menu.prototype.setHighlighted = function(item) {
var currentHighlighted = this.highlightedItem_;
Menu.prototype.setHighlighted = function(item) {
const currentHighlighted = this.highlightedItem_;
if (currentHighlighted) {
currentHighlighted.setHighlighted(false);
this.highlightedItem_ = null;
@@ -269,12 +272,11 @@ Blockly.Menu.prototype.setHighlighted = function(item) {
this.highlightedItem_ = item;
// Bring the highlighted item into view. This has no effect if the menu is
// not scrollable.
var el = /** @type {!Element} */ (this.getElement());
Blockly.utils.style.scrollIntoContainerView(
const el = /** @type {!Element} */ (this.getElement());
style.scrollIntoContainerView(
/** @type {!Element} */ (item.getElement()), el);
Blockly.utils.aria.setState(el, Blockly.utils.aria.State.ACTIVEDESCENDANT,
item.getId());
aria.setState(el, aria.State.ACTIVEDESCENDANT, item.getId());
}
};
@@ -283,8 +285,8 @@ Blockly.Menu.prototype.setHighlighted = function(item) {
* highlighted).
* @package
*/
Blockly.Menu.prototype.highlightNext = function() {
var index = this.menuItems_.indexOf(this.highlightedItem_);
Menu.prototype.highlightNext = function() {
const index = this.menuItems_.indexOf(this.highlightedItem_);
this.highlightHelper_(index, 1);
};
@@ -293,8 +295,8 @@ Blockly.Menu.prototype.highlightNext = function() {
* currently highlighted).
* @package
*/
Blockly.Menu.prototype.highlightPrevious = function() {
var index = this.menuItems_.indexOf(this.highlightedItem_);
Menu.prototype.highlightPrevious = function() {
const index = this.menuItems_.indexOf(this.highlightedItem_);
this.highlightHelper_(index < 0 ? this.menuItems_.length : index, -1);
};
@@ -302,7 +304,7 @@ Blockly.Menu.prototype.highlightPrevious = function() {
* Highlights the first highlightable item.
* @private
*/
Blockly.Menu.prototype.highlightFirst_ = function() {
Menu.prototype.highlightFirst_ = function() {
this.highlightHelper_(-1, 1);
};
@@ -310,7 +312,7 @@ Blockly.Menu.prototype.highlightFirst_ = function() {
* Highlights the last highlightable item.
* @private
*/
Blockly.Menu.prototype.highlightLast_ = function() {
Menu.prototype.highlightLast_ = function() {
this.highlightHelper_(this.menuItems_.length, -1);
};
@@ -321,9 +323,9 @@ Blockly.Menu.prototype.highlightLast_ = function() {
* @param {number} delta Step direction: 1 to go down, -1 to go up.
* @private
*/
Blockly.Menu.prototype.highlightHelper_ = function(startIndex, delta) {
var index = startIndex + delta;
var menuItem;
Menu.prototype.highlightHelper_ = function(startIndex, delta) {
let index = startIndex + delta;
let menuItem;
while ((menuItem = this.menuItems_[index])) {
if (menuItem.isEnabled()) {
this.setHighlighted(menuItem);
@@ -340,8 +342,8 @@ Blockly.Menu.prototype.highlightHelper_ = function(startIndex, delta) {
* @param {!Event} e Mouse event to handle.
* @private
*/
Blockly.Menu.prototype.handleMouseOver_ = function(e) {
var menuItem = this.getMenuItem_(/** @type {Element} */ (e.target));
Menu.prototype.handleMouseOver_ = function(e) {
const menuItem = this.getMenuItem_(/** @type {Element} */ (e.target));
if (menuItem) {
if (menuItem.isEnabled()) {
@@ -359,13 +361,13 @@ Blockly.Menu.prototype.handleMouseOver_ = function(e) {
* @param {!Event} e Click event to handle.
* @private
*/
Blockly.Menu.prototype.handleClick_ = function(e) {
var oldCoords = this.openingCoords;
Menu.prototype.handleClick_ = function(e) {
const oldCoords = this.openingCoords;
// Clear out the saved opening coords immediately so they're not used twice.
this.openingCoords = null;
if (oldCoords && typeof e.clientX == 'number') {
var newCoords = new Blockly.utils.Coordinate(e.clientX, e.clientY);
if (Blockly.utils.Coordinate.distance(oldCoords, newCoords) < 1) {
const newCoords = new Coordinate(e.clientX, e.clientY);
if (Coordinate.distance(oldCoords, newCoords) < 1) {
// This menu was opened by a mousedown and we're handling the consequent
// click event. The coords haven't changed, meaning this was the same
// opening event. Don't do the usual behavior because the menu just popped
@@ -374,7 +376,7 @@ Blockly.Menu.prototype.handleClick_ = function(e) {
}
}
var menuItem = this.getMenuItem_(/** @type {Element} */ (e.target));
const menuItem = this.getMenuItem_(/** @type {Element} */ (e.target));
if (menuItem) {
menuItem.performAction();
}
@@ -385,7 +387,7 @@ Blockly.Menu.prototype.handleClick_ = function(e) {
* @param {!Event} _e Mouse event to handle.
* @private
*/
Blockly.Menu.prototype.handleMouseEnter_ = function(_e) {
Menu.prototype.handleMouseEnter_ = function(_e) {
this.focus();
};
@@ -394,7 +396,7 @@ Blockly.Menu.prototype.handleMouseEnter_ = function(_e) {
* @param {!Event} _e Mouse event to handle.
* @private
*/
Blockly.Menu.prototype.handleMouseLeave_ = function(_e) {
Menu.prototype.handleMouseLeave_ = function(_e) {
if (this.getElement()) {
this.blur_();
this.setHighlighted(null);
@@ -409,7 +411,7 @@ Blockly.Menu.prototype.handleMouseLeave_ = function(_e) {
* @param {!Event} e Key event to handle.
* @private
*/
Blockly.Menu.prototype.handleKeyEvent_ = function(e) {
Menu.prototype.handleKeyEvent_ = function(e) {
if (!this.menuItems_.length) {
// Empty menu.
return;
@@ -419,30 +421,30 @@ Blockly.Menu.prototype.handleKeyEvent_ = function(e) {
return;
}
var highlighted = this.highlightedItem_;
const highlighted = this.highlightedItem_;
switch (e.keyCode) {
case Blockly.utils.KeyCodes.ENTER:
case Blockly.utils.KeyCodes.SPACE:
case KeyCodes.ENTER:
case KeyCodes.SPACE:
if (highlighted) {
highlighted.performAction();
}
break;
case Blockly.utils.KeyCodes.UP:
case KeyCodes.UP:
this.highlightPrevious();
break;
case Blockly.utils.KeyCodes.DOWN:
case KeyCodes.DOWN:
this.highlightNext();
break;
case Blockly.utils.KeyCodes.PAGE_UP:
case Blockly.utils.KeyCodes.HOME:
case KeyCodes.PAGE_UP:
case KeyCodes.HOME:
this.highlightFirst_();
break;
case Blockly.utils.KeyCodes.PAGE_DOWN:
case Blockly.utils.KeyCodes.END:
case KeyCodes.PAGE_DOWN:
case KeyCodes.END:
this.highlightLast_();
break;
@@ -457,13 +459,16 @@ Blockly.Menu.prototype.handleKeyEvent_ = function(e) {
/**
* Get the size of a rendered menu.
* @return {!Blockly.utils.Size} Object with width and height properties.
* @return {!Size} Object with width and height properties.
* @package
*/
Blockly.Menu.prototype.getSize = function() {
var menuDom = this.getElement();
var menuSize = Blockly.utils.style.getSize(/** @type {!Element} */ (menuDom));
Menu.prototype.getSize = function() {
const menuDom = this.getElement();
const menuSize = style.getSize(/** @type {!Element} */
(menuDom));
// Recalculate height for the total content, not only box height.
menuSize.height = menuDom.scrollHeight;
return menuSize;
};
exports = Menu;

View File

@@ -11,81 +11,88 @@
*/
'use strict';
goog.provide('Blockly.Mutator');
goog.module('Blockly.Mutator');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Bubble');
goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const Abstract = goog.requireType('Blockly.Events.Abstract');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const Blockly = goog.requireType('Blockly');
const Bubble = goog.require('Blockly.Bubble');
/* eslint-disable-next-line no-unused-vars */
const Connection = goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
const Icon = goog.require('Blockly.Icon');
const Options = goog.require('Blockly.Options');
const Svg = goog.require('Blockly.utils.Svg');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const WorkspaceSvg = goog.require('Blockly.WorkspaceSvg');
const Xml = goog.require('Blockly.Xml');
const dom = goog.require('Blockly.utils.dom');
const internalConstants = goog.require('Blockly.internalConstants');
const object = goog.require('Blockly.utils.object');
const toolbox = goog.require('Blockly.utils.toolbox');
const xml = goog.require('Blockly.utils.xml');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BubbleOpen');
goog.require('Blockly.Icon');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Options');
goog.require('Blockly.utils');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.toolbox');
goog.require('Blockly.utils.xml');
goog.require('Blockly.WorkspaceSvg');
goog.require('Blockly.Xml');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.Connection');
goog.requireType('Blockly.Events.Abstract');
goog.requireType('Blockly.utils.Coordinate');
goog.requireType('Blockly.Workspace');
/**
* Class for a mutator dialog.
* @param {!Array<string>} quarkNames List of names of sub-blocks for flyout.
* @extends {Blockly.Icon}
* @extends {Icon}
* @constructor
*/
Blockly.Mutator = function(quarkNames) {
Blockly.Mutator.superClass_.constructor.call(this, null);
const Mutator = function(quarkNames) {
Mutator.superClass_.constructor.call(this, null);
this.quarkNames_ = quarkNames;
};
Blockly.utils.object.inherits(Blockly.Mutator, Blockly.Icon);
object.inherits(Mutator, Icon);
/**
* Workspace in the mutator's bubble.
* @type {?Blockly.WorkspaceSvg}
* @type {?WorkspaceSvg}
* @private
*/
Blockly.Mutator.prototype.workspace_ = null;
Mutator.prototype.workspace_ = null;
/**
* Width of workspace.
* @private
*/
Blockly.Mutator.prototype.workspaceWidth_ = 0;
Mutator.prototype.workspaceWidth_ = 0;
/**
* Height of workspace.
* @private
*/
Blockly.Mutator.prototype.workspaceHeight_ = 0;
Mutator.prototype.workspaceHeight_ = 0;
/**
* Set the block this mutator is associated with.
* @param {!Blockly.BlockSvg} block The block associated with this mutator.
* @param {!BlockSvg} block The block associated with this mutator.
* @package
*/
Blockly.Mutator.prototype.setBlock = function(block) {
Mutator.prototype.setBlock = function(block) {
this.block_ = block;
};
/**
* Returns the workspace inside this mutator icon's bubble.
* @return {?Blockly.WorkspaceSvg} The workspace inside this mutator icon's
* @return {?WorkspaceSvg} The workspace inside this mutator icon's
* bubble or null if the mutator isn't open.
* @package
*/
Blockly.Mutator.prototype.getWorkspace = function() {
Mutator.prototype.getWorkspace = function() {
return this.workspace_;
};
@@ -94,11 +101,10 @@ Blockly.Mutator.prototype.getWorkspace = function() {
* @param {!Element} group The icon group.
* @protected
*/
Blockly.Mutator.prototype.drawIcon_ = function(group) {
Mutator.prototype.drawIcon_ = function(group) {
// Square with rounded corners.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
dom.createSvgElement(
Svg.RECT, {
'class': 'blocklyIconShape',
'rx': '4',
'ry': '4',
@@ -107,29 +113,22 @@ Blockly.Mutator.prototype.drawIcon_ = function(group) {
},
group);
// Gear teeth.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATH,
{
dom.createSvgElement(
Svg.PATH, {
'class': 'blocklyIconSymbol',
'd': 'm4.203,7.296 0,1.368 -0.92,0.677 -0.11,0.41 0.9,1.559 0.41,' +
'0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 1.8,0 0.3,' +
'-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 0.9,' +
'-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,' +
'-0.41 -0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 ' +
'-0.127,-1.138 -0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,' +
'0.682 -1.043,-0.457 -0.41,0.11 -0.899,1.559 0.108,0.409z'
'0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 1.8,0 0.3,' +
'-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 0.9,' +
'-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,' +
'-0.41 -0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 ' +
'-0.127,-1.138 -0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,' +
'0.682 -1.043,-0.457 -0.41,0.11 -0.899,1.559 0.108,0.409z'
},
group);
// Axle hole.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.CIRCLE,
{
'class': 'blocklyIconShape',
'r': '2.7',
'cx': '8',
'cy': '8'
},
group);
dom.createSvgElement(
Svg.CIRCLE,
{'class': 'blocklyIconShape', 'r': '2.7', 'cx': '8', 'cy': '8'}, group);
};
/**
@@ -139,9 +138,9 @@ Blockly.Mutator.prototype.drawIcon_ = function(group) {
* @protected
* @override
*/
Blockly.Mutator.prototype.iconClick_ = function(e) {
Mutator.prototype.iconClick_ = function(e) {
if (this.block_.isEditable()) {
Blockly.Icon.prototype.iconClick_.call(this, e);
Icon.prototype.iconClick_.call(this, e);
}
};
@@ -150,28 +149,27 @@ Blockly.Mutator.prototype.iconClick_ = function(e) {
* @return {!SVGElement} The top-level node of the editor.
* @private
*/
Blockly.Mutator.prototype.createEditor_ = function() {
Mutator.prototype.createEditor_ = function() {
/* Create the editor. Here's the markup that will be generated:
<svg>
[Workspace]
</svg>
*/
this.svgDialog_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.SVG,
{'x': Blockly.Bubble.BORDER_WIDTH, 'y': Blockly.Bubble.BORDER_WIDTH},
null);
this.svgDialog_ = dom.createSvgElement(
Svg.SVG, {'x': Bubble.BORDER_WIDTH, 'y': Bubble.BORDER_WIDTH}, null);
// Convert the list of names into a list of XML objects for the flyout.
let quarkXml;
if (this.quarkNames_.length) {
var quarkXml = Blockly.utils.xml.createElement('xml');
for (var i = 0, quarkName; (quarkName = this.quarkNames_[i]); i++) {
var element = Blockly.utils.xml.createElement('block');
quarkXml = xml.createElement('xml');
for (let i = 0, quarkName; (quarkName = this.quarkNames_[i]); i++) {
const element = xml.createElement('block');
element.setAttribute('type', quarkName);
quarkXml.appendChild(element);
}
} else {
var quarkXml = null;
quarkXml = null;
}
var workspaceOptions = new Blockly.Options(
const workspaceOptions = new Options(
/** @type {!Blockly.BlocklyOptions} */
({
// If you want to enable disabling, also remove the
@@ -184,25 +182,22 @@ Blockly.Mutator.prototype.createEditor_ = function() {
'renderer': this.block_.workspace.options.renderer,
'rendererOverrides': this.block_.workspace.options.rendererOverrides
}));
workspaceOptions.toolboxPosition = this.block_.RTL ?
Blockly.utils.toolbox.Position.RIGHT :
Blockly.utils.toolbox.Position.LEFT;
var hasFlyout = !!quarkXml;
workspaceOptions.toolboxPosition =
this.block_.RTL ? toolbox.Position.RIGHT : toolbox.Position.LEFT;
const hasFlyout = !!quarkXml;
if (hasFlyout) {
workspaceOptions.languageTree =
Blockly.utils.toolbox.convertToolboxDefToJson(quarkXml);
workspaceOptions.languageTree = toolbox.convertToolboxDefToJson(quarkXml);
}
this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions);
this.workspace_ = new WorkspaceSvg(workspaceOptions);
this.workspace_.isMutator = true;
this.workspace_.addChangeListener(Blockly.Events.disableOrphans);
this.workspace_.addChangeListener(Events.disableOrphans);
// Mutator flyouts go inside the mutator workspace's <g> rather than in
// a top level SVG. Instead of handling scale themselves, mutators
// inherit scale from the parent workspace.
// To fix this, scale needs to be applied at a different level in the DOM.
var flyoutSvg = hasFlyout ?
this.workspace_.addFlyout(Blockly.utils.Svg.G) : null;
var background = this.workspace_.createDom('blocklyMutatorBackground');
const flyoutSvg = hasFlyout ? this.workspace_.addFlyout(Svg.G) : null;
const background = this.workspace_.createDom('blocklyMutatorBackground');
if (flyoutSvg) {
// Insert the flyout after the <rect> but before the block canvas so that
@@ -218,12 +213,12 @@ Blockly.Mutator.prototype.createEditor_ = function() {
/**
* Add or remove the UI indicating if this icon may be clicked or not.
*/
Blockly.Mutator.prototype.updateEditable = function() {
Blockly.Mutator.superClass_.updateEditable.call(this);
Mutator.prototype.updateEditable = function() {
Mutator.superClass_.updateEditable.call(this);
if (!this.block_.isInFlyout) {
if (this.block_.isEditable()) {
if (this.iconGroup_) {
Blockly.utils.dom.removeClass(
dom.removeClass(
/** @type {!Element} */ (this.iconGroup_),
'blocklyIconGroupReadonly');
}
@@ -231,7 +226,7 @@ Blockly.Mutator.prototype.updateEditable = function() {
// Close any mutator bubble. Icon is not clickable.
this.setVisible(false);
if (this.iconGroup_) {
Blockly.utils.dom.addClass(
dom.addClass(
/** @type {!Element} */ (this.iconGroup_),
'blocklyIconGroupReadonly');
}
@@ -243,15 +238,15 @@ Blockly.Mutator.prototype.updateEditable = function() {
* Resize the bubble to match the size of the workspace.
* @private
*/
Blockly.Mutator.prototype.resizeBubble_ = function() {
var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH;
var workspaceSize = this.workspace_.getCanvas().getBBox();
var width = workspaceSize.width + workspaceSize.x;
var height = workspaceSize.height + doubleBorderWidth * 3;
var flyout = this.workspace_.getFlyout();
Mutator.prototype.resizeBubble_ = function() {
const doubleBorderWidth = 2 * Bubble.BORDER_WIDTH;
const workspaceSize = this.workspace_.getCanvas().getBBox();
let width = workspaceSize.width + workspaceSize.x;
let height = workspaceSize.height + doubleBorderWidth * 3;
const flyout = this.workspace_.getFlyout();
if (flyout) {
var flyoutScrollMetrics = flyout.getWorkspace().getMetricsManager()
.getScrollMetrics();
const flyoutScrollMetrics =
flyout.getWorkspace().getMetricsManager().getScrollMetrics();
height = Math.max(height, flyoutScrollMetrics.height + 20);
width += flyout.getWidth();
}
@@ -276,7 +271,7 @@ Blockly.Mutator.prototype.resizeBubble_ = function() {
if (this.block_.RTL) {
// Scroll the workspace to always left-align.
var translation = 'translate(' + this.workspaceWidth_ + ',0)';
const translation = 'translate(' + this.workspaceWidth_ + ',0)';
this.workspace_.getCanvas().setAttribute('transform', translation);
}
this.workspace_.resize();
@@ -286,7 +281,7 @@ Blockly.Mutator.prototype.resizeBubble_ = function() {
* A method handler for when the bubble is moved.
* @private
*/
Blockly.Mutator.prototype.onBubbleMove_ = function() {
Mutator.prototype.onBubbleMove_ = function() {
if (this.workspace_) {
this.workspace_.recordDragTargets();
}
@@ -296,43 +291,44 @@ Blockly.Mutator.prototype.onBubbleMove_ = function() {
* Show or hide the mutator bubble.
* @param {boolean} visible True if the bubble should be visible.
*/
Blockly.Mutator.prototype.setVisible = function(visible) {
Mutator.prototype.setVisible = function(visible) {
if (visible == this.isVisible()) {
// No change.
return;
}
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.BUBBLE_OPEN))(
this.block_, visible, 'mutator'));
Events.fire(
new (Events.get(Events.BUBBLE_OPEN))(this.block_, visible, 'mutator'));
if (visible) {
// Create the bubble.
this.bubble_ = new Blockly.Bubble(
/** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace),
this.bubble_ = new Bubble(
/** @type {!WorkspaceSvg} */ (this.block_.workspace),
this.createEditor_(), this.block_.pathObject.svgPath,
/** @type {!Blockly.utils.Coordinate} */ (this.iconXY_), null, null);
/** @type {!Coordinate} */ (this.iconXY_), null, null);
// Expose this mutator's block's ID on its top-level SVG group.
this.bubble_.setSvgId(this.block_.id);
this.bubble_.registerMoveEvent(this.onBubbleMove_.bind(this));
var tree = this.workspace_.options.languageTree;
var flyout = this.workspace_.getFlyout();
const tree = this.workspace_.options.languageTree;
const flyout = this.workspace_.getFlyout();
if (tree) {
flyout.init(this.workspace_);
flyout.show(tree);
}
this.rootBlock_ = this.block_.decompose(this.workspace_);
var blocks = this.rootBlock_.getDescendants(false);
for (var i = 0, child; (child = blocks[i]); i++) {
const blocks = this.rootBlock_.getDescendants(false);
for (let i = 0, child; (child = blocks[i]); i++) {
child.render();
}
// The root block should not be draggable or deletable.
this.rootBlock_.setMovable(false);
this.rootBlock_.setDeletable(false);
let margin, x;
if (flyout) {
var margin = flyout.CORNER_RADIUS * 2;
var x = this.rootBlock_.RTL ? flyout.getWidth() + margin : margin;
margin = flyout.CORNER_RADIUS * 2;
x = this.rootBlock_.RTL ? flyout.getWidth() + margin : margin;
} else {
var margin = 16;
var x = margin;
margin = 16;
x = margin;
}
if (this.block_.RTL) {
x = -x;
@@ -340,10 +336,9 @@ Blockly.Mutator.prototype.setVisible = function(visible) {
this.rootBlock_.moveBy(x, margin);
// Save the initial connections, then listen for further changes.
if (this.block_.saveConnections) {
var thisMutator = this;
var mutatorBlock =
/** @type {{saveConnections: function(!Blockly.Block)}} */ (
this.block_);
const thisMutator = this;
const mutatorBlock =
/** @type {{saveConnections: function(!Block)}} */ (this.block_);
mutatorBlock.saveConnections(this.rootBlock_);
this.sourceListener_ = function() {
mutatorBlock.saveConnections(thisMutator.rootBlock_);
@@ -375,21 +370,20 @@ Blockly.Mutator.prototype.setVisible = function(visible) {
* Update the source block when the mutator's blocks are changed.
* Bump down any block that's too high.
* Fired whenever a change is made to the mutator's workspace.
* @param {!Blockly.Events.Abstract} e Custom data for event.
* @param {!Abstract} e Custom data for event.
* @private
*/
Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
if (e.isUiEvent ||
(e.type == Blockly.Events.CHANGE && e.element == 'disabled')) {
Mutator.prototype.workspaceChanged_ = function(e) {
if (e.isUiEvent || (e.type == Events.CHANGE && e.element == 'disabled')) {
return;
}
if (!this.workspace_.isDragging()) {
var blocks = this.workspace_.getTopBlocks(false);
var MARGIN = 20;
const blocks = this.workspace_.getTopBlocks(false);
const MARGIN = 20;
for (var b = 0, block; (block = blocks[b]); b++) {
var blockXY = block.getRelativeToSurfaceXY();
for (let b = 0, block; (block = blocks[b]); b++) {
const blockXY = block.getRelativeToSurfaceXY();
// Bump any block that's above the top back inside.
if (blockXY.y < MARGIN) {
@@ -397,8 +391,8 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
}
// Bump any block overlapping the flyout back inside.
if (block.RTL) {
var right = -MARGIN;
var flyout = this.workspace_.getFlyout();
let right = -MARGIN;
const flyout = this.workspace_.getFlyout();
if (flyout) {
right -= flyout.getWidth();
}
@@ -413,13 +407,13 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
// When the mutator's workspace changes, update the source block.
if (this.rootBlock_.workspace == this.workspace_) {
Blockly.Events.setGroup(true);
var block = this.block_;
var oldMutationDom = block.mutationToDom();
var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
Events.setGroup(true);
const block = this.block_;
const oldMutationDom = block.mutationToDom();
const oldMutation = oldMutationDom && Xml.domToText(oldMutationDom);
// Switch off rendering while the source block is rebuilt.
var savedRendered = block.rendered;
const savedRendered = block.rendered;
// TODO(#4288): We should not be setting the rendered property to false.
block.rendered = false;
@@ -434,18 +428,18 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
block.render();
}
var newMutationDom = block.mutationToDom();
var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom);
const newMutationDom = block.mutationToDom();
const newMutation = newMutationDom && Xml.domToText(newMutationDom);
if (oldMutation != newMutation) {
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))(
Events.fire(new (Events.get(Events.BLOCK_CHANGE))(
block, 'mutation', null, oldMutation, newMutation));
// Ensure that any bump is part of this mutation's event group.
var group = Blockly.Events.getGroup();
const group = Events.getGroup();
setTimeout(function() {
Blockly.Events.setGroup(group);
Events.setGroup(group);
block.bumpNeighbours();
Blockly.Events.setGroup(false);
}, Blockly.internalConstants.BUMP_DELAY);
Events.setGroup(false);
}, internalConstants.BUMP_DELAY);
}
// Don't update the bubble until the drag has ended, to avoid moving blocks
@@ -453,35 +447,35 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
if (!this.workspace_.isDragging()) {
this.resizeBubble_();
}
Blockly.Events.setGroup(false);
Events.setGroup(false);
}
};
/**
* Dispose of this mutator.
*/
Blockly.Mutator.prototype.dispose = function() {
Mutator.prototype.dispose = function() {
this.block_.mutator = null;
Blockly.Icon.prototype.dispose.call(this);
Icon.prototype.dispose.call(this);
};
/**
* Update the styles on all blocks in the mutator.
* @public
*/
Blockly.Mutator.prototype.updateBlockStyle = function() {
var ws = this.workspace_;
Mutator.prototype.updateBlockStyle = function() {
const ws = this.workspace_;
if (ws && ws.getAllBlocks(false)) {
var workspaceBlocks = ws.getAllBlocks(false);
for (var i = 0, block; (block = workspaceBlocks[i]); i++) {
const workspaceBlocks = ws.getAllBlocks(false);
for (let i = 0, block; (block = workspaceBlocks[i]); i++) {
block.setStyle(block.getStyleName());
}
var flyout = ws.getFlyout();
const flyout = ws.getFlyout();
if (flyout) {
var flyoutBlocks = flyout.workspace_.getAllBlocks(false);
for (var i = 0, block; (block = flyoutBlocks[i]); i++) {
const flyoutBlocks = flyout.workspace_.getAllBlocks(false);
for (let i = 0, block; (block = flyoutBlocks[i]); i++) {
block.setStyle(block.getStyleName());
}
}
@@ -490,17 +484,17 @@ Blockly.Mutator.prototype.updateBlockStyle = function() {
/**
* Reconnect an block to a mutated input.
* @param {Blockly.Connection} connectionChild Connection on child block.
* @param {!Blockly.Block} block Parent block.
* @param {Connection} connectionChild Connection on child block.
* @param {!Block} block Parent block.
* @param {string} inputName Name of input on parent block.
* @return {boolean} True iff a reconnection was made, false otherwise.
*/
Blockly.Mutator.reconnect = function(connectionChild, block, inputName) {
Mutator.reconnect = function(connectionChild, block, inputName) {
if (!connectionChild || !connectionChild.getSourceBlock().workspace) {
return false; // No connection or block has been deleted.
}
var connectionParent = block.getInput(inputName).connection;
var currentParent = connectionChild.targetBlock();
const connectionParent = block.getInput(inputName).connection;
const currentParent = connectionChild.targetBlock();
if ((!currentParent || currentParent == block) &&
connectionParent.targetConnection != connectionChild) {
if (connectionParent.isConnected()) {
@@ -516,14 +510,14 @@ Blockly.Mutator.reconnect = function(connectionChild, block, inputName) {
/**
* Get the parent workspace of a workspace that is inside a mutator, taking into
* account whether it is a flyout.
* @param {Blockly.Workspace} workspace The workspace that is inside a mutator.
* @return {?Blockly.Workspace} The mutator's parent workspace or null.
* @param {Workspace} workspace The workspace that is inside a mutator.
* @return {?Workspace} The mutator's parent workspace or null.
* @public
*/
Blockly.Mutator.findParentWs = function(workspace) {
var outerWs = null;
Mutator.findParentWs = function(workspace) {
let outerWs = null;
if (workspace && workspace.options) {
var parent = workspace.options.parentWorkspace;
const parent = workspace.options.parentWorkspace;
// If we were in a flyout in a mutator, need to go up two levels to find
// the actual parent.
if (workspace.isFlyout) {
@@ -536,3 +530,5 @@ Blockly.Mutator.findParentWs = function(workspace) {
}
return outerWs;
};
exports = Mutator;

View File

@@ -11,20 +11,33 @@
*/
'use strict';
goog.provide('Blockly.registry');
goog.module('Blockly.registry');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.blockRendering.Renderer');
goog.requireType('Blockly.Cursor');
goog.requireType('Blockly.Events.Abstract');
goog.requireType('Blockly.Field');
goog.requireType('Blockly.IBlockDragger');
goog.requireType('Blockly.IConnectionChecker');
goog.requireType('Blockly.IFlyout');
goog.requireType('Blockly.IMetricsManager');
goog.requireType('Blockly.IToolbox');
goog.requireType('Blockly.Options');
goog.requireType('Blockly.Theme');
goog.requireType('Blockly.ToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const Abstract = goog.requireType('Blockly.Events.Abstract');
/* eslint-disable-next-line no-unused-vars */
const Cursor = goog.requireType('Blockly.Cursor');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
/* eslint-disable-next-line no-unused-vars */
const IBlockDragger = goog.requireType('Blockly.IBlockDragger');
/* eslint-disable-next-line no-unused-vars */
const IConnectionChecker = goog.requireType('Blockly.IConnectionChecker');
/* eslint-disable-next-line no-unused-vars */
const IFlyout = goog.requireType('Blockly.IFlyout');
/* eslint-disable-next-line no-unused-vars */
const IMetricsManager = goog.requireType('Blockly.IMetricsManager');
/* eslint-disable-next-line no-unused-vars */
const IToolbox = goog.requireType('Blockly.IToolbox');
/* eslint-disable-next-line no-unused-vars */
const Options = goog.requireType('Blockly.Options');
/* eslint-disable-next-line no-unused-vars */
const Renderer = goog.requireType('Blockly.blockRendering.Renderer');
/* eslint-disable-next-line no-unused-vars */
const Theme = goog.requireType('Blockly.Theme');
/* eslint-disable-next-line no-unused-vars */
const ToolboxItem = goog.requireType('Blockly.ToolboxItem');
/**
@@ -34,13 +47,16 @@ goog.requireType('Blockly.ToolboxItem');
*
* @type {Object<string, Object<string, function(new:?)>>}
*/
Blockly.registry.typeMap_ = Object.create(null);
const typeMap = Object.create(null);
/** @private */
exports.typeMap_ = typeMap;
/**
* The string used to register the default class for a type of plugin.
* @type {string}
*/
Blockly.registry.DEFAULT = 'default';
const DEFAULT = 'default';
exports.DEFAULT = DEFAULT;
/**
* A name with the type of the element stored in the generic.
@@ -48,67 +64,63 @@ Blockly.registry.DEFAULT = 'default';
* @constructor
* @template T
*/
Blockly.registry.Type = function(name) {
const Type = function(name) {
/**
* @type {string}
* @private
*/
this.name_ = name;
};
exports.Type = Type;
/**
* Returns the name of the type.
* @return {string} The name.
* @override
*/
Blockly.registry.Type.prototype.toString = function() {
Type.prototype.toString = function() {
return this.name_;
};
/** @type {!Blockly.registry.Type<Blockly.IConnectionChecker>} */
Blockly.registry.Type.CONNECTION_CHECKER =
new Blockly.registry.Type('connectionChecker');
/** @type {!Type<IConnectionChecker>} */
Type.CONNECTION_CHECKER = new Type('connectionChecker');
/** @type {!Blockly.registry.Type<Blockly.Cursor>} */
Blockly.registry.Type.CURSOR = new Blockly.registry.Type('cursor');
/** @type {!Type<Cursor>} */
Type.CURSOR = new Type('cursor');
/** @type {!Blockly.registry.Type<Blockly.Events.Abstract>} */
Blockly.registry.Type.EVENT = new Blockly.registry.Type('event');
/** @type {!Type<Abstract>} */
Type.EVENT = new Type('event');
/** @type {!Blockly.registry.Type<Blockly.Field>} */
Blockly.registry.Type.FIELD = new Blockly.registry.Type('field');
/** @type {!Type<Field>} */
Type.FIELD = new Type('field');
/** @type {!Blockly.registry.Type<Blockly.blockRendering.Renderer>} */
Blockly.registry.Type.RENDERER = new Blockly.registry.Type('renderer');
/** @type {!Type<Renderer>} */
Type.RENDERER = new Type('renderer');
/** @type {!Blockly.registry.Type<Blockly.IToolbox>} */
Blockly.registry.Type.TOOLBOX = new Blockly.registry.Type('toolbox');
/** @type {!Type<IToolbox>} */
Type.TOOLBOX = new Type('toolbox');
/** @type {!Blockly.registry.Type<Blockly.Theme>} */
Blockly.registry.Type.THEME = new Blockly.registry.Type('theme');
/** @type {!Type<Theme>} */
Type.THEME = new Type('theme');
/** @type {!Blockly.registry.Type<Blockly.ToolboxItem>} */
Blockly.registry.Type.TOOLBOX_ITEM = new Blockly.registry.Type('toolboxItem');
/** @type {!Type<ToolboxItem>} */
Type.TOOLBOX_ITEM = new Type('toolboxItem');
/** @type {!Blockly.registry.Type<Blockly.IFlyout>} */
Blockly.registry.Type.FLYOUTS_VERTICAL_TOOLBOX =
new Blockly.registry.Type('flyoutsVerticalToolbox');
/** @type {!Type<IFlyout>} */
Type.FLYOUTS_VERTICAL_TOOLBOX = new Type('flyoutsVerticalToolbox');
/** @type {!Blockly.registry.Type<Blockly.IFlyout>} */
Blockly.registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX =
new Blockly.registry.Type('flyoutsHorizontalToolbox');
/** @type {!Type<IFlyout>} */
Type.FLYOUTS_HORIZONTAL_TOOLBOX = new Type('flyoutsHorizontalToolbox');
/** @type {!Blockly.registry.Type<Blockly.IMetricsManager>} */
Blockly.registry.Type.METRICS_MANAGER =
new Blockly.registry.Type('metricsManager');
/** @type {!Type<IMetricsManager>} */
Type.METRICS_MANAGER = new Type('metricsManager');
/** @type {!Blockly.registry.Type<Blockly.IBlockDragger>} */
Blockly.registry.Type.BLOCK_DRAGGER =
new Blockly.registry.Type('blockDragger');
/** @type {!Type<IBlockDragger>} */
Type.BLOCK_DRAGGER = new Type('blockDragger');
/**
* Registers a class based on a type and name.
* @param {string|!Blockly.registry.Type<T>} type The type of the plugin.
* @param {string|!Type<T>} type The type of the plugin.
* (e.g. Field, Renderer)
* @param {string} name The plugin's name. (Ex. field_angle, geras)
* @param {?function(new:T, ...?)|Object} registryItem The class or object to
@@ -120,9 +132,8 @@ Blockly.registry.Type.BLOCK_DRAGGER =
* it's type.
* @template T
*/
Blockly.registry.register = function(
type, name, registryItem, opt_allowOverrides) {
if ((!(type instanceof Blockly.registry.Type) && typeof type != 'string') ||
const register = function(type, name, registryItem, opt_allowOverrides) {
if ((!(type instanceof Type) && typeof type != 'string') ||
String(type).trim() == '') {
throw Error(
'Invalid type "' + type + '". The type must be a' +
@@ -139,14 +150,14 @@ Blockly.registry.register = function(
if (!registryItem) {
throw Error('Can not register a null value');
}
var typeRegistry = Blockly.registry.typeMap_[type];
let typeRegistry = typeMap[type];
// If the type registry has not been created, create it.
if (!typeRegistry) {
typeRegistry = Blockly.registry.typeMap_[type] = Object.create(null);
typeRegistry = typeMap[type] = Object.create(null);
}
// Validate that the given class has all the required properties.
Blockly.registry.validate_(type, registryItem);
validate(type, registryItem);
// Don't throw an error if opt_allowOverrides is true.
if (!opt_allowOverrides && typeRegistry[name]) {
@@ -155,6 +166,7 @@ Blockly.registry.register = function(
}
typeRegistry[name] = registryItem;
};
exports.register = register;
/**
* Checks the given registry item for properties that are required based on the
@@ -162,11 +174,10 @@ Blockly.registry.register = function(
* @param {string} type The type of the plugin. (e.g. Field, Renderer)
* @param {Function|Object} registryItem A class or object that we are checking
* for the required properties.
* @private
*/
Blockly.registry.validate_ = function(type, registryItem) {
const validate = function(type, registryItem) {
switch (type) {
case String(Blockly.registry.Type.FIELD):
case String(Type.FIELD):
if (typeof registryItem.fromJson != 'function') {
throw Error('Type "' + type + '" must have a fromJson function');
}
@@ -176,27 +187,29 @@ Blockly.registry.validate_ = function(type, registryItem) {
/**
* Unregisters the registry item with the given type and name.
* @param {string|!Blockly.registry.Type<T>} type The type of the plugin.
* @param {string|!Type<T>} type The type of the plugin.
* (e.g. Field, Renderer)
* @param {string} name The plugin's name. (Ex. field_angle, geras)
* @template T
*/
Blockly.registry.unregister = function(type, name) {
const unregister = function(type, name) {
type = String(type).toLowerCase();
name = name.toLowerCase();
var typeRegistry = Blockly.registry.typeMap_[type];
const typeRegistry = typeMap[type];
if (!typeRegistry || !typeRegistry[name]) {
console.warn('Unable to unregister [' + name + '][' + type + '] from the ' +
'registry.');
console.warn(
'Unable to unregister [' + name + '][' + type + '] from the ' +
'registry.');
return;
}
delete Blockly.registry.typeMap_[type][name];
delete typeMap[type][name];
};
exports.unregister = unregister;
/**
* Gets the registry item for the given name and type. This can be either a
* class or an object.
* @param {string|!Blockly.registry.Type<T>} type The type of the plugin.
* @param {string|!Type<T>} type The type of the plugin.
* (e.g. Field, Renderer)
* @param {string} name The plugin's name. (Ex. field_angle, geras)
* @param {boolean=} opt_throwIfMissing Whether or not to throw an error if we
@@ -205,15 +218,15 @@ Blockly.registry.unregister = function(type, name) {
* name and type or null if none exists.
* @template T
*/
Blockly.registry.getItem_ = function(type, name, opt_throwIfMissing) {
const getItem = function(type, name, opt_throwIfMissing) {
type = String(type).toLowerCase();
name = name.toLowerCase();
var typeRegistry = Blockly.registry.typeMap_[type];
const typeRegistry = typeMap[type];
if (!typeRegistry || !typeRegistry[name]) {
var msg = 'Unable to find [' + name + '][' + type + '] in the registry.';
const msg = 'Unable to find [' + name + '][' + type + '] in the registry.';
if (opt_throwIfMissing) {
throw new Error(msg + ' You must require or register a ' + type +
' plugin.');
throw new Error(
msg + ' You must require or register a ' + type + ' plugin.');
} else {
console.warn(msg);
}
@@ -225,26 +238,27 @@ Blockly.registry.getItem_ = function(type, name, opt_throwIfMissing) {
/**
* Returns whether or not the registry contains an item with the given type and
* name.
* @param {string|!Blockly.registry.Type<T>} type The type of the plugin.
* @param {string|!Type<T>} type The type of the plugin.
* (e.g. Field, Renderer)
* @param {string} name The plugin's name. (Ex. field_angle, geras)
* @return {boolean} True if the registry has an item with the given type and
* name, false otherwise.
* @template T
*/
Blockly.registry.hasItem = function(type, name) {
const hasItem = function(type, name) {
type = String(type).toLowerCase();
name = name.toLowerCase();
var typeRegistry = Blockly.registry.typeMap_[type];
const typeRegistry = typeMap[type];
if (!typeRegistry) {
return false;
}
return !!(typeRegistry[name]);
};
exports.hasItem = hasItem;
/**
* Gets the class for the given name and type.
* @param {string|!Blockly.registry.Type<T>} type The type of the plugin.
* @param {string|!Type<T>} type The type of the plugin.
* (e.g. Field, Renderer)
* @param {string} name The plugin's name. (Ex. field_angle, geras)
* @param {boolean=} opt_throwIfMissing Whether or not to throw an error if we
@@ -253,14 +267,15 @@ Blockly.registry.hasItem = function(type, name) {
* null if none exists.
* @template T
*/
Blockly.registry.getClass = function(type, name, opt_throwIfMissing) {
const getClass = function(type, name, opt_throwIfMissing) {
return /** @type {?function(new:T, ...?)} */ (
Blockly.registry.getItem_(type, name, opt_throwIfMissing));
getItem(type, name, opt_throwIfMissing));
};
exports.getClass = getClass;
/**
* Gets the object for the given name and type.
* @param {string|!Blockly.registry.Type<T>} type The type of the plugin.
* @param {string|!Type<T>} type The type of the plugin.
* (e.g. Category)
* @param {string} name The plugin's name. (Ex. logic_category)
* @param {boolean=} opt_throwIfMissing Whether or not to throw an error if we
@@ -268,30 +283,30 @@ Blockly.registry.getClass = function(type, name, opt_throwIfMissing) {
* @return {?T} The object with the given name and type or null if none exists.
* @template T
*/
Blockly.registry.getObject = function(type, name, opt_throwIfMissing) {
return /** @type {T} */ (
Blockly.registry.getItem_(type, name, opt_throwIfMissing));
const getObject = function(type, name, opt_throwIfMissing) {
return /** @type {T} */ (getItem(type, name, opt_throwIfMissing));
};
exports.getObject = getObject;
/**
* Gets the class from Blockly options for the given type.
* This is used for plugins that override a built in feature. (e.g. Toolbox)
* @param {!Blockly.registry.Type<T>} type The type of the plugin.
* @param {!Blockly.Options} options The option object to check for the given
* @param {!Type<T>} type The type of the plugin.
* @param {!Options} options The option object to check for the given
* plugin.
* @param {boolean=} opt_throwIfMissing Whether or not to throw an error if we
* are unable to find the plugin.
* @return {?function(new:T, ...?)} The class for the plugin.
* @template T
*/
Blockly.registry.getClassFromOptions = function(type, options,
opt_throwIfMissing) {
var typeName = type.toString();
var plugin = options.plugins[typeName] || Blockly.registry.DEFAULT;
const getClassFromOptions = function(type, options, opt_throwIfMissing) {
const typeName = type.toString();
const plugin = options.plugins[typeName] || DEFAULT;
// If the user passed in a plugin class instead of a registered plugin name.
if (typeof plugin == 'function') {
return plugin;
}
return Blockly.registry.getClass(type, plugin, opt_throwIfMissing);
return getClass(type, plugin, opt_throwIfMissing);
};
exports.getClassFromOptions = getClassFromOptions;

View File

@@ -10,36 +10,40 @@
*/
'use strict';
goog.provide('Blockly.RenderedConnection');
goog.module('Blockly.RenderedConnection');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Connection');
goog.require('Blockly.connectionTypes');
goog.require('Blockly.internalConstants');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.deprecation');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Svg');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.ConnectionDB');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Connection = goog.require('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const ConnectionDB = goog.requireType('Blockly.ConnectionDB');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
const Svg = goog.require('Blockly.utils.Svg');
const connectionTypes = goog.require('Blockly.connectionTypes');
const deprecation = goog.require('Blockly.utils.deprecation');
const dom = goog.require('Blockly.utils.dom');
const internalConstants = goog.require('Blockly.internalConstants');
const object = goog.require('Blockly.utils.object');
const utils = goog.require('Blockly.utils');
/**
* Class for a connection between blocks that may be rendered on screen.
* @param {!Blockly.BlockSvg} source The block establishing this connection.
* @param {!BlockSvg} source The block establishing this connection.
* @param {number} type The type of the connection.
* @extends {Blockly.Connection}
* @extends {Connection}
* @constructor
*/
Blockly.RenderedConnection = function(source, type) {
Blockly.RenderedConnection.superClass_.constructor.call(this, source, type);
const RenderedConnection = function(source, type) {
RenderedConnection.superClass_.constructor.call(this, source, type);
/**
* Connection database for connections of this type on the current workspace.
* @const {!Blockly.ConnectionDB}
* @const {!ConnectionDB}
* @private
*/
this.db_ = source.workspace.connectionDBList[type];
@@ -47,34 +51,33 @@ Blockly.RenderedConnection = function(source, type) {
/**
* Connection database for connections compatible with this type on the
* current workspace.
* @const {!Blockly.ConnectionDB}
* @const {!ConnectionDB}
* @private
*/
this.dbOpposite_ =
source.workspace
.connectionDBList[Blockly.internalConstants.OPPOSITE_TYPE[type]];
source.workspace.connectionDBList[internalConstants.OPPOSITE_TYPE[type]];
/**
* Workspace units, (0, 0) is top left of block.
* @type {!Blockly.utils.Coordinate}
* @type {!Coordinate}
* @private
*/
this.offsetInBlock_ = new Blockly.utils.Coordinate(0, 0);
this.offsetInBlock_ = new Coordinate(0, 0);
/**
* Describes the state of this connection's tracked-ness.
* @type {Blockly.RenderedConnection.TrackedState}
* @type {RenderedConnection.TrackedState}
* @private
*/
this.trackedState_ = Blockly.RenderedConnection.TrackedState.WILL_TRACK;
this.trackedState_ = RenderedConnection.TrackedState.WILL_TRACK;
/**
* Connection this connection connects to. Null if not connected.
* @type {Blockly.RenderedConnection}
* @type {RenderedConnection}
*/
this.targetConnection = null;
};
Blockly.utils.object.inherits(Blockly.RenderedConnection, Blockly.Connection);
object.inherits(RenderedConnection, Connection);
/**
* Enum for different kinds of tracked states.
@@ -88,7 +91,7 @@ Blockly.utils.object.inherits(Blockly.RenderedConnection, Blockly.Connection);
* TRACKED means that this connection is currently being tracked.
* @enum {number}
*/
Blockly.RenderedConnection.TrackedState = {
RenderedConnection.TrackedState = {
WILL_TRACK: -1,
UNTRACKED: 0,
TRACKED: 1
@@ -100,65 +103,65 @@ Blockly.RenderedConnection.TrackedState = {
* @override
* @package
*/
Blockly.RenderedConnection.prototype.dispose = function() {
Blockly.RenderedConnection.superClass_.dispose.call(this);
if (this.trackedState_ == Blockly.RenderedConnection.TrackedState.TRACKED) {
RenderedConnection.prototype.dispose = function() {
RenderedConnection.superClass_.dispose.call(this);
if (this.trackedState_ == RenderedConnection.TrackedState.TRACKED) {
this.db_.removeConnection(this, this.y);
}
};
/**
* Get the source block for this connection.
* @return {!Blockly.BlockSvg} The source block.
* @return {!BlockSvg} The source block.
* @override
*/
Blockly.RenderedConnection.prototype.getSourceBlock = function() {
return /** @type {!Blockly.BlockSvg} */ (
Blockly.RenderedConnection.superClass_.getSourceBlock.call(this));
RenderedConnection.prototype.getSourceBlock = function() {
return /** @type {!BlockSvg} */ (
RenderedConnection.superClass_.getSourceBlock.call(this));
};
/**
* Returns the block that this connection connects to.
* @return {?Blockly.BlockSvg} The connected block or null if none is connected.
* @return {?BlockSvg} The connected block or null if none is connected.
* @override
*/
Blockly.RenderedConnection.prototype.targetBlock = function() {
return /** @type {Blockly.BlockSvg} */ (
Blockly.RenderedConnection.superClass_.targetBlock.call(this));
RenderedConnection.prototype.targetBlock = function() {
return /** @type {BlockSvg} */ (
RenderedConnection.superClass_.targetBlock.call(this));
};
/**
* Returns the distance between this connection and another connection in
* workspace units.
* @param {!Blockly.Connection} otherConnection The other connection to measure
* @param {!Connection} otherConnection The other connection to measure
* the distance to.
* @return {number} The distance between connections, in workspace units.
*/
Blockly.RenderedConnection.prototype.distanceFrom = function(otherConnection) {
var xDiff = this.x - otherConnection.x;
var yDiff = this.y - otherConnection.y;
RenderedConnection.prototype.distanceFrom = function(otherConnection) {
const xDiff = this.x - otherConnection.x;
const yDiff = this.y - otherConnection.y;
return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
};
/**
* Move the block(s) belonging to the connection to a point where they don't
* visually interfere with the specified connection.
* @param {!Blockly.Connection} staticConnection The connection to move away
* @param {!Connection} staticConnection The connection to move away
* from.
* @package
*/
Blockly.RenderedConnection.prototype.bumpAwayFrom = function(staticConnection) {
RenderedConnection.prototype.bumpAwayFrom = function(staticConnection) {
if (this.sourceBlock_.workspace.isDragging()) {
// Don't move blocks around while the user is doing the same.
return;
}
// Move the root block.
var rootBlock = this.sourceBlock_.getRootBlock();
let rootBlock = this.sourceBlock_.getRootBlock();
if (rootBlock.isInFlyout) {
// Don't move blocks around in a flyout.
return;
}
var reverse = false;
let reverse = false;
if (!rootBlock.isMovable()) {
// Can't bump an uneditable block away.
// Check to see if the other block is movable.
@@ -171,24 +174,21 @@ Blockly.RenderedConnection.prototype.bumpAwayFrom = function(staticConnection) {
reverse = true;
}
// Raise it to the top for extra visibility.
var selected = Blockly.selected == rootBlock;
const selected = Blockly.selected == rootBlock;
selected || rootBlock.addSelect();
var dx =
(staticConnection.x + Blockly.internalConstants.SNAP_RADIUS +
Math.floor(Math.random() * Blockly.internalConstants.BUMP_RANDOMNESS)) -
let dx = (staticConnection.x + internalConstants.SNAP_RADIUS +
Math.floor(Math.random() * internalConstants.BUMP_RANDOMNESS)) -
this.x;
var dy =
(staticConnection.y + Blockly.internalConstants.SNAP_RADIUS +
Math.floor(Math.random() * Blockly.internalConstants.BUMP_RANDOMNESS)) -
let dy = (staticConnection.y + internalConstants.SNAP_RADIUS +
Math.floor(Math.random() * internalConstants.BUMP_RANDOMNESS)) -
this.y;
if (reverse) {
// When reversing a bump due to an uneditable block, bump up.
dy = -dy;
}
if (rootBlock.RTL) {
dx = (staticConnection.x - Blockly.internalConstants.SNAP_RADIUS -
Math.floor(
Math.random() * Blockly.internalConstants.BUMP_RANDOMNESS)) -
dx = (staticConnection.x - internalConstants.SNAP_RADIUS -
Math.floor(Math.random() * internalConstants.BUMP_RANDOMNESS)) -
this.x;
}
rootBlock.moveBy(dx, dy);
@@ -200,12 +200,11 @@ Blockly.RenderedConnection.prototype.bumpAwayFrom = function(staticConnection) {
* @param {number} x New absolute x coordinate, in workspace coordinates.
* @param {number} y New absolute y coordinate, in workspace coordinates.
*/
Blockly.RenderedConnection.prototype.moveTo = function(x, y) {
if (this.trackedState_ == Blockly.RenderedConnection.TrackedState.WILL_TRACK) {
RenderedConnection.prototype.moveTo = function(x, y) {
if (this.trackedState_ == RenderedConnection.TrackedState.WILL_TRACK) {
this.db_.addConnection(this, y);
this.trackedState_ = Blockly.RenderedConnection.TrackedState.TRACKED;
} else if (this.trackedState_ == Blockly.RenderedConnection
.TrackedState.TRACKED) {
this.trackedState_ = RenderedConnection.TrackedState.TRACKED;
} else if (this.trackedState_ == RenderedConnection.TrackedState.TRACKED) {
this.db_.removeConnection(this, this.y);
this.db_.addConnection(this, y);
}
@@ -218,19 +217,19 @@ Blockly.RenderedConnection.prototype.moveTo = function(x, y) {
* @param {number} dx Change to x coordinate, in workspace units.
* @param {number} dy Change to y coordinate, in workspace units.
*/
Blockly.RenderedConnection.prototype.moveBy = function(dx, dy) {
RenderedConnection.prototype.moveBy = function(dx, dy) {
this.moveTo(this.x + dx, this.y + dy);
};
/**
* Move this connection to the location given by its offset within the block and
* the location of the block's top left corner.
* @param {!Blockly.utils.Coordinate} blockTL The location of the top left
* @param {!Coordinate} blockTL The location of the top left
* corner of the block, in workspace coordinates.
*/
Blockly.RenderedConnection.prototype.moveToOffset = function(blockTL) {
this.moveTo(blockTL.x + this.offsetInBlock_.x,
blockTL.y + this.offsetInBlock_.y);
RenderedConnection.prototype.moveToOffset = function(blockTL) {
this.moveTo(
blockTL.x + this.offsetInBlock_.x, blockTL.y + this.offsetInBlock_.y);
};
/**
@@ -238,17 +237,17 @@ Blockly.RenderedConnection.prototype.moveToOffset = function(blockTL) {
* @param {number} x The new relative x, in workspace units.
* @param {number} y The new relative y, in workspace units.
*/
Blockly.RenderedConnection.prototype.setOffsetInBlock = function(x, y) {
RenderedConnection.prototype.setOffsetInBlock = function(x, y) {
this.offsetInBlock_.x = x;
this.offsetInBlock_.y = y;
};
/**
* Get the offset of this connection relative to the top left of its block.
* @return {!Blockly.utils.Coordinate} The offset of the connection.
* @return {!Coordinate} The offset of the connection.
* @package
*/
Blockly.RenderedConnection.prototype.getOffsetInBlock = function() {
RenderedConnection.prototype.getOffsetInBlock = function() {
return this.offsetInBlock_;
};
@@ -256,19 +255,19 @@ Blockly.RenderedConnection.prototype.getOffsetInBlock = function() {
* Move the blocks on either side of this connection right next to each other.
* @package
*/
Blockly.RenderedConnection.prototype.tighten = function() {
var dx = this.targetConnection.x - this.x;
var dy = this.targetConnection.y - this.y;
RenderedConnection.prototype.tighten = function() {
const dx = this.targetConnection.x - this.x;
const dy = this.targetConnection.y - this.y;
if (dx != 0 || dy != 0) {
var block = this.targetBlock();
var svgRoot = block.getSvgRoot();
const block = this.targetBlock();
const svgRoot = block.getSvgRoot();
if (!svgRoot) {
throw Error('block is not rendered.');
}
// Workspace coordinates.
var xy = Blockly.utils.getRelativeXY(svgRoot);
block.getSvgRoot().setAttribute('transform',
'translate(' + (xy.x - dx) + ',' + (xy.y - dy) + ')');
const xy = utils.getRelativeXY(svgRoot);
block.getSvgRoot().setAttribute(
'transform', 'translate(' + (xy.x - dx) + ',' + (xy.y - dy) + ')');
block.moveConnections(-dx, -dy);
}
};
@@ -277,47 +276,44 @@ Blockly.RenderedConnection.prototype.tighten = function() {
* Find the closest compatible connection to this connection.
* All parameters are in workspace units.
* @param {number} maxLimit The maximum radius to another connection.
* @param {!Blockly.utils.Coordinate} dxy Offset between this connection's location
* @param {!Coordinate} dxy Offset between this connection's location
* in the database and the current location (as a result of dragging).
* @return {!{connection: ?Blockly.Connection, radius: number}} Contains two
* @return {!{connection: ?Connection, radius: number}} Contains two
* properties: 'connection' which is either another connection or null,
* and 'radius' which is the distance.
*/
Blockly.RenderedConnection.prototype.closest = function(maxLimit, dxy) {
RenderedConnection.prototype.closest = function(maxLimit, dxy) {
return this.dbOpposite_.searchForClosest(this, maxLimit, dxy);
};
/**
* Add highlighting around this connection.
*/
Blockly.RenderedConnection.prototype.highlight = function() {
var steps;
var sourceBlockSvg = /** @type {!Blockly.BlockSvg} */ (this.sourceBlock_);
var renderConstants = sourceBlockSvg.workspace.getRenderer().getConstants();
var shape = renderConstants.shapeFor(this);
if (this.type == Blockly.connectionTypes.INPUT_VALUE ||
this.type == Blockly.connectionTypes.OUTPUT_VALUE) {
RenderedConnection.prototype.highlight = function() {
let steps;
const sourceBlockSvg = /** @type {!BlockSvg} */ (this.sourceBlock_);
const renderConstants = sourceBlockSvg.workspace.getRenderer().getConstants();
const shape = renderConstants.shapeFor(this);
if (this.type == connectionTypes.INPUT_VALUE ||
this.type == connectionTypes.OUTPUT_VALUE) {
// Vertical line, puzzle tab, vertical line.
var yLen = renderConstants.TAB_OFFSET_FROM_TOP;
steps = Blockly.utils.svgPaths.moveBy(0, -yLen) +
Blockly.utils.svgPaths.lineOnAxis('v', yLen) +
shape.pathDown +
Blockly.utils.svgPaths.lineOnAxis('v', yLen);
const yLen = renderConstants.TAB_OFFSET_FROM_TOP;
steps = utils.svgPaths.moveBy(0, -yLen) +
utils.svgPaths.lineOnAxis('v', yLen) + shape.pathDown +
utils.svgPaths.lineOnAxis('v', yLen);
} else {
var xLen =
const xLen =
renderConstants.NOTCH_OFFSET_LEFT - renderConstants.CORNER_RADIUS;
// Horizontal line, notch, horizontal line.
steps = Blockly.utils.svgPaths.moveBy(-xLen, 0) +
Blockly.utils.svgPaths.lineOnAxis('h', xLen) +
shape.pathLeft +
Blockly.utils.svgPaths.lineOnAxis('h', xLen);
steps = utils.svgPaths.moveBy(-xLen, 0) +
utils.svgPaths.lineOnAxis('h', xLen) + shape.pathLeft +
utils.svgPaths.lineOnAxis('h', xLen);
}
var xy = this.sourceBlock_.getRelativeToSurfaceXY();
var x = this.x - xy.x;
var y = this.y - xy.y;
Blockly.Connection.highlightedPath_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATH,
{
const xy = this.sourceBlock_.getRelativeToSurfaceXY();
const x = this.x - xy.x;
const y = this.y - xy.y;
Connection.highlightedPath_ = dom.createSvgElement(
Svg.PATH, {
'class': 'blocklyHighlightedConnectionPath',
'd': steps,
transform: 'translate(' + x + ',' + y + ')' +
@@ -329,9 +325,9 @@ Blockly.RenderedConnection.prototype.highlight = function() {
/**
* Remove the highlighting around this connection.
*/
Blockly.RenderedConnection.prototype.unhighlight = function() {
Blockly.utils.dom.removeNode(Blockly.Connection.highlightedPath_);
delete Blockly.Connection.highlightedPath_;
RenderedConnection.prototype.unhighlight = function() {
dom.removeNode(Connection.highlightedPath_);
delete Connection.highlightedPath_;
};
/**
@@ -339,11 +335,11 @@ Blockly.RenderedConnection.prototype.unhighlight = function() {
* @param {boolean} doTracking If true, start tracking. If false, stop tracking.
* @package
*/
Blockly.RenderedConnection.prototype.setTracking = function(doTracking) {
if ((doTracking && this.trackedState_ ==
Blockly.RenderedConnection.TrackedState.TRACKED) ||
(!doTracking && this.trackedState_ ==
Blockly.RenderedConnection.TrackedState.UNTRACKED)) {
RenderedConnection.prototype.setTracking = function(doTracking) {
if ((doTracking &&
this.trackedState_ == RenderedConnection.TrackedState.TRACKED) ||
(!doTracking &&
this.trackedState_ == RenderedConnection.TrackedState.UNTRACKED)) {
return;
}
if (this.sourceBlock_.isInFlyout) {
@@ -352,13 +348,13 @@ Blockly.RenderedConnection.prototype.setTracking = function(doTracking) {
}
if (doTracking) {
this.db_.addConnection(this, this.y);
this.trackedState_ = Blockly.RenderedConnection.TrackedState.TRACKED;
this.trackedState_ = RenderedConnection.TrackedState.TRACKED;
return;
}
if (this.trackedState_ == Blockly.RenderedConnection.TrackedState.TRACKED) {
if (this.trackedState_ == RenderedConnection.TrackedState.TRACKED) {
this.db_.removeConnection(this, this.y);
}
this.trackedState_ = Blockly.RenderedConnection.TrackedState.UNTRACKED;
this.trackedState_ = RenderedConnection.TrackedState.UNTRACKED;
};
/**
@@ -369,20 +365,20 @@ Blockly.RenderedConnection.prototype.setTracking = function(doTracking) {
* Also closes down-stream icons/bubbles.
* @package
*/
Blockly.RenderedConnection.prototype.stopTrackingAll = function() {
RenderedConnection.prototype.stopTrackingAll = function() {
this.setTracking(false);
if (this.targetConnection) {
var blocks = this.targetBlock().getDescendants(false);
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
const blocks = this.targetBlock().getDescendants(false);
for (let i = 0; i < blocks.length; i++) {
const block = blocks[i];
// Stop tracking connections of all children.
var connections = block.getConnections_(true);
for (var j = 0; j < connections.length; j++) {
const connections = block.getConnections_(true);
for (let j = 0; j < connections.length; j++) {
connections[j].setTracking(false);
}
// Close all bubbles of all children.
var icons = block.getIcons();
for (var j = 0; j < icons.length; j++) {
const icons = block.getIcons();
for (let j = 0; j < icons.length; j++) {
icons[j].setVisible(false);
}
}
@@ -392,23 +388,23 @@ Blockly.RenderedConnection.prototype.stopTrackingAll = function() {
/**
* Start tracking this connection, as well as all down-stream connections on
* any block attached to this connection. This happens when a block is expanded.
* @return {!Array<!Blockly.Block>} List of blocks to render.
* @return {!Array<!Block>} List of blocks to render.
*/
Blockly.RenderedConnection.prototype.startTrackingAll = function() {
RenderedConnection.prototype.startTrackingAll = function() {
this.setTracking(true);
// All blocks that are not tracked must start tracking before any
// rendering takes place, since rendering requires knowing the dimensions
// of lower blocks. Also, since rendering a block renders all its parents,
// we only need to render the leaf nodes.
var renderList = [];
if (this.type != Blockly.connectionTypes.INPUT_VALUE &&
this.type != Blockly.connectionTypes.NEXT_STATEMENT) {
const renderList = [];
if (this.type != connectionTypes.INPUT_VALUE &&
this.type != connectionTypes.NEXT_STATEMENT) {
// Only spider down.
return renderList;
}
var block = this.targetBlock();
const block = this.targetBlock();
if (block) {
var connections;
let connections;
if (block.isCollapsed()) {
// This block should only be partially revealed since it is collapsed.
connections = [];
@@ -419,7 +415,7 @@ Blockly.RenderedConnection.prototype.startTrackingAll = function() {
// Show all connections of this block.
connections = block.getConnections_(true);
}
for (var i = 0; i < connections.length; i++) {
for (let i = 0; i < connections.length; i++) {
renderList.push.apply(renderList, connections[i].startTrackingAll());
}
if (!renderList.length) {
@@ -432,62 +428,60 @@ Blockly.RenderedConnection.prototype.startTrackingAll = function() {
/**
* Check if the two connections can be dragged to connect to each other.
* @param {!Blockly.Connection} candidate A nearby connection to check.
* @param {!Connection} candidate A nearby connection to check.
* @param {number=} maxRadius The maximum radius allowed for connections, in
* workspace units.
* @return {boolean} True if the connection is allowed, false otherwise.
* @deprecated July 2020
*/
Blockly.RenderedConnection.prototype.isConnectionAllowed = function(candidate,
maxRadius) {
Blockly.utils.deprecation.warn(
'RenderedConnection.prototype.isConnectionAllowed',
'July 2020',
RenderedConnection.prototype.isConnectionAllowed = function(
candidate, maxRadius) {
deprecation.warn(
'RenderedConnection.prototype.isConnectionAllowed', 'July 2020',
'July 2021',
'Blockly.Workspace.prototype.getConnectionChecker().canConnect');
if (this.distanceFrom(candidate) > maxRadius) {
return false;
}
return Blockly.RenderedConnection.superClass_.isConnectionAllowed.call(this,
candidate);
return RenderedConnection.superClass_.isConnectionAllowed.call(
this, candidate);
};
/**
* Behavior after a connection attempt fails.
* Bumps this connection away from the other connection. Called when an
* attempted connection fails.
* @param {!Blockly.Connection} otherConnection Connection that this connection
* @param {!Connection} otherConnection Connection that this connection
* failed to connect to.
* @package
*/
Blockly.RenderedConnection.prototype.onFailedConnect =
function(otherConnection) {
var block = this.getSourceBlock();
if (Blockly.Events.recordUndo) {
var group = Blockly.Events.getGroup();
setTimeout(function() {
if (!block.isDisposed() && !block.getParent()) {
Blockly.Events.setGroup(group);
this.bumpAwayFrom(otherConnection);
Blockly.Events.setGroup(false);
}
}.bind(this), Blockly.internalConstants.BUMP_DELAY);
RenderedConnection.prototype.onFailedConnect = function(otherConnection) {
const block = this.getSourceBlock();
if (Events.recordUndo) {
const group = Events.getGroup();
setTimeout(function() {
if (!block.isDisposed() && !block.getParent()) {
Events.setGroup(group);
this.bumpAwayFrom(otherConnection);
Events.setGroup(false);
}
};
}.bind(this), internalConstants.BUMP_DELAY);
}
};
/**
* Disconnect two blocks that are connected by this connection.
* @param {!Blockly.Block} parentBlock The superior block.
* @param {!Blockly.Block} childBlock The inferior block.
* @param {!Block} parentBlock The superior block.
* @param {!Block} childBlock The inferior block.
* @protected
* @override
*/
Blockly.RenderedConnection.prototype.disconnectInternal_ = function(parentBlock,
childBlock) {
Blockly.RenderedConnection.superClass_.disconnectInternal_.call(this,
parentBlock, childBlock);
RenderedConnection.prototype.disconnectInternal_ = function(
parentBlock, childBlock) {
RenderedConnection.superClass_.disconnectInternal_.call(
this, parentBlock, childBlock);
// Rerender the parent so that it may reflow.
if (parentBlock.rendered) {
parentBlock.render();
@@ -506,9 +500,9 @@ Blockly.RenderedConnection.prototype.disconnectInternal_ = function(parentBlock,
* @protected
* @override
*/
Blockly.RenderedConnection.prototype.respawnShadow_ = function() {
Blockly.RenderedConnection.superClass_.respawnShadow_.call(this);
var blockShadow = this.targetBlock();
RenderedConnection.prototype.respawnShadow_ = function() {
RenderedConnection.superClass_.respawnShadow_.call(this);
const blockShadow = this.targetBlock();
if (!blockShadow) {
// This connection must not have a shadowDom_.
return;
@@ -516,7 +510,7 @@ Blockly.RenderedConnection.prototype.respawnShadow_ = function() {
blockShadow.initSvg();
blockShadow.render(false);
var parentBlock = this.getSourceBlock();
const parentBlock = this.getSourceBlock();
if (parentBlock.rendered) {
parentBlock.render();
}
@@ -527,27 +521,27 @@ Blockly.RenderedConnection.prototype.respawnShadow_ = function() {
* Type checking does not apply, since this function is used for bumping.
* @param {number} maxLimit The maximum radius to another connection, in
* workspace units.
* @return {!Array<!Blockly.Connection>} List of connections.
* @return {!Array<!Connection>} List of connections.
* @package
*/
Blockly.RenderedConnection.prototype.neighbours = function(maxLimit) {
RenderedConnection.prototype.neighbours = function(maxLimit) {
return this.dbOpposite_.getNeighbours(this, maxLimit);
};
/**
* Connect two connections together. This is the connection on the superior
* block. Rerender blocks as needed.
* @param {!Blockly.Connection} childConnection Connection on inferior block.
* @param {!Connection} childConnection Connection on inferior block.
* @protected
*/
Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
Blockly.RenderedConnection.superClass_.connect_.call(this, childConnection);
RenderedConnection.prototype.connect_ = function(childConnection) {
RenderedConnection.superClass_.connect_.call(this, childConnection);
var parentConnection = this;
var parentBlock = parentConnection.getSourceBlock();
var childBlock = childConnection.getSourceBlock();
var parentRendered = parentBlock.rendered;
var childRendered = childBlock.rendered;
const parentConnection = this;
const parentBlock = parentConnection.getSourceBlock();
const childBlock = childConnection.getSourceBlock();
const parentRendered = parentBlock.rendered;
const childRendered = childBlock.rendered;
if (parentRendered) {
parentBlock.updateDisabled();
@@ -556,8 +550,8 @@ Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
childBlock.updateDisabled();
}
if (parentRendered && childRendered) {
if (parentConnection.type == Blockly.connectionTypes.NEXT_STATEMENT ||
parentConnection.type == Blockly.connectionTypes.PREVIOUS_STATEMENT) {
if (parentConnection.type == connectionTypes.NEXT_STATEMENT ||
parentConnection.type == connectionTypes.PREVIOUS_STATEMENT) {
// Child block may need to square off its corners if it is in a stack.
// Rendering a child will render its parent.
childBlock.render();
@@ -569,9 +563,9 @@ Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
}
// The input the child block is connected to (if any).
var parentInput = parentBlock.getInputWithBlock(childBlock);
const parentInput = parentBlock.getInputWithBlock(childBlock);
if (parentInput) {
var visible = parentInput.isVisible();
const visible = parentInput.isVisible();
childBlock.getSvgRoot().style.display = visible ? 'block' : 'none';
}
};
@@ -580,14 +574,17 @@ Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
* Function to be called when this connection's compatible types have changed.
* @protected
*/
Blockly.RenderedConnection.prototype.onCheckChanged_ = function() {
RenderedConnection.prototype.onCheckChanged_ = function() {
// The new value type may not be compatible with the existing connection.
if (this.isConnected() && (!this.targetConnection ||
!this.getConnectionChecker().canConnect(
this, this.targetConnection, false))) {
var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
if (this.isConnected() &&
(!this.targetConnection ||
!this.getConnectionChecker().canConnect(
this, this.targetConnection, false))) {
const child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
child.unplug();
// Bump away.
this.sourceBlock_.bumpNeighbours();
}
};
exports = RenderedConnection;

View File

@@ -10,19 +10,20 @@
*/
'use strict';
goog.provide('Blockly.blockRendering.ConstantProvider');
goog.module('Blockly.blockRendering.ConstantProvider');
goog.module.declareLegacyNamespace();
goog.require('Blockly.connectionTypes');
goog.require('Blockly.utils');
goog.require('Blockly.utils.colour');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.svgPaths');
goog.require('Blockly.utils.userAgent');
goog.requireType('Blockly.blockRendering.Debug');
goog.requireType('Blockly.RenderedConnection');
goog.requireType('Blockly.Theme');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
const Svg = goog.require('Blockly.utils.Svg');
/* eslint-disable-next-line no-unused-vars */
const Theme = goog.requireType('Blockly.Theme');
const colour = goog.require('Blockly.utils.colour');
const connectionTypes = goog.require('Blockly.connectionTypes');
const dom = goog.require('Blockly.utils.dom');
const svgPaths = goog.require('Blockly.utils.svgPaths');
const userAgent = goog.require('Blockly.utils.userAgent');
const utils = goog.require('Blockly.utils');
/**
@@ -30,8 +31,7 @@ goog.requireType('Blockly.Theme');
* @constructor
* @package
*/
Blockly.blockRendering.ConstantProvider = function() {
const ConstantProvider = function() {
/**
* The size of an empty spacer.
* @type {number}
@@ -214,15 +214,15 @@ Blockly.blockRendering.ConstantProvider = function() {
this.EXTERNAL_VALUE_INPUT_PADDING = 2;
/**
* The height of an empty statement input. Note that in the old rendering this
* varies slightly depending on whether the block has external or inline inputs.
* In the new rendering this is consistent. It seems unlikely that the old
* behaviour was intentional.
* The height of an empty statement input. Note that in the old rendering
* this varies slightly depending on whether the block has external or inline
* inputs. In the new rendering this is consistent. It seems unlikely that
* the old behaviour was intentional.
* @type {number}
*/
this.EMPTY_STATEMENT_INPUT_HEIGHT = this.MIN_BLOCK_HEIGHT;
this.START_POINT = Blockly.utils.svgPaths.moveBy(0, 0);
this.START_POINT = svgPaths.moveBy(0, 0);
/**
* Height of SVG path for jagged teeth at the end of collapsed blocks.
@@ -303,8 +303,7 @@ Blockly.blockRendering.ConstantProvider = function() {
* A field's text element's dominant baseline.
* @type {boolean}
*/
this.FIELD_TEXT_BASELINE_CENTER =
!Blockly.utils.userAgent.IE && !Blockly.utils.userAgent.EDGE;
this.FIELD_TEXT_BASELINE_CENTER = !userAgent.IE && !userAgent.EDGE;
/**
* A dropdown field's border rect height.
@@ -349,17 +348,17 @@ Blockly.blockRendering.ConstantProvider = function() {
* @type {string}
*/
this.FIELD_DROPDOWN_SVG_ARROW_DATAURI =
'data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllci' +
'AxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMi43MSIgaG' +
'VpZ2h0PSI4Ljc5IiB2aWV3Qm94PSIwIDAgMTIuNzEgOC43OSI+PHRpdGxlPmRyb3Bkb3duLW' +
'Fycm93PC90aXRsZT48ZyBvcGFjaXR5PSIwLjEiPjxwYXRoIGQ9Ik0xMi43MSwyLjQ0QTIuND' +
'EsMi40MSwwLDAsMSwxMiw0LjE2TDguMDgsOC4wOGEyLjQ1LDIuNDUsMCwwLDEtMy40NSwwTD' +
'AuNzIsNC4xNkEyLjQyLDIuNDIsMCwwLDEsMCwyLjQ0LDIuNDgsMi40OCwwLDAsMSwuNzEuNz' +
'FDMSwwLjQ3LDEuNDMsMCw2LjM2LDBTMTEuNzUsMC40NiwxMiwuNzFBMi40NCwyLjQ0LDAsMC' +
'wxLDEyLjcxLDIuNDRaIiBmaWxsPSIjMjMxZjIwIi8+PC9nPjxwYXRoIGQ9Ik02LjM2LDcuNz' +
'lhMS40MywxLjQzLDAsMCwxLTEtLjQyTDEuNDIsMy40NWExLjQ0LDEuNDQsMCwwLDEsMC0yYz' +
'AuNTYtLjU2LDkuMzEtMC41Niw5Ljg3LDBhMS40NCwxLjQ0LDAsMCwxLDAsMkw3LjM3LDcuMz' +
'dBMS40MywxLjQzLDAsMCwxLDYuMzYsNy43OVoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=';
'data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllci' +
'AxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMi43MSIgaG' +
'VpZ2h0PSI4Ljc5IiB2aWV3Qm94PSIwIDAgMTIuNzEgOC43OSI+PHRpdGxlPmRyb3Bkb3duLW' +
'Fycm93PC90aXRsZT48ZyBvcGFjaXR5PSIwLjEiPjxwYXRoIGQ9Ik0xMi43MSwyLjQ0QTIuND' +
'EsMi40MSwwLDAsMSwxMiw0LjE2TDguMDgsOC4wOGEyLjQ1LDIuNDUsMCwwLDEtMy40NSwwTD' +
'AuNzIsNC4xNkEyLjQyLDIuNDIsMCwwLDEsMCwyLjQ0LDIuNDgsMi40OCwwLDAsMSwuNzEuNz' +
'FDMSwwLjQ3LDEuNDMsMCw2LjM2LDBTMTEuNzUsMC40NiwxMiwuNzFBMi40NCwyLjQ0LDAsMC' +
'wxLDEyLjcxLDIuNDRaIiBmaWxsPSIjMjMxZjIwIi8+PC9nPjxwYXRoIGQ9Ik02LjM2LDcuNz' +
'lhMS40MywxLjQzLDAsMCwxLTEtLjQyTDEuNDIsMy40NWExLjQ0LDEuNDQsMCwwLDEsMC0yYz' +
'AuNTYtLjU2LDkuMzEtMC41Niw5Ljg3LDBhMS40NCwxLjQ0LDAsMCwxLDAsMkw3LjM3LDcuMz' +
'dBMS40MywxLjQzLDAsMCwxLDYuMzYsNy43OVoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=';
/**
* Whether or not to show a box shadow around the widget div. This is only a
@@ -401,6 +400,14 @@ Blockly.blockRendering.ConstantProvider = function() {
*/
this.randomIdentifier = String(Math.random()).substring(2);
/**
* The defs tag that contains all filters and patterns for this Blockly
* instance.
* @type {?SVGElement}
* @private
*/
this.defs_ = null;
/**
* The ID of the emboss filter, or the empty string if no filter is set.
* @type {string}
@@ -525,18 +532,14 @@ Blockly.blockRendering.ConstantProvider = function() {
* Enum for connection shapes.
* @enum {number}
*/
this.SHAPES = {
PUZZLE: 1,
NOTCH: 2
};
this.SHAPES = {PUZZLE: 1, NOTCH: 2};
};
/**
* Initialize shape objects based on the constants set in the constructor.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.init = function() {
ConstantProvider.prototype.init = function() {
/**
* An object containing sizing and path information about collapsed block
* indicators.
@@ -577,21 +580,19 @@ Blockly.blockRendering.ConstantProvider.prototype.init = function() {
/**
* Refresh constants properties that depend on the theme.
* @param {!Blockly.Theme} theme The current workspace theme.
* @param {!Theme} theme The current workspace theme.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.setTheme = function(
theme) {
ConstantProvider.prototype.setTheme = function(theme) {
/**
* The block styles map.
* @type {Object<string, !Blockly.Theme.BlockStyle>}
* @type {Object<string, !Theme.BlockStyle>}
* @package
*/
this.blockStyles = Object.create(null);
var blockStyles = theme.blockStyles;
for (var key in blockStyles) {
const blockStyles = theme.blockStyles;
for (const key in blockStyles) {
this.blockStyles[key] = this.validatedBlockStyle_(blockStyles[key]);
}
@@ -600,39 +601,38 @@ Blockly.blockRendering.ConstantProvider.prototype.setTheme = function(
/**
* Sets dynamic properties that depend on other values or theme properties.
* @param {!Blockly.Theme} theme The current workspace theme.
* @param {!Theme} theme The current workspace theme.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.setDynamicProperties_ =
function(theme) {
/* eslint-disable indent */
ConstantProvider.prototype.setDynamicProperties_ = function(theme) {
this.setFontConstants_(theme);
this.setComponentConstants_(theme);
this.ADD_START_HATS = theme.startHats != null ? theme.startHats :
this.ADD_START_HATS;
}; /* eslint-enable indent */
this.ADD_START_HATS =
theme.startHats != null ? theme.startHats : this.ADD_START_HATS;
};
/**
* Set constants related to fonts.
* @param {!Blockly.Theme} theme The current workspace theme.
* @param {!Theme} theme The current workspace theme.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.setFontConstants_ = function(
theme) {
ConstantProvider.prototype.setFontConstants_ = function(theme) {
this.FIELD_TEXT_FONTFAMILY =
theme.fontStyle && theme.fontStyle['family'] != undefined ?
theme.fontStyle['family'] : this.FIELD_TEXT_FONTFAMILY;
theme.fontStyle['family'] :
this.FIELD_TEXT_FONTFAMILY;
this.FIELD_TEXT_FONTWEIGHT =
theme.fontStyle && theme.fontStyle['weight'] != undefined ?
theme.fontStyle['weight'] : this.FIELD_TEXT_FONTWEIGHT;
theme.fontStyle['weight'] :
this.FIELD_TEXT_FONTWEIGHT;
this.FIELD_TEXT_FONTSIZE =
theme.fontStyle && theme.fontStyle['size'] != undefined ?
theme.fontStyle['size'] : this.FIELD_TEXT_FONTSIZE;
theme.fontStyle['size'] :
this.FIELD_TEXT_FONTSIZE;
var fontMetrics = Blockly.utils.dom.measureFontMetrics('Hg',
this.FIELD_TEXT_FONTSIZE + 'pt',
this.FIELD_TEXT_FONTWEIGHT,
const fontMetrics = dom.measureFontMetrics(
'Hg', this.FIELD_TEXT_FONTSIZE + 'pt', this.FIELD_TEXT_FONTWEIGHT,
this.FIELD_TEXT_FONTFAMILY);
this.FIELD_TEXT_HEIGHT = fontMetrics.height;
@@ -641,68 +641,60 @@ Blockly.blockRendering.ConstantProvider.prototype.setFontConstants_ = function(
/**
* Set constants from a theme's component styles.
* @param {!Blockly.Theme} theme The current workspace theme.
* @param {!Theme} theme The current workspace theme.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.setComponentConstants_ =
function(theme) {
/* eslint-disable indent */
this.CURSOR_COLOUR = theme.getComponentStyle('cursorColour') ||
this.CURSOR_COLOUR;
this.MARKER_COLOUR = theme.getComponentStyle('markerColour') ||
this.MARKER_COLOUR;
ConstantProvider.prototype.setComponentConstants_ = function(theme) {
this.CURSOR_COLOUR =
theme.getComponentStyle('cursorColour') || this.CURSOR_COLOUR;
this.MARKER_COLOUR =
theme.getComponentStyle('markerColour') || this.MARKER_COLOUR;
this.INSERTION_MARKER_COLOUR =
theme.getComponentStyle('insertionMarkerColour') ||
this.INSERTION_MARKER_COLOUR;
theme.getComponentStyle('insertionMarkerColour') ||
this.INSERTION_MARKER_COLOUR;
this.INSERTION_MARKER_OPACITY =
Number(theme.getComponentStyle('insertionMarkerOpacity')) ||
this.INSERTION_MARKER_OPACITY;
}; /* eslint-enable indent */
Number(theme.getComponentStyle('insertionMarkerOpacity')) ||
this.INSERTION_MARKER_OPACITY;
};
/**
* Get or create a block style based on a single colour value. Generate a name
* for the style based on the colour.
* @param {string} colour #RRGGBB colour string.
* @return {{style: !Blockly.Theme.BlockStyle, name: string}} An object
* @return {{style: !Theme.BlockStyle, name: string}} An object
* containing the style and an autogenerated name for that style.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.getBlockStyleForColour =
function(colour) {
/* eslint-disable indent */
var name = 'auto_' + colour;
ConstantProvider.prototype.getBlockStyleForColour = function(colour) {
const name = 'auto_' + colour;
if (!this.blockStyles[name]) {
this.blockStyles[name] = this.createBlockStyle_(colour);
}
return {style: this.blockStyles[name], name: name};
}; /* eslint-enable indent */
};
/**
* Gets the BlockStyle for the given block style name.
* @param {?string} blockStyleName The name of the block style.
* @return {!Blockly.Theme.BlockStyle} The named block style, or a default style
* @return {!Theme.BlockStyle} The named block style, or a default style
* if no style with the given name was found.
*/
Blockly.blockRendering.ConstantProvider.prototype.getBlockStyle = function(
blockStyleName) {
ConstantProvider.prototype.getBlockStyle = function(blockStyleName) {
return this.blockStyles[blockStyleName || ''] ||
(blockStyleName && blockStyleName.indexOf('auto_') == 0 ?
this.getBlockStyleForColour(blockStyleName.substring(5)).style :
this.createBlockStyle_('#000000'));
this.getBlockStyleForColour(blockStyleName.substring(5)).style :
this.createBlockStyle_('#000000'));
};
/**
* Create a block style object based on the given colour.
* @param {string} colour #RRGGBB colour string.
* @return {!Blockly.Theme.BlockStyle} A populated block style based on the
* @return {!Theme.BlockStyle} A populated block style based on the
* given colour.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.createBlockStyle_ = function(
colour) {
return this.validatedBlockStyle_({
'colourPrimary': colour
});
ConstantProvider.prototype.createBlockStyle_ = function(colour) {
return this.validatedBlockStyle_({'colourPrimary': colour});
};
/**
@@ -715,56 +707,49 @@ Blockly.blockRendering.ConstantProvider.prototype.createBlockStyle_ = function(
* hat:(string|undefined)
* }} blockStyle A full or partial block style object.
* @return {!Blockly.Theme.BlockStyle} A full block style object, with all
* @return {!Theme.BlockStyle} A full block style object, with all
* required properties populated.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.validatedBlockStyle_ =
function(blockStyle) {
/* eslint-disable indent */
ConstantProvider.prototype.validatedBlockStyle_ = function(blockStyle) {
// Make a new object with all of the same properties.
var valid = /** @type {!Blockly.Theme.BlockStyle} */ ({});
const valid = /** @type {!Theme.BlockStyle} */ ({});
if (blockStyle) {
Blockly.utils.object.mixin(valid, blockStyle);
utils.object.mixin(valid, blockStyle);
}
// Validate required properties.
var parsedColour = Blockly.utils.parseBlockColour(
valid['colourPrimary'] || '#000');
const parsedColour = utils.parseBlockColour(valid['colourPrimary'] || '#000');
valid.colourPrimary = parsedColour.hex;
valid.colourSecondary = valid['colourSecondary'] ?
Blockly.utils.parseBlockColour(valid['colourSecondary']).hex :
utils.parseBlockColour(valid['colourSecondary']).hex :
this.generateSecondaryColour_(valid.colourPrimary);
valid.colourTertiary = valid['colourTertiary'] ?
Blockly.utils.parseBlockColour(valid['colourTertiary']).hex :
utils.parseBlockColour(valid['colourTertiary']).hex :
this.generateTertiaryColour_(valid.colourPrimary);
valid.hat = valid['hat'] || '';
return valid;
}; /* eslint-enable indent */
};
/**
* Generate a secondary colour from the passed in primary colour.
* @param {string} colour Primary colour.
* @param {string} inputColour Primary colour.
* @return {string} The generated secondary colour.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.generateSecondaryColour_ =
function(colour) {
/* eslint-disable indent */
return Blockly.utils.colour.blend('#fff', colour, 0.6) || colour;
}; /* eslint-enable indent */
ConstantProvider.prototype.generateSecondaryColour_ = function(inputColour) {
return colour.blend('#fff', inputColour, 0.6) || inputColour;
};
/**
* Generate a tertiary colour from the passed in primary colour.
* @param {string} colour Primary colour.
* @param {string} inputColour Primary colour.
* @return {string} The generated tertiary colour.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.generateTertiaryColour_ =
function(colour) {
/* eslint-disable indent */
return Blockly.utils.colour.blend('#fff', colour, 0.3) || colour;
}; /* eslint-enable indent */
ConstantProvider.prototype.generateTertiaryColour_ = function(inputColour) {
return colour.blend('#fff', inputColour, 0.3) || inputColour;
};
/**
@@ -772,15 +757,15 @@ Blockly.blockRendering.ConstantProvider.prototype.generateTertiaryColour_ =
* Delete all DOM elements that this provider created.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.dispose = function() {
ConstantProvider.prototype.dispose = function() {
if (this.embossFilter_) {
Blockly.utils.dom.removeNode(this.embossFilter_);
dom.removeNode(this.embossFilter_);
}
if (this.disabledPattern_) {
Blockly.utils.dom.removeNode(this.disabledPattern_);
dom.removeNode(this.disabledPattern_);
}
if (this.debugFilter_) {
Blockly.utils.dom.removeNode(this.debugFilter_);
dom.removeNode(this.debugFilter_);
}
this.cssNode_ = null;
};
@@ -790,22 +775,15 @@ Blockly.blockRendering.ConstantProvider.prototype.dispose = function() {
* collapsed block indicators.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.makeJaggedTeeth = function() {
var height = this.JAGGED_TEETH_HEIGHT;
var width = this.JAGGED_TEETH_WIDTH;
ConstantProvider.prototype.makeJaggedTeeth = function() {
const height = this.JAGGED_TEETH_HEIGHT;
const width = this.JAGGED_TEETH_WIDTH;
var mainPath =
Blockly.utils.svgPaths.line(
[
Blockly.utils.svgPaths.point(width, height / 4),
Blockly.utils.svgPaths.point(-width * 2, height / 2),
Blockly.utils.svgPaths.point(width, height / 4)
]);
return {
height: height,
width: width,
path: mainPath
};
const mainPath = svgPaths.line([
svgPaths.point(width, height / 4), svgPaths.point(-width * 2, height / 2),
svgPaths.point(width, height / 4)
]);
return {height: height, width: width, path: mainPath};
};
/**
@@ -813,22 +791,15 @@ Blockly.blockRendering.ConstantProvider.prototype.makeJaggedTeeth = function() {
* start hats.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.makeStartHat = function() {
var height = this.START_HAT_HEIGHT;
var width = this.START_HAT_WIDTH;
ConstantProvider.prototype.makeStartHat = function() {
const height = this.START_HAT_HEIGHT;
const width = this.START_HAT_WIDTH;
var mainPath =
Blockly.utils.svgPaths.curve('c',
[
Blockly.utils.svgPaths.point(30, -height),
Blockly.utils.svgPaths.point(70, -height),
Blockly.utils.svgPaths.point(width, 0)
]);
return {
height: height,
width: width,
path: mainPath
};
const mainPath = svgPaths.curve('c', [
svgPaths.point(30, -height), svgPaths.point(70, -height),
svgPaths.point(width, 0)
]);
return {height: height, width: width, path: mainPath};
};
/**
@@ -836,9 +807,9 @@ Blockly.blockRendering.ConstantProvider.prototype.makeStartHat = function() {
* puzzle tabs.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.makePuzzleTab = function() {
var width = this.TAB_WIDTH;
var height = this.TAB_HEIGHT;
ConstantProvider.prototype.makePuzzleTab = function() {
const width = this.TAB_WIDTH;
const height = this.TAB_HEIGHT;
// The main path for the puzzle tab is made out of a few curves (c and s).
// Those curves are defined with relative positions. The 'up' and 'down'
@@ -846,35 +817,32 @@ Blockly.blockRendering.ConstantProvider.prototype.makePuzzleTab = function() {
// are the signs to use to move the cursor in the direction that the path is
// being drawn.
function makeMainPath(up) {
var forward = up ? -1 : 1;
var back = -forward;
const forward = up ? -1 : 1;
const back = -forward;
var overlap = 2.5;
var halfHeight = height / 2;
var control1Y = halfHeight + overlap;
var control2Y = halfHeight + 0.5;
var control3Y = overlap; // 2.5
const overlap = 2.5;
const halfHeight = height / 2;
const control1Y = halfHeight + overlap;
const control2Y = halfHeight + 0.5;
const control3Y = overlap; // 2.5
var endPoint1 = Blockly.utils.svgPaths.point(-width, forward * halfHeight);
var endPoint2 = Blockly.utils.svgPaths.point(width, forward * halfHeight);
const endPoint1 = svgPaths.point(-width, forward * halfHeight);
const endPoint2 = svgPaths.point(width, forward * halfHeight);
return Blockly.utils.svgPaths.curve('c',
[
Blockly.utils.svgPaths.point(0, forward * control1Y),
Blockly.utils.svgPaths.point(-width, back * control2Y),
endPoint1
]) +
Blockly.utils.svgPaths.curve('s',
[
Blockly.utils.svgPaths.point(width, back * control3Y),
endPoint2
]);
return svgPaths.curve(
'c',
[
svgPaths.point(0, forward * control1Y),
svgPaths.point(-width, back * control2Y), endPoint1
]) +
svgPaths.curve(
's', [svgPaths.point(width, back * control3Y), endPoint2]);
}
// c 0,-10 -8,8 -8,-7.5 s 8,2.5 8,-7.5
var pathUp = makeMainPath(true);
const pathUp = makeMainPath(true);
// c 0,10 -8,-8 -8,7.5 s 8,-2.5 8,7.5
var pathDown = makeMainPath(false);
const pathDown = makeMainPath(false);
return {
type: this.SHAPES.PUZZLE,
@@ -890,21 +858,20 @@ Blockly.blockRendering.ConstantProvider.prototype.makePuzzleTab = function() {
* notches.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.makeNotch = function() {
var width = this.NOTCH_WIDTH;
var height = this.NOTCH_HEIGHT;
var innerWidth = 3;
var outerWidth = (width - innerWidth) / 2;
ConstantProvider.prototype.makeNotch = function() {
const width = this.NOTCH_WIDTH;
const height = this.NOTCH_HEIGHT;
const innerWidth = 3;
const outerWidth = (width - innerWidth) / 2;
function makeMainPath(dir) {
return Blockly.utils.svgPaths.line(
[
Blockly.utils.svgPaths.point(dir * outerWidth, height),
Blockly.utils.svgPaths.point(dir * innerWidth, 0),
Blockly.utils.svgPaths.point(dir * outerWidth, -height)
]);
return svgPaths.line([
svgPaths.point(dir * outerWidth, height),
svgPaths.point(dir * innerWidth, 0),
svgPaths.point(dir * outerWidth, -height)
]);
}
var pathLeft = makeMainPath(1);
var pathRight = makeMainPath(-1);
const pathLeft = makeMainPath(1);
const pathRight = makeMainPath(-1);
return {
type: this.SHAPES.NOTCH,
@@ -920,14 +887,14 @@ Blockly.blockRendering.ConstantProvider.prototype.makeNotch = function() {
* inside corners.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.makeInsideCorners = function() {
var radius = this.CORNER_RADIUS;
ConstantProvider.prototype.makeInsideCorners = function() {
const radius = this.CORNER_RADIUS;
var innerTopLeftCorner = Blockly.utils.svgPaths.arc('a', '0 0,0', radius,
Blockly.utils.svgPaths.point(-radius, radius));
const innerTopLeftCorner =
svgPaths.arc('a', '0 0,0', radius, svgPaths.point(-radius, radius));
var innerBottomLeftCorner = Blockly.utils.svgPaths.arc('a', '0 0,0', radius,
Blockly.utils.svgPaths.point(radius, radius));
const innerBottomLeftCorner =
svgPaths.arc('a', '0 0,0', radius, svgPaths.point(radius, radius));
return {
width: radius,
@@ -942,38 +909,35 @@ Blockly.blockRendering.ConstantProvider.prototype.makeInsideCorners = function()
* outside corners.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.makeOutsideCorners = function() {
var radius = this.CORNER_RADIUS;
ConstantProvider.prototype.makeOutsideCorners = function() {
const radius = this.CORNER_RADIUS;
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
var topLeft =
Blockly.utils.svgPaths.moveBy(0, radius) +
Blockly.utils.svgPaths.arc('a', '0 0,1', radius,
Blockly.utils.svgPaths.point(radius, -radius));
const topLeft = svgPaths.moveBy(0, radius) +
svgPaths.arc('a', '0 0,1', radius, svgPaths.point(radius, -radius));
/**
* SVG path for drawing the rounded top-right corner.
* @const
*/
var topRight =
Blockly.utils.svgPaths.arc('a', '0 0,1', radius,
Blockly.utils.svgPaths.point(radius, radius));
const topRight =
svgPaths.arc('a', '0 0,1', radius, svgPaths.point(radius, radius));
/**
* SVG path for drawing the rounded bottom-left corner.
* @const
*/
var bottomLeft = Blockly.utils.svgPaths.arc('a', '0 0,1', radius,
Blockly.utils.svgPaths.point(-radius, -radius));
const bottomLeft =
svgPaths.arc('a', '0 0,1', radius, svgPaths.point(-radius, -radius));
/**
* SVG path for drawing the rounded bottom-right corner.
* @const
*/
var bottomRight = Blockly.utils.svgPaths.arc('a', '0 0,1', radius,
Blockly.utils.svgPaths.point(-radius, radius));
const bottomRight =
svgPaths.arc('a', '0 0,1', radius, svgPaths.point(-radius, radius));
return {
topLeft: topLeft,
@@ -987,19 +951,18 @@ Blockly.blockRendering.ConstantProvider.prototype.makeOutsideCorners = function(
/**
* Get an object with connection shape and sizing information based on the type
* of the connection.
* @param {!Blockly.RenderedConnection} connection The connection to find a
* @param {!RenderedConnection} connection The connection to find a
* shape object for
* @return {!Object} The shape object for the connection.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.shapeFor = function(
connection) {
ConstantProvider.prototype.shapeFor = function(connection) {
switch (connection.type) {
case Blockly.connectionTypes.INPUT_VALUE:
case Blockly.connectionTypes.OUTPUT_VALUE:
case connectionTypes.INPUT_VALUE:
case connectionTypes.OUTPUT_VALUE:
return this.PUZZLE_TAB;
case Blockly.connectionTypes.PREVIOUS_STATEMENT:
case Blockly.connectionTypes.NEXT_STATEMENT:
case connectionTypes.PREVIOUS_STATEMENT:
case connectionTypes.NEXT_STATEMENT:
return this.NOTCH;
default:
throw Error('Unknown connection type');
@@ -1014,8 +977,7 @@ Blockly.blockRendering.ConstantProvider.prototype.shapeFor = function(
* @suppress {strictModuleDepCheck} Debug renderer only included in playground.
* @package
*/
Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg,
tagName, selector) {
ConstantProvider.prototype.createDom = function(svg, tagName, selector) {
this.injectCSS_(tagName, selector);
/*
@@ -1023,8 +985,7 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg,
... filters go here ...
</defs>
*/
var defs = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.DEFS, {}, svg);
this.defs_ = dom.createSvgElement(Svg.DEFS, {}, svg);
/*
<filter id="blocklyEmbossFilter837493">
<feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur" />
@@ -1039,15 +1000,14 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg,
k1="0" k2="1" k3="1" k4="0" />
</filter>
*/
var embossFilter = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FILTER,
{'id': 'blocklyEmbossFilter' + this.randomIdentifier}, defs);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FEGAUSSIANBLUR,
const embossFilter = dom.createSvgElement(
Svg.FILTER, {'id': 'blocklyEmbossFilter' + this.randomIdentifier},
this.defs_);
dom.createSvgElement(
Svg.FEGAUSSIANBLUR,
{'in': 'SourceAlpha', 'stdDeviation': 1, 'result': 'blur'}, embossFilter);
var feSpecularLighting = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FESPECULARLIGHTING,
{
const feSpecularLighting = dom.createSvgElement(
Svg.FESPECULARLIGHTING, {
'in': 'blur',
'surfaceScale': 1,
'specularConstant': 0.5,
@@ -1056,20 +1016,19 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg,
'result': 'specOut'
},
embossFilter);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FEPOINTLIGHT,
{'x': -5000, 'y': -10000, 'z': 20000}, feSpecularLighting);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FECOMPOSITE,
{
dom.createSvgElement(
Svg.FEPOINTLIGHT, {'x': -5000, 'y': -10000, 'z': 20000},
feSpecularLighting);
dom.createSvgElement(
Svg.FECOMPOSITE, {
'in': 'specOut',
'in2': 'SourceAlpha',
'operator': 'in',
'result': 'specOut'
}, embossFilter);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FECOMPOSITE,
{
},
embossFilter);
dom.createSvgElement(
Svg.FECOMPOSITE, {
'in': 'SourceGraphic',
'in2': 'specOut',
'operator': 'arithmetic',
@@ -1077,7 +1036,8 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg,
'k2': 1,
'k3': 1,
'k4': 0
}, embossFilter);
},
embossFilter);
this.embossFilterId = embossFilter.id;
this.embossFilter_ = embossFilter;
@@ -1088,59 +1048,60 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg,
<path d="M 0 0 L 10 10 M 10 0 L 0 10" stroke="#cc0" />
</pattern>
*/
var disabledPattern = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATTERN,
{
const disabledPattern = dom.createSvgElement(
Svg.PATTERN, {
'id': 'blocklyDisabledPattern' + this.randomIdentifier,
'patternUnits': 'userSpaceOnUse',
'width': 10,
'height': 10
}, defs);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{'width': 10, 'height': 10, 'fill': '#aaa'}, disabledPattern);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATH,
{'d': 'M 0 0 L 10 10 M 10 0 L 0 10', 'stroke': '#cc0'}, disabledPattern);
},
this.defs_);
dom.createSvgElement(
Svg.RECT, {'width': 10, 'height': 10, 'fill': '#aaa'}, disabledPattern);
dom.createSvgElement(
Svg.PATH, {'d': 'M 0 0 L 10 10 M 10 0 L 0 10', 'stroke': '#cc0'},
disabledPattern);
this.disabledPatternId = disabledPattern.id;
this.disabledPattern_ = disabledPattern;
if (Blockly.blockRendering.Debug) {
var debugFilter = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FILTER,
{
this.createDebugFilter();
};
/**
* Create a filter for highlighting the currently rendering block during
* render debugging.
* @private
*/
ConstantProvider.prototype.createDebugFilter = function() {
// Only create the debug filter once.
if (!this.debugFilter_) {
const debugFilter = dom.createSvgElement(
Svg.FILTER, {
'id': 'blocklyDebugFilter' + this.randomIdentifier,
'height': '160%',
'width': '180%',
y: '-30%',
x: '-40%'
},
defs);
this.defs_);
// Set all gaussian blur pixels to 1 opacity before applying flood
var debugComponentTransfer = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FECOMPONENTTRANSFER, {
'result': 'outBlur'
}, debugFilter);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FEFUNCA,
{
'type': 'table', 'tableValues': '0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'
},
const debugComponentTransfer = dom.createSvgElement(
Svg.FECOMPONENTTRANSFER, {'result': 'outBlur'}, debugFilter);
dom.createSvgElement(
Svg.FEFUNCA,
{'type': 'table', 'tableValues': '0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'},
debugComponentTransfer);
// Color the highlight
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FEFLOOD,
{
'flood-color': '#ff0000',
'flood-opacity': 0.5,
'result': 'outColor'
},
dom.createSvgElement(
Svg.FEFLOOD,
{'flood-color': '#ff0000', 'flood-opacity': 0.5, 'result': 'outColor'},
debugFilter);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FECOMPOSITE,
{
'in': 'outColor', 'in2': 'outBlur',
'operator': 'in', 'result': 'outGlow'
dom.createSvgElement(
Svg.FECOMPOSITE, {
'in': 'outColor',
'in2': 'outBlur',
'operator': 'in',
'result': 'outGlow'
},
debugFilter);
this.debugFilterId = debugFilter.id;
@@ -1154,23 +1115,22 @@ Blockly.blockRendering.ConstantProvider.prototype.createDom = function(svg,
* @param {string} selector The CSS selector to use.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.injectCSS_ = function(
tagName, selector) {
var cssArray = this.getCSS_(selector);
var cssNodeId = 'blockly-renderer-style-' + tagName;
ConstantProvider.prototype.injectCSS_ = function(tagName, selector) {
const cssArray = this.getCSS_(selector);
const cssNodeId = 'blockly-renderer-style-' + tagName;
this.cssNode_ =
/** @type {!HTMLStyleElement} */ (document.getElementById(cssNodeId));
var text = cssArray.join('\n');
/** @type {!HTMLStyleElement} */ (document.getElementById(cssNodeId));
const text = cssArray.join('\n');
if (this.cssNode_) {
// Already injected, update if the theme changed.
this.cssNode_.firstChild.textContent = text;
return;
}
// Inject CSS tag at start of head.
var cssNode =
/** @type {!HTMLStyleElement} */ (document.createElement('style'));
const cssNode =
/** @type {!HTMLStyleElement} */ (document.createElement('style'));
cssNode.id = cssNodeId;
var cssTextNode = document.createTextNode(text);
const cssTextNode = document.createTextNode(text);
cssNode.appendChild(cssTextNode);
document.head.insertBefore(cssNode, document.head.firstChild);
this.cssNode_ = cssNode;
@@ -1182,9 +1142,10 @@ Blockly.blockRendering.ConstantProvider.prototype.injectCSS_ = function(
* @return {!Array<string>} Array of CSS strings.
* @protected
*/
Blockly.blockRendering.ConstantProvider.prototype.getCSS_ = function(selector) {
ConstantProvider.prototype.getCSS_ = function(selector) {
return [
/* eslint-disable indent */
/* clang-format off */
// Text.
selector + ' .blocklyText, ',
selector + ' .blocklyFlyoutLabelText {',
@@ -1254,6 +1215,9 @@ Blockly.blockRendering.ConstantProvider.prototype.getCSS_ = function(selector) {
'fill-opacity: ' + this.INSERTION_MARKER_OPACITY + ';',
'stroke: none;',
'}',
/* clang-format on */
/* eslint-enable indent */
];
};
exports = ConstantProvider;

View File

@@ -13,25 +13,33 @@
goog.module('Blockly.blockRendering.RenderInfo');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const BottomRow = goog.require('Blockly.blockRendering.BottomRow');
/* eslint-disable-next-line no-unused-vars */
const ConstantProvider = goog.requireType('Blockly.blockRendering.ConstantProvider');
const ExternalValueInput = goog.require('Blockly.blockRendering.ExternalValueInput');
const Field = goog.require('Blockly.blockRendering.Field');
const Hat = goog.require('Blockly.blockRendering.Hat');
const Icon = goog.require('Blockly.blockRendering.Icon');
const InlineInput = goog.require('Blockly.blockRendering.InlineInput');
/* eslint-disable-next-line no-unused-vars */
const Input = goog.requireType('Blockly.Input');
const InputRow = goog.require('Blockly.blockRendering.InputRow');
const InRowSpacer = goog.require('Blockly.blockRendering.InRowSpacer');
const JaggedEdge = goog.require('Blockly.blockRendering.JaggedEdge');
/* eslint-disable-next-line no-unused-vars */
const Measurable = goog.require('Blockly.blockRendering.Measurable');
const NextConnection = goog.require('Blockly.blockRendering.NextConnection');
const OutputConnection = goog.require('Blockly.blockRendering.OutputConnection');
const PreviousConnection = goog.require('Blockly.blockRendering.PreviousConnection');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
/* eslint-disable-next-line no-unused-vars */
const Renderer = goog.requireType('Blockly.blockRendering.Renderer');
/* eslint-disable-next-line no-unused-vars */
const RoundCorner = goog.require('Blockly.blockRendering.RoundCorner');
/* eslint-disable-next-line no-unused-vars */
const Row = goog.require('Blockly.blockRendering.Row');
const SpacerRow = goog.require('Blockly.blockRendering.SpacerRow');
const SquareCorner = goog.require('Blockly.blockRendering.SquareCorner');

View File

@@ -11,34 +11,39 @@
'use strict';
goog.provide('Blockly.blockRendering.PathObject');
goog.module('Blockly.blockRendering.PathObject');
goog.module.declareLegacyNamespace();
goog.require('Blockly.blockRendering.ConstantProvider');
goog.require('Blockly.blockRendering.IPathObject');
goog.require('Blockly.Theme');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Svg');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const Connection = goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const ConstantProvider = goog.requireType('Blockly.blockRendering.ConstantProvider');
/* eslint-disable-next-line no-unused-vars */
const IPathObject = goog.require('Blockly.blockRendering.IPathObject');
const Svg = goog.require('Blockly.utils.Svg');
/* eslint-disable-next-line no-unused-vars */
const Theme = goog.requireType('Blockly.Theme');
const dom = goog.require('Blockly.utils.dom');
/**
* An object that handles creating and setting each of the SVG elements
* used by the renderer.
* @param {!SVGElement} root The root SVG element.
* @param {!Blockly.Theme.BlockStyle} style The style object to use for
* @param {!Theme.BlockStyle} style The style object to use for
* colouring.
* @param {!Blockly.blockRendering.ConstantProvider} constants The renderer's
* @param {!ConstantProvider} constants The renderer's
* constants.
* @constructor
* @implements {Blockly.blockRendering.IPathObject}
* @implements {IPathObject}
* @package
*/
Blockly.blockRendering.PathObject = function(root, style, constants) {
const PathObject = function(root, style, constants) {
/**
* The renderer's constant provider.
* @type {!Blockly.blockRendering.ConstantProvider}
* @type {!ConstantProvider}
* @package
*/
this.constants = constants;
@@ -50,13 +55,12 @@ Blockly.blockRendering.PathObject = function(root, style, constants) {
* @type {!SVGElement}
* @package
*/
this.svgPath = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATH,
{'class': 'blocklyPath'}, this.svgRoot);
this.svgPath =
dom.createSvgElement(Svg.PATH, {'class': 'blocklyPath'}, this.svgRoot);
/**
* The style object to use when colouring block paths.
* @type {!Blockly.Theme.BlockStyle}
* @type {!Theme.BlockStyle}
* @package
*/
this.style = style;
@@ -83,7 +87,7 @@ Blockly.blockRendering.PathObject = function(root, style, constants) {
* @param {string} pathString The path.
* @package
*/
Blockly.blockRendering.PathObject.prototype.setPath = function(pathString) {
PathObject.prototype.setPath = function(pathString) {
this.svgPath.setAttribute('d', pathString);
};
@@ -91,7 +95,7 @@ Blockly.blockRendering.PathObject.prototype.setPath = function(pathString) {
* Flip the SVG paths in RTL.
* @package
*/
Blockly.blockRendering.PathObject.prototype.flipRTL = function() {
PathObject.prototype.flipRTL = function() {
// Mirror the block's path.
this.svgPath.setAttribute('transform', 'scale(-1 1)');
};
@@ -102,7 +106,7 @@ Blockly.blockRendering.PathObject.prototype.flipRTL = function() {
* block SVG group.
* @package
*/
Blockly.blockRendering.PathObject.prototype.setCursorSvg = function(cursorSvg) {
PathObject.prototype.setCursorSvg = function(cursorSvg) {
if (!cursorSvg) {
this.cursorSvg = null;
return;
@@ -118,7 +122,7 @@ Blockly.blockRendering.PathObject.prototype.setCursorSvg = function(cursorSvg) {
* block SVG group.
* @package
*/
Blockly.blockRendering.PathObject.prototype.setMarkerSvg = function(markerSvg) {
PathObject.prototype.setMarkerSvg = function(markerSvg) {
if (!markerSvg) {
this.markerSvg = null;
return;
@@ -135,10 +139,10 @@ Blockly.blockRendering.PathObject.prototype.setMarkerSvg = function(markerSvg) {
/**
* Apply the stored colours to the block's path, taking into account whether
* the paths belong to a shadow block.
* @param {!Blockly.Block} block The source block.
* @param {!Block} block The source block.
* @package
*/
Blockly.blockRendering.PathObject.prototype.applyColour = function(block) {
PathObject.prototype.applyColour = function(block) {
this.svgPath.setAttribute('stroke', this.style.colourTertiary);
this.svgPath.setAttribute('fill', this.style.colourPrimary);
@@ -148,10 +152,10 @@ Blockly.blockRendering.PathObject.prototype.applyColour = function(block) {
/**
* Set the style.
* @param {!Blockly.Theme.BlockStyle} blockStyle The block style to use.
* @param {!Theme.BlockStyle} blockStyle The block style to use.
* @package
*/
Blockly.blockRendering.PathObject.prototype.setStyle = function(blockStyle) {
PathObject.prototype.setStyle = function(blockStyle) {
this.style = blockStyle;
};
@@ -162,14 +166,11 @@ Blockly.blockRendering.PathObject.prototype.setStyle = function(blockStyle) {
* be removed.
* @protected
*/
Blockly.blockRendering.PathObject.prototype.setClass_ = function(
className, add) {
PathObject.prototype.setClass_ = function(className, add) {
if (add) {
Blockly.utils.dom.addClass(/** @type {!Element} */ (this.svgRoot),
className);
dom.addClass(/** @type {!Element} */ (this.svgRoot), className);
} else {
Blockly.utils.dom.removeClass(/** @type {!Element} */ (this.svgRoot),
className);
dom.removeClass(/** @type {!Element} */ (this.svgRoot), className);
}
};
@@ -179,11 +180,10 @@ Blockly.blockRendering.PathObject.prototype.setClass_ = function(
* @param {boolean} enable True if highlighted.
* @package
*/
Blockly.blockRendering.PathObject.prototype.updateHighlighted = function(
enable) {
PathObject.prototype.updateHighlighted = function(enable) {
if (enable) {
this.svgPath.setAttribute('filter',
'url(#' + this.constants.embossFilterId + ')');
this.svgPath.setAttribute(
'filter', 'url(#' + this.constants.embossFilterId + ')');
} else {
this.svgPath.setAttribute('filter', 'none');
}
@@ -194,7 +194,7 @@ Blockly.blockRendering.PathObject.prototype.updateHighlighted = function(
* @param {boolean} shadow True if the block is a shadow block.
* @protected
*/
Blockly.blockRendering.PathObject.prototype.updateShadow_ = function(shadow) {
PathObject.prototype.updateShadow_ = function(shadow) {
if (shadow) {
this.svgPath.setAttribute('stroke', 'none');
this.svgPath.setAttribute('fill', this.style.colourSecondary);
@@ -206,12 +206,11 @@ Blockly.blockRendering.PathObject.prototype.updateShadow_ = function(shadow) {
* @param {boolean} disabled True if disabled.
* @protected
*/
Blockly.blockRendering.PathObject.prototype.updateDisabled_ = function(
disabled) {
PathObject.prototype.updateDisabled_ = function(disabled) {
this.setClass_('blocklyDisabled', disabled);
if (disabled) {
this.svgPath.setAttribute('fill',
'url(#' + this.constants.disabledPatternId + ')');
this.svgPath.setAttribute(
'fill', 'url(#' + this.constants.disabledPatternId + ')');
}
};
@@ -220,7 +219,7 @@ Blockly.blockRendering.PathObject.prototype.updateDisabled_ = function(
* @param {boolean} enable True if selection is enabled, false otherwise.
* @package
*/
Blockly.blockRendering.PathObject.prototype.updateSelected = function(enable) {
PathObject.prototype.updateSelected = function(enable) {
this.setClass_('blocklySelected', enable);
};
@@ -230,8 +229,7 @@ Blockly.blockRendering.PathObject.prototype.updateSelected = function(enable) {
* area, false otherwise.
* @package
*/
Blockly.blockRendering.PathObject.prototype.updateDraggingDelete = function(
enable) {
PathObject.prototype.updateDraggingDelete = function(enable) {
this.setClass_('blocklyDraggingDelete', enable);
};
@@ -241,8 +239,7 @@ Blockly.blockRendering.PathObject.prototype.updateDraggingDelete = function(
* otherwise.
* @package
*/
Blockly.blockRendering.PathObject.prototype.updateInsertionMarker = function(
enable) {
PathObject.prototype.updateInsertionMarker = function(enable) {
this.setClass_('blocklyInsertionMarker', enable);
};
@@ -251,7 +248,7 @@ Blockly.blockRendering.PathObject.prototype.updateInsertionMarker = function(
* @param {boolean} enable True if the block is movable, false otherwise.
* @package
*/
Blockly.blockRendering.PathObject.prototype.updateMovable = function(enable) {
PathObject.prototype.updateMovable = function(enable) {
this.setClass_('blocklyDraggable', enable);
};
@@ -262,21 +259,19 @@ Blockly.blockRendering.PathObject.prototype.updateMovable = function(enable) {
* @param {boolean} enable True if styling should be added.
* @package
*/
Blockly.blockRendering.PathObject.prototype.updateReplacementFade =
function(enable) {
/* eslint-disable indent */
PathObject.prototype.updateReplacementFade = function(enable) {
this.setClass_('blocklyReplaceable', enable);
}; /* eslint-enable indent */
};
/**
* Add or remove styling that shows that if the dragging block is dropped, this
* block will be connected to the input.
* @param {Blockly.Connection} _conn The connection on the input to highlight.
* @param {Connection} _conn The connection on the input to highlight.
* @param {boolean} _enable True if styling should be added.
* @package
*/
Blockly.blockRendering.PathObject.prototype.updateShapeForInputHighlight =
function(_conn, _enable) {
/* eslint-disable indent */
PathObject.prototype.updateShapeForInputHighlight = function(_conn, _enable) {
// NOP
}; /* eslint-enable indent */
};
exports = PathObject;

View File

@@ -42,7 +42,6 @@ goog.require('Blockly.Trashcan');
goog.require('Blockly.VariablesDynamic');
// Only need to require these two if you're using workspace comments.
// goog.require('Blockly.WorkspaceCommentSvg');
// goog.require('Blockly.WorkspaceCommentSvg.render');
// If zoom controls aren't required, then Blockly.inject's
// "zoom"/"controls" configuration must be false.
goog.require('Blockly.ZoomControls');

View File

@@ -11,27 +11,28 @@
*/
'use strict';
goog.provide('Blockly.ShortcutRegistry');
goog.module('Blockly.ShortcutRegistry');
goog.module.declareLegacyNamespace();
goog.require('Blockly.utils.KeyCodes');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.Workspace');
const KeyCodes = goog.require('Blockly.utils.KeyCodes');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const object = goog.require('Blockly.utils.object');
/**
* Class for the registry of keyboard shortcuts. This is intended to be a
* singleton. You should not create a new instance, and only access this class
* from Blockly.ShortcutRegistry.registry.
* from ShortcutRegistry.registry.
* @constructor
*/
Blockly.ShortcutRegistry = function() {
const ShortcutRegistry = function() {
// Singleton instance should be registered once.
Blockly.ShortcutRegistry.registry = this;
ShortcutRegistry.registry = this;
/**
* Registry of all keyboard shortcuts, keyed by name of shortcut.
* @type {!Object<string, !Blockly.ShortcutRegistry.KeyboardShortcut>}
* @type {!Object<string, !ShortcutRegistry.KeyboardShortcut>}
* @private
*/
this.registry_ = Object.create(null);
@@ -46,39 +47,38 @@ Blockly.ShortcutRegistry = function() {
/**
* Enum of valid modifiers.
* @enum {!Blockly.utils.KeyCodes<number>}
* @enum {!KeyCodes<number>}
*/
Blockly.ShortcutRegistry.modifierKeys = {
'Shift': Blockly.utils.KeyCodes.SHIFT,
'Control': Blockly.utils.KeyCodes.CTRL,
'Alt': Blockly.utils.KeyCodes.ALT,
'Meta': Blockly.utils.KeyCodes.META
ShortcutRegistry.modifierKeys = {
'Shift': KeyCodes.SHIFT,
'Control': KeyCodes.CTRL,
'Alt': KeyCodes.ALT,
'Meta': KeyCodes.META
};
/**
* A keyboard shortcut.
* @typedef {{
* callback: ((function(!Blockly.Workspace, Event,
* !Blockly.ShortcutRegistry.KeyboardShortcut):boolean)|undefined),
* callback: ((function(!Workspace, Event,
* !ShortcutRegistry.KeyboardShortcut):boolean)|undefined),
* name: string,
* preconditionFn: ((function(!Blockly.Workspace):boolean)|undefined),
* preconditionFn: ((function(!Workspace):boolean)|undefined),
* metadata: (Object|undefined)
* }}
*/
Blockly.ShortcutRegistry.KeyboardShortcut;
ShortcutRegistry.KeyboardShortcut;
/**
* Registers a keyboard shortcut.
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} shortcut The
* @param {!ShortcutRegistry.KeyboardShortcut} shortcut The
* shortcut for this key code.
* @param {boolean=} opt_allowOverrides True to prevent a warning when
* overriding an already registered item.
* @throws {Error} if a shortcut with the same name already exists.
* @public
*/
Blockly.ShortcutRegistry.prototype.register = function(
shortcut, opt_allowOverrides) {
var registeredShortcut = this.registry_[shortcut.name];
ShortcutRegistry.prototype.register = function(shortcut, opt_allowOverrides) {
const registeredShortcut = this.registry_[shortcut.name];
if (registeredShortcut && !opt_allowOverrides) {
throw new Error(
'Shortcut with name "' + shortcut.name + '" already exists.');
@@ -93,8 +93,8 @@ Blockly.ShortcutRegistry.prototype.register = function(
* @return {boolean} True if an item was unregistered, false otherwise.
* @public
*/
Blockly.ShortcutRegistry.prototype.unregister = function(shortcutName) {
var shortcut = this.registry_[shortcutName];
ShortcutRegistry.prototype.unregister = function(shortcutName) {
const shortcut = this.registry_[shortcutName];
if (!shortcut) {
console.warn(
@@ -110,9 +110,9 @@ Blockly.ShortcutRegistry.prototype.unregister = function(shortcutName) {
/**
* Adds a mapping between a keycode and a keyboard shortcut.
* @param {string|Blockly.utils.KeyCodes} keyCode The key code for the keyboard
* @param {string|KeyCodes} keyCode The key code for the keyboard
* shortcut. If registering a key code with a modifier (ex: ctrl+c) use
* Blockly.ShortcutRegistry.registry.createSerializedKey;
* ShortcutRegistry.registry.createSerializedKey;
* @param {string} shortcutName The name of the shortcut to execute when the
* given keycode is pressed.
* @param {boolean=} opt_allowCollision True to prevent an error when adding a
@@ -120,10 +120,10 @@ Blockly.ShortcutRegistry.prototype.unregister = function(shortcutName) {
* @throws {Error} if the given key code is already mapped to a shortcut.
* @public
*/
Blockly.ShortcutRegistry.prototype.addKeyMapping = function(
ShortcutRegistry.prototype.addKeyMapping = function(
keyCode, shortcutName, opt_allowCollision) {
keyCode = String(keyCode);
var shortcutNames = this.keyMap_[keyCode];
const shortcutNames = this.keyMap_[keyCode];
if (shortcutNames && !opt_allowCollision) {
throw new Error(
'Shortcut with name "' + shortcutName + '" collides with shortcuts ' +
@@ -139,7 +139,7 @@ Blockly.ShortcutRegistry.prototype.addKeyMapping = function(
* Removes a mapping between a keycode and a keyboard shortcut.
* @param {string} keyCode The key code for the keyboard shortcut. If
* registering a key code with a modifier (ex: ctrl+c) use
* Blockly.ShortcutRegistry.registry.createSerializedKey;
* ShortcutRegistry.registry.createSerializedKey;
* @param {string} shortcutName The name of the shortcut to execute when the
* given keycode is pressed.
* @param {boolean=} opt_quiet True to not console warn when there is no
@@ -147,9 +147,9 @@ Blockly.ShortcutRegistry.prototype.addKeyMapping = function(
* @return {boolean} True if a key mapping was removed, false otherwise.
* @public
*/
Blockly.ShortcutRegistry.prototype.removeKeyMapping = function(
ShortcutRegistry.prototype.removeKeyMapping = function(
keyCode, shortcutName, opt_quiet) {
var shortcutNames = this.keyMap_[keyCode];
const shortcutNames = this.keyMap_[keyCode];
if (!shortcutNames && !opt_quiet) {
console.warn(
@@ -158,7 +158,7 @@ Blockly.ShortcutRegistry.prototype.removeKeyMapping = function(
return false;
}
var shortcutIdx = shortcutNames.indexOf(shortcutName);
const shortcutIdx = shortcutNames.indexOf(shortcutName);
if (shortcutIdx > -1) {
shortcutNames.splice(shortcutIdx, 1);
if (shortcutNames.length == 0) {
@@ -167,7 +167,8 @@ Blockly.ShortcutRegistry.prototype.removeKeyMapping = function(
return true;
}
if (!opt_quiet) {
console.warn('No keyboard shortcut with name "' + shortcutName +
console.warn(
'No keyboard shortcut with name "' + shortcutName +
'" registered with key code "' + keyCode + '"');
}
return false;
@@ -175,13 +176,14 @@ Blockly.ShortcutRegistry.prototype.removeKeyMapping = function(
/**
* Removes all the key mappings for a shortcut with the given name.
* Useful when changing the default key mappings and the key codes registered to the shortcut are
* unknown.
* @param {string} shortcutName The name of the shortcut to remove from the key map.
* Useful when changing the default key mappings and the key codes registered to
* the shortcut are unknown.
* @param {string} shortcutName The name of the shortcut to remove from the key
* map.
* @public
*/
Blockly.ShortcutRegistry.prototype.removeAllKeyMappings = function(shortcutName) {
for (var keyCode in this.keyMap_) {
ShortcutRegistry.prototype.removeAllKeyMappings = function(shortcutName) {
for (const keyCode in this.keyMap_) {
this.removeKeyMapping(keyCode, shortcutName, true);
}
};
@@ -192,46 +194,46 @@ Blockly.ShortcutRegistry.prototype.removeAllKeyMappings = function(shortcutName)
* shortcut names.
* @public
*/
Blockly.ShortcutRegistry.prototype.setKeyMap = function(keyMap) {
ShortcutRegistry.prototype.setKeyMap = function(keyMap) {
this.keyMap_ = keyMap;
};
/**
* Gets the current key map.
* @return {!Object<string,!Array<!Blockly.ShortcutRegistry.KeyboardShortcut>>}
* The object holding key codes to Blockly.ShortcutRegistry.KeyboardShortcut.
* @return {!Object<string,!Array<!ShortcutRegistry.KeyboardShortcut>>}
* The object holding key codes to ShortcutRegistry.KeyboardShortcut.
* @public
*/
Blockly.ShortcutRegistry.prototype.getKeyMap = function() {
return Blockly.utils.object.deepMerge(Object.create(null), this.keyMap_);
ShortcutRegistry.prototype.getKeyMap = function() {
return object.deepMerge(Object.create(null), this.keyMap_);
};
/**
* Gets the registry of keyboard shortcuts.
* @return {!Object<string, !Blockly.ShortcutRegistry.KeyboardShortcut>}
* @return {!Object<string, !ShortcutRegistry.KeyboardShortcut>}
* The registry of keyboard shortcuts.
* @public
*/
Blockly.ShortcutRegistry.prototype.getRegistry = function() {
return Blockly.utils.object.deepMerge(Object.create(null), this.registry_);
ShortcutRegistry.prototype.getRegistry = function() {
return object.deepMerge(Object.create(null), this.registry_);
};
/**
* Handles key down events.
* @param {!Blockly.Workspace} workspace The main workspace where the event was
* @param {!Workspace} workspace The main workspace where the event was
* captured.
* @param {!Event} e The key down event.
* @return {boolean} True if the event was handled, false otherwise.
* @public
*/
Blockly.ShortcutRegistry.prototype.onKeyDown = function(workspace, e) {
var key = this.serializeKeyEvent_(e);
var shortcutNames = this.getShortcutNamesByKeyCode(key);
ShortcutRegistry.prototype.onKeyDown = function(workspace, e) {
const key = this.serializeKeyEvent_(e);
const shortcutNames = this.getShortcutNamesByKeyCode(key);
if (!shortcutNames) {
return false;
}
for (var i = 0, shortcutName; (shortcutName = shortcutNames[i]); i++) {
var shortcut = this.registry_[shortcutName];
for (let i = 0, shortcutName; (shortcutName = shortcutNames[i]); i++) {
const shortcut = this.registry_[shortcutName];
if (!shortcut.preconditionFn || shortcut.preconditionFn(workspace)) {
// If the key has been handled, stop processing shortcuts.
if (shortcut.callback && shortcut.callback(workspace, e, shortcut)) {
@@ -249,8 +251,7 @@ Blockly.ShortcutRegistry.prototype.onKeyDown = function(workspace, e) {
* given keyCode is used. Undefined if no shortcuts exist.
* @public
*/
Blockly.ShortcutRegistry.prototype.getShortcutNamesByKeyCode = function(
keyCode) {
ShortcutRegistry.prototype.getShortcutNamesByKeyCode = function(keyCode) {
return this.keyMap_[keyCode] || [];
};
@@ -262,12 +263,11 @@ Blockly.ShortcutRegistry.prototype.getShortcutNamesByKeyCode = function(
* registered under.
* @public
*/
Blockly.ShortcutRegistry.prototype.getKeyCodesByShortcutName = function(
shortcutName) {
var keys = [];
for (var keyCode in this.keyMap_) {
var shortcuts = this.keyMap_[keyCode];
var shortcutIdx = shortcuts.indexOf(shortcutName);
ShortcutRegistry.prototype.getKeyCodesByShortcutName = function(shortcutName) {
const keys = [];
for (const keyCode in this.keyMap_) {
const shortcuts = this.keyMap_[keyCode];
const shortcutIdx = shortcuts.indexOf(shortcutName);
if (shortcutIdx > -1) {
keys.push(keyCode);
}
@@ -281,9 +281,9 @@ Blockly.ShortcutRegistry.prototype.getKeyCodesByShortcutName = function(
* @return {string} The serialized key code for the given event.
* @private
*/
Blockly.ShortcutRegistry.prototype.serializeKeyEvent_ = function(e) {
var serializedKey = '';
for (var modifier in Blockly.ShortcutRegistry.modifierKeys) {
ShortcutRegistry.prototype.serializeKeyEvent_ = function(e) {
let serializedKey = '';
for (const modifier in ShortcutRegistry.modifierKeys) {
if (e.getModifierState(modifier)) {
if (serializedKey != '') {
serializedKey += '+';
@@ -305,11 +305,9 @@ Blockly.ShortcutRegistry.prototype.serializeKeyEvent_ = function(e) {
* @throws {Error} if the modifier is not in the valid modifiers list.
* @private
*/
Blockly.ShortcutRegistry.prototype.checkModifiers_ = function(
modifiers) {
var validModifiers = Blockly.utils.object.values(
Blockly.ShortcutRegistry.modifierKeys);
for (var i = 0, modifier; (modifier = modifiers[i]); i++) {
ShortcutRegistry.prototype.checkModifiers_ = function(modifiers) {
const validModifiers = object.values(ShortcutRegistry.modifierKeys);
for (let i = 0, modifier; (modifier = modifiers[i]); i++) {
if (validModifiers.indexOf(modifier) < 0) {
throw new Error(modifier + ' is not a valid modifier key.');
}
@@ -321,19 +319,17 @@ Blockly.ShortcutRegistry.prototype.checkModifiers_ = function(
* @param {number} keyCode Number code representing the key.
* @param {?Array<string>} modifiers List of modifier key codes to be used with
* the key. All valid modifiers can be found in the
* Blockly.ShortcutRegistry.modifierKeys.
* ShortcutRegistry.modifierKeys.
* @return {string} The serialized key code for the given modifiers and key.
* @public
*/
Blockly.ShortcutRegistry.prototype.createSerializedKey = function(
keyCode, modifiers) {
var serializedKey = '';
ShortcutRegistry.prototype.createSerializedKey = function(keyCode, modifiers) {
let serializedKey = '';
if (modifiers) {
this.checkModifiers_(modifiers);
for (var modifier in Blockly.ShortcutRegistry.modifierKeys) {
var modifierKeyCode =
Blockly.ShortcutRegistry.modifierKeys[modifier];
for (const modifier in ShortcutRegistry.modifierKeys) {
const modifierKeyCode = ShortcutRegistry.modifierKeys[modifier];
if (modifiers.indexOf(modifierKeyCode) > -1) {
if (serializedKey != '') {
serializedKey += '+';
@@ -352,4 +348,6 @@ Blockly.ShortcutRegistry.prototype.createSerializedKey = function(
};
// Creates and assigns the singleton instance.
new Blockly.ShortcutRegistry();
new ShortcutRegistry();
exports = ShortcutRegistry;

View File

@@ -12,47 +12,50 @@
*/
'use strict';
goog.provide('Blockly.ThemeManager');
goog.module('Blockly.ThemeManager');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Theme');
goog.requireType('Blockly.Workspace');
goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const Theme = goog.requireType('Blockly.Theme');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const dom = goog.require('Blockly.utils.dom');
/**
* Class for storing and updating a workspace's theme and UI components.
* @param {!Blockly.WorkspaceSvg} workspace The main workspace.
* @param {!Blockly.Theme} theme The workspace theme.
* @param {!WorkspaceSvg} workspace The main workspace.
* @param {!Theme} theme The workspace theme.
* @constructor
* @package
*/
Blockly.ThemeManager = function(workspace, theme) {
const ThemeManager = function(workspace, theme) {
/**
* The main workspace.
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @private
*/
this.workspace_ = workspace;
/**
* The Blockly theme to use.
* @type {!Blockly.Theme}
* @type {!Theme}
* @private
*/
this.theme_ = theme;
/**
* A list of workspaces that are subscribed to this theme.
* @type {!Array<Blockly.Workspace>}
* @type {!Array<Workspace>}
* @private
*/
this.subscribedWorkspaces_ = [];
/**
* A map of subscribed UI components, keyed by component name.
* @type {!Object<string, !Array<!Blockly.ThemeManager.Component>>}
* @type {!Object<string, !Array<!ThemeManager.Component>>}
* @private
*/
this.componentDB_ = Object.create(null);
@@ -61,51 +64,51 @@ Blockly.ThemeManager = function(workspace, theme) {
/**
* A Blockly UI component type.
* @typedef {{
* element:!Element,
* propertyName:string
* }}
*/
Blockly.ThemeManager.Component;
* element:!Element,
* propertyName:string
* }}
*/
ThemeManager.Component;
/**
* Get the workspace theme.
* @return {!Blockly.Theme} The workspace theme.
* @return {!Theme} The workspace theme.
* @package
*/
Blockly.ThemeManager.prototype.getTheme = function() {
ThemeManager.prototype.getTheme = function() {
return this.theme_;
};
/**
* Set the workspace theme, and refresh the workspace and all components.
* @param {!Blockly.Theme} theme The workspace theme.
* @param {!Theme} theme The workspace theme.
* @package
*/
Blockly.ThemeManager.prototype.setTheme = function(theme) {
var prevTheme = this.theme_;
ThemeManager.prototype.setTheme = function(theme) {
const prevTheme = this.theme_;
this.theme_ = theme;
// Set the theme name onto the injection div.
var injectionDiv = this.workspace_.getInjectionDiv();
const injectionDiv = this.workspace_.getInjectionDiv();
if (injectionDiv) {
if (prevTheme) {
Blockly.utils.dom.removeClass(injectionDiv, prevTheme.getClassName());
dom.removeClass(injectionDiv, prevTheme.getClassName());
}
Blockly.utils.dom.addClass(injectionDiv, this.theme_.getClassName());
dom.addClass(injectionDiv, this.theme_.getClassName());
}
// Refresh all subscribed workspaces.
for (var i = 0, workspace; (workspace = this.subscribedWorkspaces_[i]); i++) {
for (let i = 0, workspace; (workspace = this.subscribedWorkspaces_[i]); i++) {
workspace.refreshTheme();
}
// Refresh all registered Blockly UI components.
for (var i = 0, keys = Object.keys(this.componentDB_),
key; (key = keys[i]); i++) {
for (var j = 0, component; (component = this.componentDB_[key][j]); j++) {
var element = component.element;
var propertyName = component.propertyName;
var style = this.theme_ && this.theme_.getComponentStyle(key);
for (let i = 0, keys = Object.keys(this.componentDB_), key; (key = keys[i]);
i++) {
for (let j = 0, component; (component = this.componentDB_[key][j]); j++) {
const element = component.element;
const propertyName = component.propertyName;
const style = this.theme_ && this.theme_.getComponentStyle(key);
element.style[propertyName] = style || '';
}
}
@@ -116,20 +119,20 @@ Blockly.ThemeManager.prototype.setTheme = function(theme) {
/**
* Subscribe a workspace to changes to the selected theme. If a new theme is
* set, the workspace is called to refresh its blocks.
* @param {!Blockly.Workspace} workspace The workspace to subscribe.
* @param {!Workspace} workspace The workspace to subscribe.
* @package
*/
Blockly.ThemeManager.prototype.subscribeWorkspace = function(workspace) {
ThemeManager.prototype.subscribeWorkspace = function(workspace) {
this.subscribedWorkspaces_.push(workspace);
};
/**
* Unsubscribe a workspace to changes to the selected theme.
* @param {!Blockly.Workspace} workspace The workspace to unsubscribe.
* @param {!Workspace} workspace The workspace to unsubscribe.
* @package
*/
Blockly.ThemeManager.prototype.unsubscribeWorkspace = function(workspace) {
var index = this.subscribedWorkspaces_.indexOf(workspace);
ThemeManager.prototype.unsubscribeWorkspace = function(workspace) {
const index = this.subscribedWorkspaces_.indexOf(workspace);
if (index < 0) {
throw Error('Cannot unsubscribe a workspace that hasn\'t been subscribed.');
}
@@ -145,20 +148,18 @@ Blockly.ThemeManager.prototype.unsubscribeWorkspace = function(workspace) {
* @param {string} propertyName The inline style property name to update.
* @package
*/
Blockly.ThemeManager.prototype.subscribe = function(element, componentName,
propertyName) {
ThemeManager.prototype.subscribe = function(
element, componentName, propertyName) {
if (!this.componentDB_[componentName]) {
this.componentDB_[componentName] = [];
}
// Add the element to our component map.
this.componentDB_[componentName].push({
element: element,
propertyName: propertyName
});
this.componentDB_[componentName].push(
{element: element, propertyName: propertyName});
// Initialize the element with its corresponding theme style.
var style = this.theme_ && this.theme_.getComponentStyle(componentName);
const style = this.theme_ && this.theme_.getComponentStyle(componentName);
element.style[propertyName] = style || '';
};
@@ -167,15 +168,15 @@ Blockly.ThemeManager.prototype.subscribe = function(element, componentName,
* @param {Element} element The element to unsubscribe.
* @package
*/
Blockly.ThemeManager.prototype.unsubscribe = function(element) {
ThemeManager.prototype.unsubscribe = function(element) {
if (!element) {
return;
}
// Go through all component, and remove any references to this element.
var componentNames = Object.keys(this.componentDB_);
for (var c = 0, componentName; (componentName = componentNames[c]); c++) {
var elements = this.componentDB_[componentName];
for (var i = elements.length - 1; i >= 0; i--) {
const componentNames = Object.keys(this.componentDB_);
for (let c = 0, componentName; (componentName = componentNames[c]); c++) {
const elements = this.componentDB_[componentName];
for (let i = elements.length - 1; i >= 0; i--) {
if (elements[i].element === element) {
elements.splice(i, 1);
}
@@ -192,9 +193,11 @@ Blockly.ThemeManager.prototype.unsubscribe = function(element) {
* @package
* @suppress {checkTypes}
*/
Blockly.ThemeManager.prototype.dispose = function() {
ThemeManager.prototype.dispose = function() {
this.owner_ = null;
this.theme_ = null;
this.subscribedWorkspaces_ = null;
this.componentDB_ = null;
};
exports = ThemeManager;

View File

@@ -10,34 +10,36 @@
*/
'use strict';
goog.provide('Blockly.CollapsibleToolboxCategory');
goog.module('Blockly.CollapsibleToolboxCategory');
goog.module.declareLegacyNamespace();
goog.require('Blockly.ICollapsibleToolboxItem');
goog.require('Blockly.registry');
goog.require('Blockly.ToolboxCategory');
goog.require('Blockly.ToolboxItem');
goog.require('Blockly.ToolboxSeparator');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.toolbox');
goog.requireType('Blockly.IToolbox');
goog.requireType('Blockly.IToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const ICollapsibleToolboxItem = goog.require('Blockly.ICollapsibleToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const IToolbox = goog.requireType('Blockly.IToolbox');
/* eslint-disable-next-line no-unused-vars */
const IToolboxItem = goog.requireType('Blockly.IToolboxItem');
const ToolboxCategory = goog.require('Blockly.ToolboxCategory');
const ToolboxSeparator = goog.require('Blockly.ToolboxSeparator');
const aria = goog.require('Blockly.utils.aria');
const dom = goog.require('Blockly.utils.dom');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
const toolbox = goog.require('Blockly.utils.toolbox');
/**
* Class for a category in a toolbox that can be collapsed.
* @param {!Blockly.utils.toolbox.CategoryInfo} categoryDef The information needed
* @param {!toolbox.CategoryInfo} categoryDef The information needed
* to create a category in the toolbox.
* @param {!Blockly.IToolbox} toolbox The parent toolbox for the category.
* @param {Blockly.ICollapsibleToolboxItem=} opt_parent The parent category or null if
* @param {!IToolbox} toolbox The parent toolbox for the category.
* @param {ICollapsibleToolboxItem=} opt_parent The parent category or null if
* the category does not have a parent.
* @constructor
* @extends {Blockly.ToolboxCategory}
* @implements {Blockly.ICollapsibleToolboxItem}
* @extends {ToolboxCategory}
* @implements {ICollapsibleToolboxItem}
*/
Blockly.CollapsibleToolboxCategory = function(categoryDef, toolbox, opt_parent) {
const CollapsibleToolboxCategory = function(categoryDef, toolbox, opt_parent) {
/**
* Container for any child categories.
* @type {?Element}
@@ -54,16 +56,16 @@ Blockly.CollapsibleToolboxCategory = function(categoryDef, toolbox, opt_parent)
/**
* The child toolbox items for this category.
* @type {!Array<!Blockly.ToolboxItem>}
* @type {!Array<!IToolboxItem>}
* @protected
*/
this.toolboxItems_ = [];
Blockly.CollapsibleToolboxCategory.superClass_.constructor.call(
CollapsibleToolboxCategory.superClass_.constructor.call(
this, categoryDef, toolbox, opt_parent);
};
Blockly.utils.object.inherits(Blockly.CollapsibleToolboxCategory, Blockly.ToolboxCategory);
object.inherits(CollapsibleToolboxCategory, ToolboxCategory);
/**
* All the CSS class names that are used to create a collapsible
@@ -80,19 +82,20 @@ Blockly.utils.object.inherits(Blockly.CollapsibleToolboxCategory, Blockly.Toolbo
* contents:?string
* }}
*/
Blockly.CollapsibleToolboxCategory.CssConfig;
CollapsibleToolboxCategory.CssConfig;
/**
* Name used for registering a collapsible toolbox category.
* @const {string}
*/
Blockly.CollapsibleToolboxCategory.registrationName = 'collapsibleCategory';
CollapsibleToolboxCategory.registrationName = 'collapsibleCategory';
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.makeDefaultCssConfig_ = function() {
var cssConfig = Blockly.CollapsibleToolboxCategory.superClass_.makeDefaultCssConfig_.call(this);
CollapsibleToolboxCategory.prototype.makeDefaultCssConfig_ = function() {
const cssConfig =
CollapsibleToolboxCategory.superClass_.makeDefaultCssConfig_.call(this);
cssConfig['contents'] = 'blocklyToolboxContents';
return cssConfig;
};
@@ -100,20 +103,21 @@ Blockly.CollapsibleToolboxCategory.prototype.makeDefaultCssConfig_ = function()
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.parseContents_ = function(categoryDef) {
var contents = categoryDef['contents'];
var prevIsFlyoutItem = true;
CollapsibleToolboxCategory.prototype.parseContents_ = function(categoryDef) {
const contents = categoryDef['contents'];
let prevIsFlyoutItem = true;
if (categoryDef['custom']) {
this.flyoutItems_ = categoryDef['custom'];
} else if (contents) {
for (var i = 0, itemDef; (itemDef = contents[i]); i++) {
for (let i = 0; i < contents.length; i++) {
const itemDef = contents[i];
// Separators can exist as either a flyout item or a toolbox item so
// decide where it goes based on the type of the previous item.
if (!Blockly.registry.hasItem(Blockly.registry.Type.TOOLBOX_ITEM, itemDef['kind']) ||
(itemDef['kind'].toLowerCase() == Blockly.ToolboxSeparator.registrationName &&
prevIsFlyoutItem)) {
var flyoutItem = /** @type {Blockly.utils.toolbox.FlyoutItemInfo} */ (itemDef);
if (!registry.hasItem(registry.Type.TOOLBOX_ITEM, itemDef['kind']) ||
(itemDef['kind'].toLowerCase() == ToolboxSeparator.registrationName &&
prevIsFlyoutItem)) {
const flyoutItem = /** @type {toolbox.FlyoutItemInfo} */ (itemDef);
this.flyoutItems_.push(flyoutItem);
prevIsFlyoutItem = true;
} else {
@@ -126,46 +130,46 @@ Blockly.CollapsibleToolboxCategory.prototype.parseContents_ = function(categoryD
/**
* Creates a toolbox item and adds it to the list of toolbox items.
* @param {!Blockly.utils.toolbox.ToolboxItemInfo} itemDef The information needed
* @param {!toolbox.ToolboxItemInfo} itemDef The information needed
* to create a toolbox item.
* @private
*/
Blockly.CollapsibleToolboxCategory.prototype.createToolboxItem_ = function(itemDef) {
var registryName = itemDef['kind'];
var categoryDef = /** @type {!Blockly.utils.toolbox.CategoryInfo} */ (itemDef);
CollapsibleToolboxCategory.prototype.createToolboxItem_ = function(itemDef) {
let registryName = itemDef['kind'];
const categoryDef = /** @type {!toolbox.CategoryInfo} */ (itemDef);
// Categories that are collapsible are created using a class registered under
// a diffferent name.
if (registryName.toUpperCase() == 'CATEGORY' &&
Blockly.utils.toolbox.isCategoryCollapsible(categoryDef)) {
registryName = Blockly.CollapsibleToolboxCategory.registrationName;
toolbox.isCategoryCollapsible(categoryDef)) {
registryName = CollapsibleToolboxCategory.registrationName;
}
var ToolboxItemClass = Blockly.registry.getClass(
Blockly.registry.Type.TOOLBOX_ITEM, registryName);
var toolboxItem = new ToolboxItemClass(itemDef, this.parentToolbox_, this);
const ToolboxItemClass =
registry.getClass(registry.Type.TOOLBOX_ITEM, registryName);
const toolboxItem = new ToolboxItemClass(itemDef, this.parentToolbox_, this);
this.toolboxItems_.push(toolboxItem);
};
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.init = function() {
Blockly.CollapsibleToolboxCategory.superClass_.init.call(this);
CollapsibleToolboxCategory.prototype.init = function() {
CollapsibleToolboxCategory.superClass_.init.call(this);
this.setExpanded(this.toolboxItemDef_['expanded'] == 'true' ||
this.setExpanded(
this.toolboxItemDef_['expanded'] == 'true' ||
this.toolboxItemDef_['expanded']);
};
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.createDom_ = function() {
Blockly.CollapsibleToolboxCategory.superClass_.createDom_.call(this);
CollapsibleToolboxCategory.prototype.createDom_ = function() {
CollapsibleToolboxCategory.superClass_.createDom_.call(this);
var subCategories = this.getChildToolboxItems();
const subCategories = this.getChildToolboxItems();
this.subcategoriesDiv_ = this.createSubCategoriesDom_(subCategories);
Blockly.utils.aria.setRole(this.subcategoriesDiv_,
Blockly.utils.aria.Role.GROUP);
aria.setRole(this.subcategoriesDiv_, aria.Role.GROUP);
this.htmlDiv_.appendChild(this.subcategoriesDiv_);
return this.htmlDiv_;
@@ -174,10 +178,10 @@ Blockly.CollapsibleToolboxCategory.prototype.createDom_ = function() {
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.createIconDom_ = function() {
var toolboxIcon = document.createElement('span');
CollapsibleToolboxCategory.prototype.createIconDom_ = function() {
const toolboxIcon = document.createElement('span');
if (!this.parentToolbox_.isHorizontal()) {
Blockly.utils.dom.addClass(toolboxIcon, this.cssConfig_['icon']);
dom.addClass(toolboxIcon, this.cssConfig_['icon']);
toolboxIcon.style.visibility = 'visible';
}
@@ -187,18 +191,19 @@ Blockly.CollapsibleToolboxCategory.prototype.createIconDom_ = function() {
/**
* Create the DOM for all subcategories.
* @param {!Array<!Blockly.ToolboxItem>} subcategories The subcategories.
* @param {!Array<!IToolboxItem>} subcategories The subcategories.
* @return {!Element} The div holding all the subcategories.
* @protected
*/
Blockly.CollapsibleToolboxCategory.prototype.createSubCategoriesDom_ = function(subcategories) {
var contentsContainer = document.createElement('div');
Blockly.utils.dom.addClass(contentsContainer, this.cssConfig_['contents']);
CollapsibleToolboxCategory.prototype.createSubCategoriesDom_ = function(
subcategories) {
const contentsContainer = document.createElement('div');
dom.addClass(contentsContainer, this.cssConfig_['contents']);
for (var i = 0; i < subcategories.length; i++) {
var newCategory = subcategories[i];
for (let i = 0; i < subcategories.length; i++) {
const newCategory = subcategories[i];
newCategory.init();
var newCategoryDiv = newCategory.getDiv();
const newCategoryDiv = newCategory.getDiv();
contentsContainer.appendChild(newCategoryDiv);
if (newCategory.getClickTarget) {
newCategory.getClickTarget().setAttribute('id', newCategory.getId());
@@ -213,7 +218,7 @@ Blockly.CollapsibleToolboxCategory.prototype.createSubCategoriesDom_ = function(
* @param {boolean} isExpanded True to expand the category, false to close.
* @public
*/
Blockly.CollapsibleToolboxCategory.prototype.setExpanded = function(isExpanded) {
CollapsibleToolboxCategory.prototype.setExpanded = function(isExpanded) {
if (this.expanded_ == isExpanded) {
return;
}
@@ -225,8 +230,8 @@ Blockly.CollapsibleToolboxCategory.prototype.setExpanded = function(isExpanded)
this.subcategoriesDiv_.style.display = 'none';
this.closeIcon_(this.iconDom_);
}
Blockly.utils.aria.setState(/** @type {!Element} */ (this.htmlDiv_),
Blockly.utils.aria.State.EXPANDED, isExpanded);
aria.setState(
/** @type {!Element} */ (this.htmlDiv_), aria.State.EXPANDED, isExpanded);
this.parentToolbox_.handleToolboxItemResize();
};
@@ -234,9 +239,11 @@ Blockly.CollapsibleToolboxCategory.prototype.setExpanded = function(isExpanded)
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.setVisible_ = function(isVisible) {
CollapsibleToolboxCategory.prototype.setVisible_ = function(isVisible) {
this.htmlDiv_.style.display = isVisible ? 'block' : 'none';
for (var i = 0, child; (child = this.getChildToolboxItems()[i]); i++) {
const childToolboxItems = this.getChildToolboxItems();
for (let i = 0; i < childToolboxItems.length; i++) {
const child = childToolboxItems[i];
child.setVisible_(isVisible);
}
this.isHidden_ = !isVisible;
@@ -252,21 +259,21 @@ Blockly.CollapsibleToolboxCategory.prototype.setVisible_ = function(isVisible) {
* is collapsed.
* @public
*/
Blockly.CollapsibleToolboxCategory.prototype.isExpanded = function() {
CollapsibleToolboxCategory.prototype.isExpanded = function() {
return this.expanded_;
};
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.isCollapsible = function() {
CollapsibleToolboxCategory.prototype.isCollapsible = function() {
return true;
};
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.onClick = function(_e) {
CollapsibleToolboxCategory.prototype.onClick = function(_e) {
this.toggleExpanded();
};
@@ -274,25 +281,28 @@ Blockly.CollapsibleToolboxCategory.prototype.onClick = function(_e) {
* Toggles whether or not the category is expanded.
* @public
*/
Blockly.CollapsibleToolboxCategory.prototype.toggleExpanded = function() {
CollapsibleToolboxCategory.prototype.toggleExpanded = function() {
this.setExpanded(!this.expanded_);
};
/**
* @override
*/
Blockly.CollapsibleToolboxCategory.prototype.getDiv = function() {
CollapsibleToolboxCategory.prototype.getDiv = function() {
return this.htmlDiv_;
};
/**
* Gets any children toolbox items. (ex. Gets the subcategories)
* @return {!Array<!Blockly.IToolboxItem>} The child toolbox items.
* @return {!Array<!IToolboxItem>} The child toolbox items.
*/
Blockly.CollapsibleToolboxCategory.prototype.getChildToolboxItems = function() {
CollapsibleToolboxCategory.prototype.getChildToolboxItems = function() {
return this.toolboxItems_;
};
Blockly.registry.register(Blockly.registry.Type.TOOLBOX_ITEM,
Blockly.CollapsibleToolboxCategory.registrationName, Blockly.CollapsibleToolboxCategory);
registry.register(
registry.Type.TOOLBOX_ITEM, CollapsibleToolboxCategory.registrationName,
CollapsibleToolboxCategory);
exports = CollapsibleToolboxCategory;

View File

@@ -16,36 +16,37 @@
* @name Blockly.utils.dom
* @namespace
*/
goog.provide('Blockly.utils.dom');
goog.module('Blockly.utils.dom');
goog.module.declareLegacyNamespace();
goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.userAgent');
const Svg = goog.require('Blockly.utils.Svg');
const userAgent = goog.require('Blockly.utils.userAgent');
/**
* Required name space for SVG elements.
* @const
*/
Blockly.utils.dom.SVG_NS = 'http://www.w3.org/2000/svg';
const SVG_NS = 'http://www.w3.org/2000/svg';
/**
* Required name space for HTML elements.
* @const
*/
Blockly.utils.dom.HTML_NS = 'http://www.w3.org/1999/xhtml';
const HTML_NS = 'http://www.w3.org/1999/xhtml';
/**
* Required name space for XLINK elements.
* @const
*/
Blockly.utils.dom.XLINK_NS = 'http://www.w3.org/1999/xlink';
const XLINK_NS = 'http://www.w3.org/1999/xlink';
/**
* Node type constants.
* https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
* @enum {number}
*/
Blockly.utils.dom.NodeType = {
const NodeType = {
ELEMENT_NODE: 1,
TEXT_NODE: 3,
COMMENT_NODE: 8,
@@ -57,36 +58,35 @@ Blockly.utils.dom.NodeType = {
* @type {Object}
* @private
*/
Blockly.utils.dom.cacheWidths_ = null;
let cacheWidths = null;
/**
* Number of current references to cache.
* @type {number}
* @private
*/
Blockly.utils.dom.cacheReference_ = 0;
let cacheReference = 0;
/**
* A HTML canvas context used for computing text width.
* @type {CanvasRenderingContext2D}
* @private
*/
Blockly.utils.dom.canvasContext_ = null;
let canvasContext = null;
/**
* Helper method for creating SVG elements.
* @param {string|Blockly.utils.Svg<T>} name Element's tag name.
* @param {string|Svg<T>} name Element's tag name.
* @param {!Object} attrs Dictionary of attribute names and values.
* @param {Element=} opt_parent Optional parent on which to append the element.
* @return {T} Newly created SVG element. The return type is {!SVGElement} if
* name is a string or a more specific type if it a member of
* Blockly.utils.Svg
* name is a string or a more specific type if it a member of Svg.
* @template T
*/
Blockly.utils.dom.createSvgElement = function(name, attrs, opt_parent) {
var e = /** @type {T} */
(document.createElementNS(Blockly.utils.dom.SVG_NS, String(name)));
for (var key in attrs) {
const createSvgElement = function(name, attrs, opt_parent) {
const e = /** @type {T} */
(document.createElementNS(SVG_NS, String(name)));
for (const key in attrs) {
e.setAttribute(key, attrs[key]);
}
// IE defines a unique attribute "runtimeStyle", it is NOT applied to
@@ -108,8 +108,8 @@ Blockly.utils.dom.createSvgElement = function(name, attrs, opt_parent) {
* @param {string} className Name of class to add.
* @return {boolean} True if class was added, false if already present.
*/
Blockly.utils.dom.addClass = function(element, className) {
var classes = element.getAttribute('class') || '';
const addClass = function(element, className) {
let classes = element.getAttribute('class') || '';
if ((' ' + classes + ' ').indexOf(' ' + className + ' ') != -1) {
return false;
}
@@ -126,11 +126,10 @@ Blockly.utils.dom.addClass = function(element, className) {
* @param {string} classNames A string of one or multiple class names for an
* element.
*/
Blockly.utils.dom.removeClasses = function(element, classNames) {
var classList = classNames.split(' ');
for (var i = 0; i < classList.length; i++) {
var cssName = classList[i];
Blockly.utils.dom.removeClass(element, cssName);
const removeClasses = function(element, classNames) {
const classList = classNames.split(' ');
for (let i = 0; i < classList.length; i++) {
removeClass(element, classList[i]);
}
};
@@ -141,13 +140,13 @@ Blockly.utils.dom.removeClasses = function(element, classNames) {
* @param {string} className Name of class to remove.
* @return {boolean} True if class was removed, false if never present.
*/
Blockly.utils.dom.removeClass = function(element, className) {
var classes = element.getAttribute('class');
const removeClass = function(element, className) {
const classes = element.getAttribute('class');
if ((' ' + classes + ' ').indexOf(' ' + className + ' ') == -1) {
return false;
}
var classList = classes.split(/\s+/);
for (var i = 0; i < classList.length; i++) {
const classList = classes.split(/\s+/);
for (let i = 0; i < classList.length; i++) {
if (!classList[i] || classList[i] == className) {
classList.splice(i, 1);
i--;
@@ -168,8 +167,8 @@ Blockly.utils.dom.removeClass = function(element, className) {
* @param {string} className Name of class to check.
* @return {boolean} True if class exists, false otherwise.
*/
Blockly.utils.dom.hasClass = function(element, className) {
var classes = element.getAttribute('class');
const hasClass = function(element, className) {
const classes = element.getAttribute('class');
return (' ' + classes + ' ').indexOf(' ' + className + ' ') != -1;
};
@@ -179,7 +178,7 @@ Blockly.utils.dom.hasClass = function(element, className) {
* @return {?Node} The node removed if removed; else, null.
*/
// Copied from Closure goog.dom.removeNode
Blockly.utils.dom.removeNode = function(node) {
const removeNode = function(node) {
return node && node.parentNode ? node.parentNode.removeChild(node) : null;
};
@@ -189,9 +188,9 @@ Blockly.utils.dom.removeNode = function(node) {
* @param {!Element} newNode New element to insert.
* @param {!Element} refNode Existing element to precede new node.
*/
Blockly.utils.dom.insertAfter = function(newNode, refNode) {
var siblingNode = refNode.nextSibling;
var parentNode = refNode.parentNode;
const insertAfter = function(newNode, refNode) {
const siblingNode = refNode.nextSibling;
const parentNode = refNode.parentNode;
if (!parentNode) {
throw Error('Reference node has no parent.');
}
@@ -208,9 +207,10 @@ Blockly.utils.dom.insertAfter = function(newNode, refNode) {
* @param {!Node} descendant The node to test presence of.
* @return {boolean} Whether the parent node contains the descendant node.
*/
Blockly.utils.dom.containsNode = function(parent, descendant) {
return !!(parent.compareDocumentPosition(descendant) &
Blockly.utils.dom.NodeType.DOCUMENT_POSITION_CONTAINED_BY);
const containsNode = function(parent, descendant) {
return !!(
parent.compareDocumentPosition(descendant) &
NodeType.DOCUMENT_POSITION_CONTAINED_BY);
};
/**
@@ -220,7 +220,7 @@ Blockly.utils.dom.containsNode = function(parent, descendant) {
* @param {!Element} element Element to which the CSS transform will be applied.
* @param {string} transform The value of the CSS `transform` property.
*/
Blockly.utils.dom.setCssTransform = function(element, transform) {
const setCssTransform = function(element, transform) {
element.style['transform'] = transform;
element.style['-webkit-transform'] = transform;
};
@@ -229,10 +229,10 @@ Blockly.utils.dom.setCssTransform = function(element, transform) {
* Start caching text widths. Every call to this function MUST also call
* stopTextWidthCache. Caches must not survive between execution threads.
*/
Blockly.utils.dom.startTextWidthCache = function() {
Blockly.utils.dom.cacheReference_++;
if (!Blockly.utils.dom.cacheWidths_) {
Blockly.utils.dom.cacheWidths_ = Object.create(null);
const startTextWidthCache = function() {
cacheReference++;
if (!cacheWidths) {
cacheWidths = Object.create(null);
}
};
@@ -240,10 +240,10 @@ Blockly.utils.dom.startTextWidthCache = function() {
* Stop caching field widths. Unless caching was already on when the
* corresponding call to startTextWidthCache was made.
*/
Blockly.utils.dom.stopTextWidthCache = function() {
Blockly.utils.dom.cacheReference_--;
if (!Blockly.utils.dom.cacheReference_) {
Blockly.utils.dom.cacheWidths_ = null;
const stopTextWidthCache = function() {
cacheReference--;
if (!cacheReference) {
cacheWidths = null;
}
};
@@ -252,13 +252,13 @@ Blockly.utils.dom.stopTextWidthCache = function() {
* @param {!Element} textElement An SVG 'text' element.
* @return {number} Width of element.
*/
Blockly.utils.dom.getTextWidth = function(textElement) {
var key = textElement.textContent + '\n' + textElement.className.baseVal;
var width;
const getTextWidth = function(textElement) {
const key = textElement.textContent + '\n' + textElement.className.baseVal;
let width;
// Return the cached width if it exists.
if (Blockly.utils.dom.cacheWidths_) {
width = Blockly.utils.dom.cacheWidths_[key];
if (cacheWidths) {
width = cacheWidths[key];
if (width) {
return width;
}
@@ -266,7 +266,7 @@ Blockly.utils.dom.getTextWidth = function(textElement) {
// Attempt to compute fetch the width of the SVG text element.
try {
if (Blockly.utils.userAgent.IE || Blockly.utils.userAgent.EDGE) {
if (userAgent.IE || userAgent.EDGE) {
width = textElement.getBBox().width;
} else {
width = textElement.getComputedTextLength();
@@ -280,8 +280,8 @@ Blockly.utils.dom.getTextWidth = function(textElement) {
}
// Cache the computed width and return.
if (Blockly.utils.dom.cacheWidths_) {
Blockly.utils.dom.cacheWidths_[key] = width;
if (cacheWidths) {
cacheWidths[key] = width;
}
return width;
};
@@ -296,10 +296,10 @@ Blockly.utils.dom.getTextWidth = function(textElement) {
* @param {string} fontFamily The font family to use.
* @return {number} Width of element.
*/
Blockly.utils.dom.getFastTextWidth = function(textElement,
fontSize, fontWeight, fontFamily) {
return Blockly.utils.dom.getFastTextWidthWithSizeString(textElement,
fontSize + 'pt', fontWeight, fontFamily);
const getFastTextWidth = function(
textElement, fontSize, fontWeight, fontFamily) {
return getFastTextWidthWithSizeString(
textElement, fontSize + 'pt', fontWeight, fontFamily);
};
/**
@@ -314,41 +314,40 @@ Blockly.utils.dom.getFastTextWidth = function(textElement,
* @param {string} fontFamily The font family to use.
* @return {number} Width of element.
*/
Blockly.utils.dom.getFastTextWidthWithSizeString = function(textElement,
fontSize, fontWeight, fontFamily) {
var text = textElement.textContent;
var key = text + '\n' + textElement.className.baseVal;
var width;
const getFastTextWidthWithSizeString = function(
textElement, fontSize, fontWeight, fontFamily) {
const text = textElement.textContent;
const key = text + '\n' + textElement.className.baseVal;
let width;
// Return the cached width if it exists.
if (Blockly.utils.dom.cacheWidths_) {
width = Blockly.utils.dom.cacheWidths_[key];
if (cacheWidths) {
width = cacheWidths[key];
if (width) {
return width;
}
}
if (!Blockly.utils.dom.canvasContext_) {
if (!canvasContext) {
// Inject the canvas element used for computing text widths.
var computeCanvas = document.createElement('canvas');
const computeCanvas = document.createElement('canvas');
computeCanvas.className = 'blocklyComputeCanvas';
document.body.appendChild(computeCanvas);
// Initialize the HTML canvas context and set the font.
// The context font must match blocklyText's fontsize and font-family
// set in CSS.
Blockly.utils.dom.canvasContext_ = computeCanvas.getContext('2d');
canvasContext = computeCanvas.getContext('2d');
}
// Set the desired font size and family.
Blockly.utils.dom.canvasContext_.font =
fontWeight + ' ' + fontSize + ' ' + fontFamily;
canvasContext.font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
// Measure the text width using the helper canvas context.
width = Blockly.utils.dom.canvasContext_.measureText(text).width;
width = canvasContext.measureText(text).width;
// Cache the computed width and return.
if (Blockly.utils.dom.cacheWidths_) {
Blockly.utils.dom.cacheWidths_[key] = width;
if (cacheWidths) {
cacheWidths[key] = width;
}
return width;
};
@@ -361,18 +360,16 @@ Blockly.utils.dom.getFastTextWidthWithSizeString = function(textElement,
* @param {string} fontFamily The font family to use.
* @return {{height: number, baseline: number}} Font measurements.
*/
Blockly.utils.dom.measureFontMetrics = function(text, fontSize, fontWeight,
fontFamily) {
var span = document.createElement('span');
const measureFontMetrics = function(text, fontSize, fontWeight, fontFamily) {
const span = document.createElement('span');
span.style.font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
span.textContent = text;
var block = document.createElement('div');
const block = document.createElement('div');
block.style.width = '1px';
block.style.height = 0;
var div = document.createElement('div');
const div = document.createElement('div');
div.setAttribute('style', 'position: fixed; top: 0; left: 0; display: flex;');
div.appendChild(span);
div.appendChild(block);
@@ -389,3 +386,25 @@ Blockly.utils.dom.measureFontMetrics = function(text, fontSize, fontWeight,
}
return result;
};
exports = {
SVG_NS,
HTML_NS,
XLINK_NS,
NodeType,
createSvgElement,
addClass,
removeClasses,
removeClass,
hasClass,
removeNode,
insertAfter,
containsNode,
setCssTransform,
startTextWidthCache,
stopTextWidthCache,
getTextWidth,
getFastTextWidth,
getFastTextWidthWithSizeString,
measureFontMetrics,
};

View File

@@ -13,10 +13,11 @@
'use strict';
/**
* @name Blockly.utils.Size
* @name Size
* @namespace
*/
goog.provide('Blockly.utils.Size');
goog.module('Blockly.utils.Size');
goog.module.declareLegacyNamespace();
/**
@@ -26,7 +27,7 @@ goog.provide('Blockly.utils.Size');
* @struct
* @constructor
*/
Blockly.utils.Size = function(width, height) {
const Size = function(width, height) {
/**
* Width
* @type {number}
@@ -42,12 +43,12 @@ Blockly.utils.Size = function(width, height) {
/**
* Compares sizes for equality.
* @param {?Blockly.utils.Size} a A Size.
* @param {?Blockly.utils.Size} b A Size.
* @param {?Size} a A Size.
* @param {?Size} b A Size.
* @return {boolean} True iff the sizes have equal widths and equal
* heights, or if both are null.
*/
Blockly.utils.Size.equals = function(a, b) {
Size.equals = function(a, b) {
if (a == b) {
return true;
}
@@ -56,3 +57,5 @@ Blockly.utils.Size.equals = function(a, b) {
}
return a.width == b.width && a.height == b.height;
};
exports = Size;

View File

@@ -10,51 +10,53 @@
*/
'use strict';
goog.provide('Blockly.Warning');
goog.module('Blockly.Warning');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Bubble');
goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Bubble = goog.require('Blockly.Bubble');
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
const Icon = goog.require('Blockly.Icon');
const Svg = goog.require('Blockly.utils.Svg');
const dom = goog.require('Blockly.utils.dom');
const object = goog.require('Blockly.utils.object');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BubbleOpen');
goog.require('Blockly.Icon');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Svg');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.utils.Coordinate');
/**
* Class for a warning.
* @param {!Blockly.Block} block The block associated with this warning.
* @extends {Blockly.Icon}
* @param {!Block} block The block associated with this warning.
* @extends {Icon}
* @constructor
*/
Blockly.Warning = function(block) {
Blockly.Warning.superClass_.constructor.call(this, block);
const Warning = function(block) {
Warning.superClass_.constructor.call(this, block);
this.createIcon();
// The text_ object can contain multiple warnings.
this.text_ = Object.create(null);
};
Blockly.utils.object.inherits(Blockly.Warning, Blockly.Icon);
object.inherits(Warning, Icon);
/**
* Does this icon get hidden when the block is collapsed.
*/
Blockly.Warning.prototype.collapseHidden = false;
Warning.prototype.collapseHidden = false;
/**
* Draw the warning icon.
* @param {!Element} group The icon group.
* @protected
*/
Blockly.Warning.prototype.drawIcon_ = function(group) {
Warning.prototype.drawIcon_ = function(group) {
// Triangle with rounded corners.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATH,
{
dom.createSvgElement(
Svg.PATH, {
'class': 'blocklyIconShape',
'd': 'M2,15Q-1,15 0.5,12L6.5,1.7Q8,-1 9.5,1.7L15.5,12Q17,15 14,15z'
},
@@ -62,19 +64,20 @@ Blockly.Warning.prototype.drawIcon_ = function(group) {
// Can't use a real '!' text character since different browsers and operating
// systems render it differently.
// Body of exclamation point.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATH,
{
dom.createSvgElement(
Svg.PATH, {
'class': 'blocklyIconSymbol',
'd': 'm7,4.8v3.16l0.27,2.27h1.46l0.27,-2.27v-3.16z'
},
group);
// Dot of exclamation point.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
dom.createSvgElement(
Svg.RECT, {
'class': 'blocklyIconSymbol',
'x': '7', 'y': '11', 'height': '2', 'width': '2'
'x': '7',
'y': '11',
'height': '2',
'width': '2'
},
group);
};
@@ -83,12 +86,12 @@ Blockly.Warning.prototype.drawIcon_ = function(group) {
* Show or hide the warning bubble.
* @param {boolean} visible True if the bubble should be visible.
*/
Blockly.Warning.prototype.setVisible = function(visible) {
Warning.prototype.setVisible = function(visible) {
if (visible == this.isVisible()) {
return;
}
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.BUBBLE_OPEN))(
this.block_, visible, 'warning'));
Events.fire(
new (Events.get(Events.BUBBLE_OPEN))(this.block_, visible, 'warning'));
if (visible) {
this.createBubble_();
} else {
@@ -100,11 +103,11 @@ Blockly.Warning.prototype.setVisible = function(visible) {
* Show the bubble.
* @private
*/
Blockly.Warning.prototype.createBubble_ = function() {
this.paragraphElement_ = Blockly.Bubble.textToDom(this.getText());
this.bubble_ = Blockly.Bubble.createNonEditableBubble(
this.paragraphElement_, /** @type {!Blockly.BlockSvg} */ (this.block_),
/** @type {!Blockly.utils.Coordinate} */ (this.iconXY_));
Warning.prototype.createBubble_ = function() {
this.paragraphElement_ = Bubble.textToDom(this.getText());
this.bubble_ = Bubble.createNonEditableBubble(
this.paragraphElement_, /** @type {!BlockSvg} */ (this.block_),
/** @type {!Coordinate} */ (this.iconXY_));
this.applyColour();
};
@@ -112,7 +115,7 @@ Blockly.Warning.prototype.createBubble_ = function() {
* Dispose of the bubble and references to it.
* @private
*/
Blockly.Warning.prototype.disposeBubble_ = function() {
Warning.prototype.disposeBubble_ = function() {
this.bubble_.dispose();
this.bubble_ = null;
this.paragraphElement_ = null;
@@ -125,7 +128,7 @@ Blockly.Warning.prototype.disposeBubble_ = function() {
* @param {string} id An ID for this text entry to be able to maintain
* multiple warnings.
*/
Blockly.Warning.prototype.setText = function(text, id) {
Warning.prototype.setText = function(text, id) {
if (this.text_[id] == text) {
return;
}
@@ -144,9 +147,9 @@ Blockly.Warning.prototype.setText = function(text, id) {
* Get this warning's texts.
* @return {string} All texts concatenated into one string.
*/
Blockly.Warning.prototype.getText = function() {
var allWarnings = [];
for (var id in this.text_) {
Warning.prototype.getText = function() {
const allWarnings = [];
for (const id in this.text_) {
allWarnings.push(this.text_[id]);
}
return allWarnings.join('\n');
@@ -155,7 +158,9 @@ Blockly.Warning.prototype.getText = function() {
/**
* Dispose of this warning.
*/
Blockly.Warning.prototype.dispose = function() {
Warning.prototype.dispose = function() {
this.block_.warning = null;
Blockly.Icon.prototype.dispose.call(this);
Icon.prototype.dispose.call(this);
};
exports = Warning;

View File

@@ -11,28 +11,27 @@
*/
'use strict';
goog.provide('Blockly.WorkspaceAudio');
goog.module('Blockly.WorkspaceAudio');
goog.module.declareLegacyNamespace();
goog.require('Blockly.internalConstants');
goog.require('Blockly.utils');
goog.require('Blockly.utils.global');
goog.require('Blockly.utils.userAgent');
goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const global = goog.require('Blockly.utils.global');
const internalConstants = goog.require('Blockly.internalConstants');
const userAgent = goog.require('Blockly.utils.userAgent');
/**
* Class for loading, storing, and playing audio for a workspace.
* @param {Blockly.WorkspaceSvg} parentWorkspace The parent of the workspace
* @param {WorkspaceSvg} parentWorkspace The parent of the workspace
* this audio object belongs to, or null.
* @constructor
*/
Blockly.WorkspaceAudio = function(parentWorkspace) {
const WorkspaceAudio = function(parentWorkspace) {
/**
* The parent of the workspace this object belongs to, or null. May be
* checked for sounds that this object can't find.
* @type {Blockly.WorkspaceSvg}
* @type {WorkspaceSvg}
* @private
*/
this.parentWorkspace_ = parentWorkspace;
@@ -49,13 +48,13 @@ Blockly.WorkspaceAudio = function(parentWorkspace) {
* @type {Date}
* @private
*/
Blockly.WorkspaceAudio.prototype.lastSound_ = null;
WorkspaceAudio.prototype.lastSound_ = null;
/**
* Dispose of this audio manager.
* @package
*/
Blockly.WorkspaceAudio.prototype.dispose = function() {
WorkspaceAudio.prototype.dispose = function() {
this.parentWorkspace_ = null;
this.SOUNDS_ = null;
};
@@ -67,24 +66,25 @@ Blockly.WorkspaceAudio.prototype.dispose = function() {
* Filenames include path from Blockly's root. File extensions matter.
* @param {string} name Name of sound.
*/
Blockly.WorkspaceAudio.prototype.load = function(filenames, name) {
WorkspaceAudio.prototype.load = function(filenames, name) {
if (!filenames.length) {
return;
}
let audioTest;
try {
var audioTest = new Blockly.utils.global['Audio']();
audioTest = new global['Audio']();
} catch (e) {
// No browser support for Audio.
// IE can throw an error even if the Audio object exists.
return;
}
var sound;
for (var i = 0; i < filenames.length; i++) {
var filename = filenames[i];
var ext = filename.match(/\.(\w+)$/);
let sound;
for (let i = 0; i < filenames.length; i++) {
const filename = filenames[i];
const ext = filename.match(/\.(\w+)$/);
if (ext && audioTest.canPlayType('audio/' + ext[1])) {
// Found an audio format we can play.
sound = new Blockly.utils.global['Audio'](filename);
sound = new global['Audio'](filename);
break;
}
}
@@ -97,16 +97,17 @@ Blockly.WorkspaceAudio.prototype.load = function(filenames, name) {
* Preload all the audio files so that they play quickly when asked for.
* @package
*/
Blockly.WorkspaceAudio.prototype.preload = function() {
for (var name in this.SOUNDS_) {
var sound = this.SOUNDS_[name];
WorkspaceAudio.prototype.preload = function() {
for (const name in this.SOUNDS_) {
const sound = this.SOUNDS_[name];
sound.volume = 0.01;
var playPromise = sound.play();
const playPromise = sound.play();
// Edge does not return a promise, so we need to check.
if (playPromise !== undefined) {
// If we don't wait for the play request to complete before calling pause()
// we will get an exception: (DOMException: The play() request was interrupted)
// See more: https://developers.google.com/web/updates/2017/06/play-request-was-interrupted
// If we don't wait for the play request to complete before calling
// pause() we will get an exception: (DOMException: The play() request was
// interrupted) See more:
// https://developers.google.com/web/updates/2017/06/play-request-was-interrupted
playPromise.then(sound.pause).catch(function() {
// Play without user interaction was prevented.
});
@@ -116,7 +117,7 @@ Blockly.WorkspaceAudio.prototype.preload = function() {
// iOS can only process one sound at a time. Trying to load more than one
// corrupts the earlier ones. Just load one and leave the others uncached.
if (Blockly.utils.userAgent.IPAD || Blockly.utils.userAgent.IPHONE) {
if (userAgent.IPAD || userAgent.IPHONE) {
break;
}
}
@@ -128,18 +129,18 @@ Blockly.WorkspaceAudio.prototype.preload = function() {
* @param {string} name Name of sound.
* @param {number=} opt_volume Volume of sound (0-1).
*/
Blockly.WorkspaceAudio.prototype.play = function(name, opt_volume) {
var sound = this.SOUNDS_[name];
WorkspaceAudio.prototype.play = function(name, opt_volume) {
const sound = this.SOUNDS_[name];
if (sound) {
// Don't play one sound on top of another.
var now = new Date;
const now = new Date;
if (this.lastSound_ != null &&
now - this.lastSound_ < Blockly.internalConstants.SOUND_LIMIT) {
now - this.lastSound_ < internalConstants.SOUND_LIMIT) {
return;
}
this.lastSound_ = now;
var mySound;
if (Blockly.utils.userAgent.IPAD || Blockly.utils.userAgent.ANDROID) {
let mySound;
if (userAgent.IPAD || userAgent.ANDROID) {
// Creating a new audio node causes lag in Android and iPad. Android
// refetches the file from the server, iPad uses a singleton audio
// node which must be deleted and recreated for each new audio tag.
@@ -154,3 +155,5 @@ Blockly.WorkspaceAudio.prototype.play = function(name, opt_volume) {
this.parentWorkspace_.getAudioManager().play(name, opt_volume);
}
};
exports = WorkspaceAudio;

View File

@@ -10,9 +10,15 @@
*/
'use strict';
goog.provide('Blockly.WorkspaceComment');
goog.module('Blockly.WorkspaceComment');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const utils = goog.require('Blockly.utils');
const xml = goog.require('Blockly.utils.xml');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.CommentChange');
/** @suppress {extraRequire} */
@@ -21,14 +27,11 @@ goog.require('Blockly.Events.CommentCreate');
goog.require('Blockly.Events.CommentDelete');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.CommentMove');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.xml');
/**
* Class for a workspace comment.
* @param {!Blockly.Workspace} workspace The block's workspace.
* @param {!Workspace} workspace The block's workspace.
* @param {string} content The content of this workspace comment.
* @param {number} height Height of the comment.
* @param {number} width Width of the comment.
@@ -36,20 +39,20 @@ goog.require('Blockly.utils.xml');
* create a new ID.
* @constructor
*/
Blockly.WorkspaceComment = function(workspace, content, height, width, opt_id) {
const WorkspaceComment = function(workspace, content, height, width, opt_id) {
/** @type {string} */
this.id = (opt_id && !workspace.getCommentById(opt_id)) ?
opt_id : Blockly.utils.genUid();
this.id =
(opt_id && !workspace.getCommentById(opt_id)) ? opt_id : utils.genUid();
workspace.addTopComment(this);
/**
* The comment's position in workspace units. (0, 0) is at the workspace's
* origin; scale does not change this value.
* @type {!Blockly.utils.Coordinate}
* @type {!Coordinate}
* @protected
*/
this.xy_ = new Blockly.utils.Coordinate(0, 0);
this.xy_ = new Coordinate(0, 0);
/**
* The comment's height in workspace units. Scale does not change this value.
@@ -66,7 +69,7 @@ Blockly.WorkspaceComment = function(workspace, content, height, width, opt_id) {
this.width_ = width;
/**
* @type {!Blockly.Workspace}
* @type {!Workspace}
*/
this.workspace = workspace;
@@ -106,22 +109,21 @@ Blockly.WorkspaceComment = function(workspace, content, height, width, opt_id) {
*/
this.isComment = true;
Blockly.WorkspaceComment.fireCreateEvent(this);
WorkspaceComment.fireCreateEvent(this);
};
/**
* Dispose of this comment.
* @package
*/
Blockly.WorkspaceComment.prototype.dispose = function() {
WorkspaceComment.prototype.dispose = function() {
if (!this.workspace) {
// The comment has already been deleted.
return;
}
if (Blockly.Events.isEnabled()) {
Blockly.Events.fire(
new (Blockly.Events.get(Blockly.Events.COMMENT_DELETE))(this));
if (Events.isEnabled()) {
Events.fire(new (Events.get(Events.COMMENT_DELETE))(this));
}
// Remove from the list of top comments and the comment database.
@@ -137,7 +139,7 @@ Blockly.WorkspaceComment.prototype.dispose = function() {
* @return {number} Comment height.
* @package
*/
Blockly.WorkspaceComment.prototype.getHeight = function() {
WorkspaceComment.prototype.getHeight = function() {
return this.height_;
};
@@ -146,7 +148,7 @@ Blockly.WorkspaceComment.prototype.getHeight = function() {
* @param {number} height Comment height.
* @package
*/
Blockly.WorkspaceComment.prototype.setHeight = function(height) {
WorkspaceComment.prototype.setHeight = function(height) {
this.height_ = height;
};
@@ -155,7 +157,7 @@ Blockly.WorkspaceComment.prototype.setHeight = function(height) {
* @return {number} Comment width.
* @package
*/
Blockly.WorkspaceComment.prototype.getWidth = function() {
WorkspaceComment.prototype.getWidth = function() {
return this.width_;
};
@@ -164,18 +166,18 @@ Blockly.WorkspaceComment.prototype.getWidth = function() {
* @param {number} width comment width.
* @package
*/
Blockly.WorkspaceComment.prototype.setWidth = function(width) {
WorkspaceComment.prototype.setWidth = function(width) {
this.width_ = width;
};
/**
* Get stored location.
* @return {!Blockly.utils.Coordinate} The comment's stored location.
* @return {!Coordinate} The comment's stored location.
* This is not valid if the comment is currently being dragged.
* @package
*/
Blockly.WorkspaceComment.prototype.getXY = function() {
return new Blockly.utils.Coordinate(this.xy_.x, this.xy_.y);
WorkspaceComment.prototype.getXY = function() {
return new Coordinate(this.xy_.x, this.xy_.y);
};
/**
@@ -184,11 +186,11 @@ Blockly.WorkspaceComment.prototype.getXY = function() {
* @param {number} dy Vertical offset, in workspace units.
* @package
*/
Blockly.WorkspaceComment.prototype.moveBy = function(dx, dy) {
var event = new (Blockly.Events.get(Blockly.Events.COMMENT_MOVE))(this);
WorkspaceComment.prototype.moveBy = function(dx, dy) {
const event = new (Events.get(Events.COMMENT_MOVE))(this);
this.xy_.translate(dx, dy);
event.recordNew();
Blockly.Events.fire(event);
Events.fire(event);
};
/**
@@ -196,7 +198,7 @@ Blockly.WorkspaceComment.prototype.moveBy = function(dx, dy) {
* @return {boolean} True if deletable.
* @package
*/
Blockly.WorkspaceComment.prototype.isDeletable = function() {
WorkspaceComment.prototype.isDeletable = function() {
return this.deletable_ &&
!(this.workspace && this.workspace.options.readOnly);
};
@@ -206,7 +208,7 @@ Blockly.WorkspaceComment.prototype.isDeletable = function() {
* @param {boolean} deletable True if deletable.
* @package
*/
Blockly.WorkspaceComment.prototype.setDeletable = function(deletable) {
WorkspaceComment.prototype.setDeletable = function(deletable) {
this.deletable_ = deletable;
};
@@ -215,9 +217,8 @@ Blockly.WorkspaceComment.prototype.setDeletable = function(deletable) {
* @return {boolean} True if movable.
* @package
*/
Blockly.WorkspaceComment.prototype.isMovable = function() {
return this.movable_ &&
!(this.workspace && this.workspace.options.readOnly);
WorkspaceComment.prototype.isMovable = function() {
return this.movable_ && !(this.workspace && this.workspace.options.readOnly);
};
/**
@@ -225,7 +226,7 @@ Blockly.WorkspaceComment.prototype.isMovable = function() {
* @param {boolean} movable True if movable.
* @package
*/
Blockly.WorkspaceComment.prototype.setMovable = function(movable) {
WorkspaceComment.prototype.setMovable = function(movable) {
this.movable_ = movable;
};
@@ -233,16 +234,15 @@ Blockly.WorkspaceComment.prototype.setMovable = function(movable) {
* Get whether this comment is editable or not.
* @return {boolean} True if editable.
*/
Blockly.WorkspaceComment.prototype.isEditable = function() {
return this.editable_ &&
!(this.workspace && this.workspace.options.readOnly);
WorkspaceComment.prototype.isEditable = function() {
return this.editable_ && !(this.workspace && this.workspace.options.readOnly);
};
/**
* Set whether this comment is editable or not.
* @param {boolean} editable True if editable.
*/
Blockly.WorkspaceComment.prototype.setEditable = function(editable) {
WorkspaceComment.prototype.setEditable = function(editable) {
this.editable_ = editable;
};
@@ -251,7 +251,7 @@ Blockly.WorkspaceComment.prototype.setEditable = function(editable) {
* @return {string} Comment text.
* @package
*/
Blockly.WorkspaceComment.prototype.getContent = function() {
WorkspaceComment.prototype.getContent = function() {
return this.content_;
};
@@ -260,10 +260,10 @@ Blockly.WorkspaceComment.prototype.getContent = function() {
* @param {string} content Comment content.
* @package
*/
Blockly.WorkspaceComment.prototype.setContent = function(content) {
WorkspaceComment.prototype.setContent = function(content) {
if (this.content_ != content) {
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.COMMENT_CHANGE))(
this, this.content_, content));
Events.fire(
new (Events.get(Events.COMMENT_CHANGE))(this, this.content_, content));
this.content_ = content;
}
};
@@ -274,8 +274,8 @@ Blockly.WorkspaceComment.prototype.setContent = function(content) {
* @return {!Element} Tree of XML elements.
* @package
*/
Blockly.WorkspaceComment.prototype.toXmlWithXY = function(opt_noId) {
var element = this.toXml(opt_noId);
WorkspaceComment.prototype.toXmlWithXY = function(opt_noId) {
const element = this.toXml(opt_noId);
element.setAttribute('x', Math.round(this.xy_.x));
element.setAttribute('y', Math.round(this.xy_.y));
element.setAttribute('h', this.height_);
@@ -291,8 +291,8 @@ Blockly.WorkspaceComment.prototype.toXmlWithXY = function(opt_noId) {
* @return {!Element} Tree of XML elements.
* @package
*/
Blockly.WorkspaceComment.prototype.toXml = function(opt_noId) {
var commentElement = Blockly.utils.xml.createElement('comment');
WorkspaceComment.prototype.toXml = function(opt_noId) {
const commentElement = xml.createElement('comment');
if (!opt_noId) {
commentElement.id = this.id;
}
@@ -302,21 +302,20 @@ Blockly.WorkspaceComment.prototype.toXml = function(opt_noId) {
/**
* Fire a create event for the given workspace comment, if comments are enabled.
* @param {!Blockly.WorkspaceComment} comment The comment that was just created.
* @param {!WorkspaceComment} comment The comment that was just created.
* @package
*/
Blockly.WorkspaceComment.fireCreateEvent = function(comment) {
if (Blockly.Events.isEnabled()) {
var existingGroup = Blockly.Events.getGroup();
WorkspaceComment.fireCreateEvent = function(comment) {
if (Events.isEnabled()) {
const existingGroup = Events.getGroup();
if (!existingGroup) {
Blockly.Events.setGroup(true);
Events.setGroup(true);
}
try {
Blockly.Events.fire(
new (Blockly.Events.get(Blockly.Events.COMMENT_CREATE))(comment));
Events.fire(new (Events.get(Events.COMMENT_CREATE))(comment));
} finally {
if (!existingGroup) {
Blockly.Events.setGroup(false);
Events.setGroup(false);
}
}
}
@@ -325,23 +324,23 @@ Blockly.WorkspaceComment.fireCreateEvent = function(comment) {
/**
* Decode an XML comment tag and create a comment on the workspace.
* @param {!Element} xmlComment XML comment element.
* @param {!Blockly.Workspace} workspace The workspace.
* @return {!Blockly.WorkspaceComment} The created workspace comment.
* @param {!Workspace} workspace The workspace.
* @return {!WorkspaceComment} The created workspace comment.
* @package
*/
Blockly.WorkspaceComment.fromXml = function(xmlComment, workspace) {
var info = Blockly.WorkspaceComment.parseAttributes(xmlComment);
WorkspaceComment.fromXml = function(xmlComment, workspace) {
const info = WorkspaceComment.parseAttributes(xmlComment);
var comment = new Blockly.WorkspaceComment(
workspace, info.content, info.h, info.w, info.id);
const comment =
new WorkspaceComment(workspace, info.content, info.h, info.w, info.id);
var commentX = parseInt(xmlComment.getAttribute('x'), 10);
var commentY = parseInt(xmlComment.getAttribute('y'), 10);
const commentX = parseInt(xmlComment.getAttribute('x'), 10);
const commentY = parseInt(xmlComment.getAttribute('y'), 10);
if (!isNaN(commentX) && !isNaN(commentY)) {
comment.moveBy(commentX, commentY);
}
Blockly.WorkspaceComment.fireCreateEvent(comment);
WorkspaceComment.fireCreateEvent(comment);
return comment;
};
@@ -352,9 +351,9 @@ Blockly.WorkspaceComment.fromXml = function(xmlComment, workspace) {
* object containing the id, size, position, and comment string.
* @package
*/
Blockly.WorkspaceComment.parseAttributes = function(xml) {
var xmlH = xml.getAttribute('h');
var xmlW = xml.getAttribute('w');
WorkspaceComment.parseAttributes = function(xml) {
const xmlH = xml.getAttribute('h');
const xmlW = xml.getAttribute('w');
return {
// @type {string}
@@ -377,3 +376,5 @@ Blockly.WorkspaceComment.parseAttributes = function(xml) {
content: xml.textContent
};
};
exports = WorkspaceComment;

View File

@@ -1,463 +0,0 @@
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Methods for rendering a workspace comment as SVG
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.provide('Blockly.WorkspaceCommentSvg.render');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Svg');
/**
* Size of the resize icon.
* @type {number}
* @const
* @private
*/
Blockly.WorkspaceCommentSvg.RESIZE_SIZE = 8;
/**
* Radius of the border around the comment.
* @type {number}
* @const
* @private
*/
Blockly.WorkspaceCommentSvg.BORDER_RADIUS = 3;
/**
* Offset from the foreignobject edge to the textarea edge.
* @type {number}
* @const
* @private
*/
Blockly.WorkspaceCommentSvg.TEXTAREA_OFFSET = 2;
/**
* Offset from the top to make room for a top bar.
* @type {number}
* @const
* @private
*/
Blockly.WorkspaceCommentSvg.TOP_OFFSET = 10;
/**
* Returns a bounding box describing the dimensions of this comment.
* @return {!{height: number, width: number}} Object with height and width
* properties in workspace units.
* @package
*/
Blockly.WorkspaceCommentSvg.prototype.getHeightWidth = function() {
return { width: this.getWidth(), height: this.getHeight() };
};
/**
* Renders the workspace comment.
* @package
*/
Blockly.WorkspaceCommentSvg.prototype.render = function() {
if (this.rendered_) {
return;
}
var size = this.getHeightWidth();
// Add text area
this.createEditor_();
this.svgGroup_.appendChild(this.foreignObject_);
this.svgHandleTarget_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
'class': 'blocklyCommentHandleTarget',
'x': 0,
'y': 0
});
this.svgGroup_.appendChild(this.svgHandleTarget_);
this.svgRectTarget_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
'class': 'blocklyCommentTarget',
'x': 0,
'y': 0,
'rx': Blockly.WorkspaceCommentSvg.BORDER_RADIUS,
'ry': Blockly.WorkspaceCommentSvg.BORDER_RADIUS
});
this.svgGroup_.appendChild(this.svgRectTarget_);
// Add the resize icon
this.addResizeDom_();
if (this.isDeletable()) {
// Add the delete icon
this.addDeleteDom_();
}
this.setSize_(size.width, size.height);
// Set the content
this.textarea_.value = this.content_;
this.rendered_ = true;
if (this.resizeGroup_) {
Blockly.browserEvents.conditionalBind(
this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_);
}
if (this.isDeletable()) {
Blockly.browserEvents.conditionalBind(
this.deleteGroup_, 'mousedown', this, this.deleteMouseDown_);
Blockly.browserEvents.conditionalBind(
this.deleteGroup_, 'mouseout', this, this.deleteMouseOut_);
Blockly.browserEvents.conditionalBind(
this.deleteGroup_, 'mouseup', this, this.deleteMouseUp_);
}
};
/**
* Create the text area for the comment.
* @return {!Element} The top-level node of the editor.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.createEditor_ = function() {
/* Create the editor. Here's the markup that will be generated:
<foreignObject class="blocklyCommentForeignObject" x="0" y="10" width="164" height="164">
<body xmlns="http://www.w3.org/1999/xhtml" class="blocklyMinimalBody">
<textarea xmlns="http://www.w3.org/1999/xhtml"
class="blocklyCommentTextarea"
style="height: 164px; width: 164px;"></textarea>
</body>
</foreignObject>
*/
this.foreignObject_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FOREIGNOBJECT,
{
'x': 0,
'y': Blockly.WorkspaceCommentSvg.TOP_OFFSET,
'class': 'blocklyCommentForeignObject'
},
null);
var body = document.createElementNS(Blockly.utils.dom.HTML_NS, 'body');
body.setAttribute('xmlns', Blockly.utils.dom.HTML_NS);
body.className = 'blocklyMinimalBody';
var textarea = document.createElementNS(Blockly.utils.dom.HTML_NS, 'textarea');
textarea.className = 'blocklyCommentTextarea';
textarea.setAttribute('dir', this.RTL ? 'RTL' : 'LTR');
textarea.readOnly = !this.isEditable();
body.appendChild(textarea);
this.textarea_ = textarea;
this.foreignObject_.appendChild(body);
// Don't zoom with mousewheel.
Blockly.browserEvents.conditionalBind(textarea, 'wheel', this, function(e) {
e.stopPropagation();
});
Blockly.browserEvents.conditionalBind(
textarea, 'change', this,
function(
/* eslint-disable no-unused-vars */ e
/* eslint-enable no-unused-vars */) {
this.setContent(textarea.value);
});
return this.foreignObject_;
};
/**
* Add the resize icon to the DOM
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.addResizeDom_ = function() {
this.resizeGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G,
{
'class': this.RTL ? 'blocklyResizeSW' : 'blocklyResizeSE'
},
this.svgGroup_);
var resizeSize = Blockly.WorkspaceCommentSvg.RESIZE_SIZE;
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.POLYGON,
{'points': '0,x x,x x,0'.replace(/x/g, resizeSize.toString())},
this.resizeGroup_);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE,
{
'class': 'blocklyResizeLine',
'x1': resizeSize / 3, 'y1': resizeSize - 1,
'x2': resizeSize - 1, 'y2': resizeSize / 3
}, this.resizeGroup_);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE,
{
'class': 'blocklyResizeLine',
'x1': resizeSize * 2 / 3, 'y1': resizeSize - 1,
'x2': resizeSize - 1, 'y2': resizeSize * 2 / 3
}, this.resizeGroup_);
};
/**
* Add the delete icon to the DOM
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.addDeleteDom_ = function() {
this.deleteGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G,
{
'class': 'blocklyCommentDeleteIcon'
},
this.svgGroup_);
this.deleteIconBorder_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.CIRCLE,
{
'class': 'blocklyDeleteIconShape',
'r': '7',
'cx': '7.5',
'cy': '7.5'
},
this.deleteGroup_);
// x icon.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE,
{
'x1': '5', 'y1': '10',
'x2': '10', 'y2': '5',
'stroke': '#fff',
'stroke-width': '2'
},
this.deleteGroup_);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE,
{
'x1': '5', 'y1': '5',
'x2': '10', 'y2': '10',
'stroke': '#fff',
'stroke-width': '2'
},
this.deleteGroup_);
};
/**
* Handle a mouse-down on comment's resize corner.
* @param {!Event} e Mouse down event.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.resizeMouseDown_ = function(e) {
this.unbindDragEvents_();
if (Blockly.utils.isRightButton(e)) {
// No right-click.
e.stopPropagation();
return;
}
// Left-click (or middle click)
this.workspace.startDrag(e, new Blockly.utils.Coordinate(
this.workspace.RTL ? -this.width_ : this.width_, this.height_));
this.onMouseUpWrapper_ = Blockly.browserEvents.conditionalBind(
document, 'mouseup', this, this.resizeMouseUp_);
this.onMouseMoveWrapper_ = Blockly.browserEvents.conditionalBind(
document, 'mousemove', this, this.resizeMouseMove_);
Blockly.hideChaff();
// This event has been handled. No need to bubble up to the document.
e.stopPropagation();
};
/**
* Handle a mouse-down on comment's delete icon.
* @param {!Event} e Mouse down event.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.deleteMouseDown_ = function(e) {
// Highlight the delete icon.
Blockly.utils.dom.addClass(
/** @type {!Element} */ (this.deleteIconBorder_),
'blocklyDeleteIconHighlighted');
// This event has been handled. No need to bubble up to the document.
e.stopPropagation();
};
/**
* Handle a mouse-out on comment's delete icon.
* @param {!Event} _e Mouse out event.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.deleteMouseOut_ = function(_e) {
// Restore highlight on the delete icon.
Blockly.utils.dom.removeClass(
/** @type {!Element} */ (this.deleteIconBorder_),
'blocklyDeleteIconHighlighted');
};
/**
* Handle a mouse-up on comment's delete icon.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.deleteMouseUp_ = function(e) {
// Delete this comment.
this.dispose(true, true);
// This event has been handled. No need to bubble up to the document.
e.stopPropagation();
};
/**
* Stop binding to the global mouseup and mousemove events.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.unbindDragEvents_ = function() {
if (this.onMouseUpWrapper_) {
Blockly.browserEvents.unbind(this.onMouseUpWrapper_);
this.onMouseUpWrapper_ = null;
}
if (this.onMouseMoveWrapper_) {
Blockly.browserEvents.unbind(this.onMouseMoveWrapper_);
this.onMouseMoveWrapper_ = null;
}
};
/**
* Handle a mouse-up event while dragging a comment's border or resize handle.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.resizeMouseUp_ = function(/* e */) {
Blockly.Touch.clearTouchIdentifier();
this.unbindDragEvents_();
};
/**
* Resize this comment to follow the mouse.
* @param {!Event} e Mouse move event.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.resizeMouseMove_ = function(e) {
this.autoLayout_ = false;
var newXY = this.workspace.moveDrag(e);
this.setSize_(this.RTL ? -newXY.x : newXY.x, newXY.y);
};
/**
* Callback function triggered when the comment has resized.
* Resize the text area accordingly.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.resizeComment_ = function() {
var size = this.getHeightWidth();
var topOffset = Blockly.WorkspaceCommentSvg.TOP_OFFSET;
var textOffset = Blockly.WorkspaceCommentSvg.TEXTAREA_OFFSET * 2;
this.foreignObject_.setAttribute('width', size.width);
this.foreignObject_.setAttribute('height', size.height - topOffset);
if (this.RTL) {
this.foreignObject_.setAttribute('x', -size.width);
}
this.textarea_.style.width = (size.width - textOffset) + 'px';
this.textarea_.style.height = (size.height - textOffset - topOffset) + 'px';
};
/**
* Set size
* @param {number} width width of the container
* @param {number} height height of the container
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.setSize_ = function(width, height) {
// Minimum size of a comment.
width = Math.max(width, 45);
height = Math.max(height, 20 + Blockly.WorkspaceCommentSvg.TOP_OFFSET);
this.width_ = width;
this.height_ = height;
this.svgRect_.setAttribute('width', width);
this.svgRect_.setAttribute('height', height);
this.svgRectTarget_.setAttribute('width', width);
this.svgRectTarget_.setAttribute('height', height);
this.svgHandleTarget_.setAttribute('width', width);
this.svgHandleTarget_.setAttribute('height',
Blockly.WorkspaceCommentSvg.TOP_OFFSET);
if (this.RTL) {
this.svgRect_.setAttribute('transform', 'scale(-1 1)');
this.svgRectTarget_.setAttribute('transform', 'scale(-1 1)');
}
var resizeSize = Blockly.WorkspaceCommentSvg.RESIZE_SIZE;
if (this.resizeGroup_) {
if (this.RTL) {
// Mirror the resize group.
this.resizeGroup_.setAttribute('transform', 'translate(' +
(-width + resizeSize) + ',' + (height - resizeSize) + ') scale(-1 1)');
this.deleteGroup_.setAttribute('transform', 'translate(' +
(-width + resizeSize) + ',' + (-resizeSize) + ') scale(-1 1)');
} else {
this.resizeGroup_.setAttribute('transform', 'translate(' +
(width - resizeSize) + ',' +
(height - resizeSize) + ')');
this.deleteGroup_.setAttribute('transform', 'translate(' +
(width - resizeSize) + ',' +
(-resizeSize) + ')');
}
}
// Allow the contents to resize.
this.resizeComment_();
};
/**
* Dispose of any rendered comment components.
* @private
*/
Blockly.WorkspaceCommentSvg.prototype.disposeInternal_ = function() {
this.textarea_ = null;
this.foreignObject_ = null;
this.svgRectTarget_ = null;
this.svgHandleTarget_ = null;
this.disposed_ = true;
};
/**
* Set the focus on the text area.
* @package
*/
Blockly.WorkspaceCommentSvg.prototype.setFocus = function() {
var comment = this;
this.focused_ = true;
// Defer CSS changes.
setTimeout(function() {
if (comment.disposed_) {
return;
}
comment.textarea_.focus();
comment.addFocus();
Blockly.utils.dom.addClass(
comment.svgRectTarget_, 'blocklyCommentTargetFocused');
Blockly.utils.dom.addClass(
comment.svgHandleTarget_, 'blocklyCommentHandleTargetFocused');
}, 0);
};
/**
* Remove focus from the text area.
* @package
*/
Blockly.WorkspaceCommentSvg.prototype.blurFocus = function() {
var comment = this;
this.focused_ = false;
// Defer CSS changes.
setTimeout(function() {
if (comment.disposed_) {
return;
}
comment.textarea_.blur();
comment.removeFocus();
Blockly.utils.dom.removeClass(
comment.svgRectTarget_, 'blocklyCommentTargetFocused');
Blockly.utils.dom.removeClass(
comment.svgHandleTarget_, 'blocklyCommentHandleTargetFocused');
}, 0);
};

File diff suppressed because it is too large Load Diff

View File

@@ -10,35 +10,37 @@
*/
'use strict';
goog.provide('Blockly.ZoomControls');
goog.module('Blockly.ZoomControls');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.ComponentManager');
goog.require('Blockly.Css');
goog.require('Blockly.Events');
const ComponentManager = goog.require('Blockly.ComponentManager');
const Css = goog.require('Blockly.Css');
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const IPositionable = goog.require('Blockly.IPositionable');
const Rect = goog.require('Blockly.utils.Rect');
const Svg = goog.require('Blockly.utils.Svg');
const Touch = goog.require('Blockly.Touch');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const browserEvents = goog.require('Blockly.browserEvents');
const dom = goog.require('Blockly.utils.dom');
const internalConstants = goog.require('Blockly.internalConstants');
const uiPosition = goog.require('Blockly.uiPosition');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.Click');
goog.require('Blockly.IPositionable');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Touch');
goog.require('Blockly.uiPosition');
goog.require('Blockly.utils');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.Svg');
goog.requireType('Blockly.WorkspaceSvg');
/**
* Class for a zoom controls.
* @param {!Blockly.WorkspaceSvg} workspace The workspace to sit in.
* @param {!WorkspaceSvg} workspace The workspace to sit in.
* @constructor
* @implements {Blockly.IPositionable}
* @implements {IPositionable}
*/
Blockly.ZoomControls = function(workspace) {
const ZoomControls = function(workspace) {
/**
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @private
*/
this.workspace_ = workspace;
@@ -52,24 +54,24 @@ Blockly.ZoomControls = function(workspace) {
/**
* A handle to use to unbind the mouse down event handler for zoom reset
* button. Opaque data returned from Blockly.bindEventWithChecks_.
* @type {?Blockly.browserEvents.Data}
* button. Opaque data returned from browserEvents.conditionalBind.
* @type {?browserEvents.Data}
* @private
*/
this.onZoomResetWrapper_ = null;
/**
* A handle to use to unbind the mouse down event handler for zoom in button.
* Opaque data returned from Blockly.bindEventWithChecks_.
* @type {?Blockly.browserEvents.Data}
* Opaque data returned from browserEvents.conditionalBind.
* @type {?browserEvents.Data}
* @private
*/
this.onZoomInWrapper_ = null;
/**
* A handle to use to unbind the mouse down event handler for zoom out button.
* Opaque data returned from Blockly.bindEventWithChecks_.
* @type {?Blockly.browserEvents.Data}
* Opaque data returned from browserEvents.conditionalBind.
* @type {?browserEvents.Data}
* @private
*/
this.onZoomOutWrapper_ = null;
@@ -102,7 +104,7 @@ Blockly.ZoomControls = function(workspace) {
* @const
* @private
*/
Blockly.ZoomControls.prototype.WIDTH_ = 32;
ZoomControls.prototype.WIDTH_ = 32;
/**
* Height of each zoom control.
@@ -110,7 +112,7 @@ Blockly.ZoomControls.prototype.WIDTH_ = 32;
* @const
* @private
*/
Blockly.ZoomControls.prototype.HEIGHT_ = 32;
ZoomControls.prototype.HEIGHT_ = 32;
/**
* Small spacing used between the zoom in and out control, in pixels.
@@ -118,7 +120,7 @@ Blockly.ZoomControls.prototype.HEIGHT_ = 32;
* @const
* @private
*/
Blockly.ZoomControls.prototype.SMALL_SPACING_ = 2;
ZoomControls.prototype.SMALL_SPACING_ = 2;
/**
* Large spacing used between the zoom in and reset control, in pixels.
@@ -126,7 +128,7 @@ Blockly.ZoomControls.prototype.SMALL_SPACING_ = 2;
* @const
* @private
*/
Blockly.ZoomControls.prototype.LARGE_SPACING_ = 11;
ZoomControls.prototype.LARGE_SPACING_ = 11;
/**
* Distance between zoom controls and bottom or top edge of workspace.
@@ -134,55 +136,54 @@ Blockly.ZoomControls.prototype.LARGE_SPACING_ = 11;
* @const
* @private
*/
Blockly.ZoomControls.prototype.MARGIN_VERTICAL_ = 20;
ZoomControls.prototype.MARGIN_VERTICAL_ = 20;
/**
* Distance between zoom controls and right or left edge of workspace.
* @type {number}
* @private
*/
Blockly.ZoomControls.prototype.MARGIN_HORIZONTAL_ = 20;
ZoomControls.prototype.MARGIN_HORIZONTAL_ = 20;
/**
* The SVG group containing the zoom controls.
* @type {SVGElement}
* @private
*/
Blockly.ZoomControls.prototype.svgGroup_ = null;
ZoomControls.prototype.svgGroup_ = null;
/**
* Left coordinate of the zoom controls.
* @type {number}
* @private
*/
Blockly.ZoomControls.prototype.left_ = 0;
ZoomControls.prototype.left_ = 0;
/**
* Top coordinate of the zoom controls.
* @type {number}
* @private
*/
Blockly.ZoomControls.prototype.top_ = 0;
ZoomControls.prototype.top_ = 0;
/**
* Whether this has been initialized.
* @type {boolean}
* @private
*/
Blockly.ZoomControls.prototype.initialized_ = false;
ZoomControls.prototype.initialized_ = false;
/**
* Create the zoom controls.
* @return {!SVGElement} The zoom controls SVG group.
*/
Blockly.ZoomControls.prototype.createDom = function() {
this.svgGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G, {}, null);
ZoomControls.prototype.createDom = function() {
this.svgGroup_ = dom.createSvgElement(Svg.G, {}, null);
// Each filter/pattern needs a unique ID for the case of multiple Blockly
// instances on a page. Browser behaviour becomes undefined otherwise.
// https://neil.fraser.name/news/2015/11/01/
var rnd = String(Math.random()).substring(2);
const rnd = String(Math.random()).substring(2);
this.createZoomOutSvg_(rnd);
this.createZoomInSvg_(rnd);
if (this.workspace_.isMovable()) {
@@ -196,11 +197,11 @@ Blockly.ZoomControls.prototype.createDom = function() {
/**
* Initializes the zoom controls.
*/
Blockly.ZoomControls.prototype.init = function() {
ZoomControls.prototype.init = function() {
this.workspace_.getComponentManager().addComponent({
component: this,
weight: 2,
capabilities: [Blockly.ComponentManager.Capability.POSITIONABLE]
capabilities: [ComponentManager.Capability.POSITIONABLE]
});
this.initialized_ = true;
};
@@ -209,36 +210,36 @@ Blockly.ZoomControls.prototype.init = function() {
* Disposes of this zoom controls.
* Unlink from all DOM elements to prevent memory leaks.
*/
Blockly.ZoomControls.prototype.dispose = function() {
ZoomControls.prototype.dispose = function() {
this.workspace_.getComponentManager().removeComponent('zoomControls');
if (this.svgGroup_) {
Blockly.utils.dom.removeNode(this.svgGroup_);
dom.removeNode(this.svgGroup_);
}
if (this.onZoomResetWrapper_) {
Blockly.browserEvents.unbind(this.onZoomResetWrapper_);
browserEvents.unbind(this.onZoomResetWrapper_);
}
if (this.onZoomInWrapper_) {
Blockly.browserEvents.unbind(this.onZoomInWrapper_);
browserEvents.unbind(this.onZoomInWrapper_);
}
if (this.onZoomOutWrapper_) {
Blockly.browserEvents.unbind(this.onZoomOutWrapper_);
browserEvents.unbind(this.onZoomOutWrapper_);
}
};
/**
* Returns the bounding rectangle of the UI element in pixel units relative to
* the Blockly injection div.
* @return {?Blockly.utils.Rect} The UI elementss bounding box. Null if
* @return {?Rect} The UI elementss bounding box. Null if
* bounding box should be ignored by other UI elements.
*/
Blockly.ZoomControls.prototype.getBoundingRectangle = function() {
var height = this.SMALL_SPACING_ + 2 * this.HEIGHT_;
ZoomControls.prototype.getBoundingRectangle = function() {
let height = this.SMALL_SPACING_ + 2 * this.HEIGHT_;
if (this.zoomResetGroup_) {
height += this.LARGE_SPACING_ + this.HEIGHT_;
}
var bottom = this.top_ + height;
var right = this.left_ + this.WIDTH_;
return new Blockly.utils.Rect(this.top_, bottom, this.left_, right);
const bottom = this.top_ + height;
const right = this.left_ + this.WIDTH_;
return new Rect(this.top_, bottom, this.left_, right);
};
@@ -247,59 +248,57 @@ Blockly.ZoomControls.prototype.getBoundingRectangle = function() {
* It is positioned in the opposite corner to the corner the
* categories/toolbox starts at.
* @param {!Blockly.MetricsManager.UiMetrics} metrics The workspace metrics.
* @param {!Array<!Blockly.utils.Rect>} savedPositions List of rectangles that
* @param {!Array<!Rect>} savedPositions List of rectangles that
* are already on the workspace.
*/
Blockly.ZoomControls.prototype.position = function(metrics, savedPositions) {
ZoomControls.prototype.position = function(metrics, savedPositions) {
// Not yet initialized.
if (!this.initialized_) {
return;
}
var cornerPosition =
Blockly.uiPosition.getCornerOppositeToolbox(this.workspace_, metrics);
var height = this.SMALL_SPACING_ + 2 * this.HEIGHT_;
const cornerPosition =
uiPosition.getCornerOppositeToolbox(this.workspace_, metrics);
let height = this.SMALL_SPACING_ + 2 * this.HEIGHT_;
if (this.zoomResetGroup_) {
height += this.LARGE_SPACING_ + this.HEIGHT_;
}
var startRect = Blockly.uiPosition.getStartPositionRect(
cornerPosition, new Blockly.utils.Size(this.WIDTH_, height),
this.MARGIN_HORIZONTAL_, this.MARGIN_VERTICAL_, metrics,
this.workspace_);
const startRect = uiPosition.getStartPositionRect(
cornerPosition, new utils.Size(this.WIDTH_, height),
this.MARGIN_HORIZONTAL_, this.MARGIN_VERTICAL_, metrics, this.workspace_);
var verticalPosition = cornerPosition.vertical;
var bumpDirection =
verticalPosition === Blockly.uiPosition.verticalPosition.TOP ?
Blockly.uiPosition.bumpDirection.DOWN :
Blockly.uiPosition.bumpDirection.UP;
var positionRect = Blockly.uiPosition.bumpPositionRect(
const verticalPosition = cornerPosition.vertical;
const bumpDirection = verticalPosition === uiPosition.verticalPosition.TOP ?
uiPosition.bumpDirection.DOWN :
uiPosition.bumpDirection.UP;
const positionRect = uiPosition.bumpPositionRect(
startRect, this.MARGIN_VERTICAL_, bumpDirection, savedPositions);
if (verticalPosition === Blockly.uiPosition.verticalPosition.TOP) {
var zoomInTranslateY = this.SMALL_SPACING_ + this.HEIGHT_;
this.zoomInGroup_.setAttribute('transform',
'translate(0, ' + zoomInTranslateY + ')');
if (verticalPosition === uiPosition.verticalPosition.TOP) {
const zoomInTranslateY = this.SMALL_SPACING_ + this.HEIGHT_;
this.zoomInGroup_.setAttribute(
'transform', 'translate(0, ' + zoomInTranslateY + ')');
if (this.zoomResetGroup_) {
var zoomResetTranslateY =
const zoomResetTranslateY =
zoomInTranslateY + this.LARGE_SPACING_ + this.HEIGHT_;
this.zoomResetGroup_.setAttribute('transform',
'translate(0, ' + zoomResetTranslateY + ')');
this.zoomResetGroup_.setAttribute(
'transform', 'translate(0, ' + zoomResetTranslateY + ')');
}
} else {
var zoomInTranslateY = this.zoomResetGroup_ ?
this.LARGE_SPACING_ + this.HEIGHT_ : 0;
this.zoomInGroup_.setAttribute('transform',
'translate(0, ' + zoomInTranslateY + ')');
var zoomOutTranslateY =
const zoomInTranslateY =
this.zoomResetGroup_ ? this.LARGE_SPACING_ + this.HEIGHT_ : 0;
this.zoomInGroup_.setAttribute(
'transform', 'translate(0, ' + zoomInTranslateY + ')');
const zoomOutTranslateY =
zoomInTranslateY + this.SMALL_SPACING_ + this.HEIGHT_;
this.zoomOutGroup_.setAttribute('transform',
'translate(0, ' + zoomOutTranslateY + ')');
this.zoomOutGroup_.setAttribute(
'transform', 'translate(0, ' + zoomOutTranslateY + ')');
}
this.top_ = positionRect.top;
this.left_ = positionRect.left;
this.svgGroup_.setAttribute('transform',
'translate(' + this.left_ + ',' + this.top_ + ')');
this.svgGroup_.setAttribute(
'transform', 'translate(' + this.left_ + ',' + this.top_ + ')');
};
/**
@@ -309,48 +308,42 @@ Blockly.ZoomControls.prototype.position = function(metrics, savedPositions) {
* instances on the same page.
* @private
*/
Blockly.ZoomControls.prototype.createZoomOutSvg_ = function(rnd) {
ZoomControls.prototype.createZoomOutSvg_ = function(rnd) {
/* This markup will be generated and added to the .svgGroup_:
<g class="blocklyZoom">
<clipPath id="blocklyZoomoutClipPath837493">
<rect width="32" height="32></rect>
</clipPath>
<image width="96" height="124" x="-64" y="-92" xlink:href="media/sprites.png"
<image width="96" height="124" x="-64" y="-92"
xlink:href="media/sprites.png"
clip-path="url(#blocklyZoomoutClipPath837493)"></image>
</g>
*/
this.zoomOutGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G,
{'class': 'blocklyZoom'}, this.svgGroup_);
var clip = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.CLIPPATH,
{
'id': 'blocklyZoomoutClipPath' + rnd
},
this.zoomOutGroup_);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
this.zoomOutGroup_ =
dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
const clip = dom.createSvgElement(
Svg.CLIPPATH, {'id': 'blocklyZoomoutClipPath' + rnd}, this.zoomOutGroup_);
dom.createSvgElement(
Svg.RECT, {
'width': 32,
'height': 32,
},
clip);
var zoomoutSvg = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.IMAGE, {
'width': Blockly.internalConstants.SPRITE.width,
'height': Blockly.internalConstants.SPRITE.height,
const zoomoutSvg = dom.createSvgElement(
Svg.IMAGE, {
'width': internalConstants.SPRITE.width,
'height': internalConstants.SPRITE.height,
'x': -64,
'y': -92,
'clip-path': 'url(#blocklyZoomoutClipPath' + rnd + ')'
},
this.zoomOutGroup_);
zoomoutSvg.setAttributeNS(
Blockly.utils.dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia +
Blockly.internalConstants.SPRITE.url);
dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia + internalConstants.SPRITE.url);
// Attach listener.
this.onZoomOutWrapper_ = Blockly.browserEvents.conditionalBind(
this.onZoomOutWrapper_ = browserEvents.conditionalBind(
this.zoomOutGroup_, 'mousedown', null, this.zoom_.bind(this, -1));
};
@@ -361,48 +354,42 @@ Blockly.ZoomControls.prototype.createZoomOutSvg_ = function(rnd) {
* instances on the same page.
* @private
*/
Blockly.ZoomControls.prototype.createZoomInSvg_ = function(rnd) {
ZoomControls.prototype.createZoomInSvg_ = function(rnd) {
/* This markup will be generated and added to the .svgGroup_:
<g class="blocklyZoom">
<clipPath id="blocklyZoominClipPath837493">
<rect width="32" height="32"></rect>
</clipPath>
<image width="96" height="124" x="-32" y="-92" xlink:href="media/sprites.png"
<image width="96" height="124" x="-32" y="-92"
xlink:href="media/sprites.png"
clip-path="url(#blocklyZoominClipPath837493)"></image>
</g>
*/
this.zoomInGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G,
{'class': 'blocklyZoom'}, this.svgGroup_);
var clip = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.CLIPPATH,
{
'id': 'blocklyZoominClipPath' + rnd
},
this.zoomInGroup_);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
this.zoomInGroup_ =
dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
const clip = dom.createSvgElement(
Svg.CLIPPATH, {'id': 'blocklyZoominClipPath' + rnd}, this.zoomInGroup_);
dom.createSvgElement(
Svg.RECT, {
'width': 32,
'height': 32,
},
clip);
var zoominSvg = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.IMAGE, {
'width': Blockly.internalConstants.SPRITE.width,
'height': Blockly.internalConstants.SPRITE.height,
const zoominSvg = dom.createSvgElement(
Svg.IMAGE, {
'width': internalConstants.SPRITE.width,
'height': internalConstants.SPRITE.height,
'x': -32,
'y': -92,
'clip-path': 'url(#blocklyZoominClipPath' + rnd + ')'
},
this.zoomInGroup_);
zoominSvg.setAttributeNS(
Blockly.utils.dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia +
Blockly.internalConstants.SPRITE.url);
dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia + internalConstants.SPRITE.url);
// Attach listener.
this.onZoomInWrapper_ = Blockly.browserEvents.conditionalBind(
this.onZoomInWrapper_ = browserEvents.conditionalBind(
this.zoomInGroup_, 'mousedown', null, this.zoom_.bind(this, 1));
};
@@ -414,13 +401,13 @@ Blockly.ZoomControls.prototype.createZoomInSvg_ = function(rnd) {
* @param {!Event} e A mouse down event.
* @private
*/
Blockly.ZoomControls.prototype.zoom_ = function(amount, e) {
ZoomControls.prototype.zoom_ = function(amount, e) {
this.workspace_.markFocused();
this.workspace_.zoomCenter(amount);
this.fireZoomEvent_();
Blockly.Touch.clearTouchIdentifier(); // Don't block future drags.
e.stopPropagation(); // Don't start a workspace scroll.
e.preventDefault(); // Stop double-clicking from selecting text.
Touch.clearTouchIdentifier(); // Don't block future drags.
e.stopPropagation(); // Don't start a workspace scroll.
e.preventDefault(); // Stop double-clicking from selecting text.
};
/**
@@ -430,47 +417,37 @@ Blockly.ZoomControls.prototype.zoom_ = function(amount, e) {
* instances on the same page.
* @private
*/
Blockly.ZoomControls.prototype.createZoomResetSvg_ = function(rnd) {
ZoomControls.prototype.createZoomResetSvg_ = function(rnd) {
/* This markup will be generated and added to the .svgGroup_:
<g class="blocklyZoom">
<clipPath id="blocklyZoomresetClipPath837493">
<rect width="32" height="32"></rect>
</clipPath>
<image width="96" height="124" x="-32" y="-92" xlink:href="media/sprites.png"
<image width="96" height="124" x="-32" y="-92"
xlink:href="media/sprites.png"
clip-path="url(#blocklyZoomresetClipPath837493)"></image>
</g>
*/
this.zoomResetGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G,
{'class': 'blocklyZoom'}, this.svgGroup_);
var clip = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.CLIPPATH,
{
'id': 'blocklyZoomresetClipPath' + rnd
},
this.zoomResetGroup_ =
dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
const clip = dom.createSvgElement(
Svg.CLIPPATH, {'id': 'blocklyZoomresetClipPath' + rnd},
this.zoomResetGroup_);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
'width': 32,
'height': 32
},
clip);
var zoomresetSvg = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.IMAGE, {
'width': Blockly.internalConstants.SPRITE.width,
'height': Blockly.internalConstants.SPRITE.height,
dom.createSvgElement(Svg.RECT, {'width': 32, 'height': 32}, clip);
const zoomresetSvg = dom.createSvgElement(
Svg.IMAGE, {
'width': internalConstants.SPRITE.width,
'height': internalConstants.SPRITE.height,
'y': -92,
'clip-path': 'url(#blocklyZoomresetClipPath' + rnd + ')'
},
this.zoomResetGroup_);
zoomresetSvg.setAttributeNS(
Blockly.utils.dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia +
Blockly.internalConstants.SPRITE.url);
dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia + internalConstants.SPRITE.url);
// Attach event listeners.
this.onZoomResetWrapper_ = Blockly.browserEvents.conditionalBind(
this.onZoomResetWrapper_ = browserEvents.conditionalBind(
this.zoomResetGroup_, 'mousedown', null, this.resetZoom_.bind(this));
};
@@ -479,55 +456,55 @@ Blockly.ZoomControls.prototype.createZoomResetSvg_ = function(rnd) {
* @param {!Event} e A mouse down event.
* @private
*/
Blockly.ZoomControls.prototype.resetZoom_ = function(e) {
ZoomControls.prototype.resetZoom_ = function(e) {
this.workspace_.markFocused();
// zoom is passed amount and computes the new scale using the formula:
// targetScale = currentScale * Math.pow(speed, amount)
var targetScale = this.workspace_.options.zoomOptions.startScale;
var currentScale = this.workspace_.scale;
var speed = this.workspace_.options.zoomOptions.scaleSpeed;
const targetScale = this.workspace_.options.zoomOptions.startScale;
const currentScale = this.workspace_.scale;
const speed = this.workspace_.options.zoomOptions.scaleSpeed;
// To compute amount:
// amount = log(speed, (targetScale / currentScale))
// Math.log computes natural logarithm (ln), to change the base, use formula:
// log(base, value) = ln(value) / ln(base)
var amount = Math.log(targetScale / currentScale) / Math.log(speed);
const amount = Math.log(targetScale / currentScale) / Math.log(speed);
this.workspace_.beginCanvasTransition();
this.workspace_.zoomCenter(amount);
this.workspace_.scrollCenter();
setTimeout(this.workspace_.endCanvasTransition.bind(this.workspace_), 500);
this.fireZoomEvent_();
Blockly.Touch.clearTouchIdentifier(); // Don't block future drags.
e.stopPropagation(); // Don't start a workspace scroll.
e.preventDefault(); // Stop double-clicking from selecting text.
Touch.clearTouchIdentifier(); // Don't block future drags.
e.stopPropagation(); // Don't start a workspace scroll.
e.preventDefault(); // Stop double-clicking from selecting text.
};
/**
* Fires a zoom control UI event.
* @private
*/
Blockly.ZoomControls.prototype.fireZoomEvent_ = function() {
var uiEvent = new (Blockly.Events.get(Blockly.Events.CLICK))(
null, this.workspace_.id, 'zoom_controls');
Blockly.Events.fire(uiEvent);
ZoomControls.prototype.fireZoomEvent_ = function() {
const uiEvent =
new (Events.get(Events.CLICK))(null, this.workspace_.id, 'zoom_controls');
Events.fire(uiEvent);
};
/**
* CSS for zoom controls. See css.js for use.
*/
Blockly.Css.register([
/* eslint-disable indent */
'.blocklyZoom>image, .blocklyZoom>svg>image {',
'opacity: .4;',
'}',
Css.register([
`.blocklyZoom>image, .blocklyZoom>svg>image {
opacity: .4;
}`,
'.blocklyZoom>image:hover, .blocklyZoom>svg>image:hover {',
'opacity: .6;',
'}',
`.blocklyZoom>image:hover, .blocklyZoom>svg>image:hover {
opacity: .6;
}`,
'.blocklyZoom>image:active, .blocklyZoom>svg>image:active {',
'opacity: .8;',
'}'
/* eslint-enable indent */
`.blocklyZoom>image:active, .blocklyZoom>svg>image:active {
'opacity: .8;
}`
]);
exports = ZoomControls;

View File

@@ -1,5 +1,41 @@
#!/bin/bash
# This file makes extensive use of perl for the purpose of extracting and
# replacing string (regex) patterns in a way that is both GNU and macOS
# compatible.
#
# Common perl flags used (also described at https://perldoc.perl.org/perlrun):
# -e : Used to execute perl programs on the command line
# -p : Assumes an input loop around script. Prints every processed line.
# -n : Assumes an input loop around script. Does not print every line.
# -i : Used for in-place editing. Used in commands for find/replace.
# -l[octnum] : Assigns the output record separator "$/" as an octal number. If
# octnum is not present, sets output record separator to the current
# value of the input record separator "$\".
#
# Common perl commands found:
# 1. perl -pi -e 's/regex/replacement/modifiers'
# This command does an in-place search-and-replace. The global ("/g") modifier
# causes it to replace all occurrences, rather than only the first match.
# 2. perl -ne 'print m/regex/modifiers'
# This command returns a string containing the regex match (designated by the
# capture group "()" in the regex). This will return the first match, unless
# the global modifier is specified, in which case, it will return all matches.
# If this command is used without a capture group it returns true or false (in
# the form a truthy or falsy value).
# 3. perl -nle 'print $& while m{regex}modifiers'
# Similar to (2), but returns regex matches separated by newlines.
# The "m{regex}modifiers" is equivalent to "m/regex/modifiers" syntax.
#
# Additional information on regex:
# This script makes use of some advanced regex syntax such as "capture groups"
# and "lookaround assertions".
# Additionally, characters are escaped from regex with a backslash "\".
# Single quotes need to be escaped in both regex and the string, resulting in
# '\'' being used to represent a single quote character.
# For a reference to syntax of regular expressions in Perl, see:
# https://perldoc.perl.org/perlre
#######################################
# Logging functions.
#######################################
@@ -89,6 +125,64 @@ commit-step() {
success "created commit with message: \"${message}\""
}
#######################################
# Extracts a list of properties that are accessed on the specified module name.
# Excludes any matches
# Arguments:
# The module name to find properties accessed for.
# The modules required by the specified module as a single string.
# The filepath to extract requires from.
# Optional: The top-level module.
# Outputs:
# Writes list of properties to stdout as items separated by spaces.
#######################################
getPropertiesAccessed() {
local module_name="$1"
local requires="$2"
local filepath="$3"
local top_module_name="$4"
# Get any strings that follow "$module_name.", excluding matches for
# "$module_name.prototype" and remove list item duplicates (sort -u).
local properties_accessed=$(perl -nle 'print $& while m{(?<='"${module_name}"'\.)(?!prototype)\w+}g' "${filepath}" | sort -u)
# Get a list of any requires that are a child of $module_name.
# Ex: Blockly.utils.dom is a child of Blockly.utils, this would return "dom"
local requires_overlap=$(echo "${requires}" | perl -nle 'print $& while m{(?<='"${module_name}"'\.)\w+}g')
# Detect if there was any overlap.
if [[ -n "${requires_overlap}" ]]; then
while read -r requires_overlap_prop; do
# Removes any instances of $requires_overlap_prop. Includes regex
# lookarounds so that it does not simply match string contains.
# Ex: if $requires_overlap is "Svg", then it would update the list
# "isTargetInput mouseToSvg noEvent Svg" to
# "isTargetInput mouseToSvg noEvent " (note that mouseToSvg is unchanged).
properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/(?<!\w)'"${requires_overlap_prop}"'(?!\w)//g')
done <<<"${requires_overlap}"
fi
# Fix formatting (remove extra whitespace) and delimit the list with spaces.
properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/\s+/ /g' | xargs)
echo "${properties_accessed}"
}
#######################################
# Extracts a list of requires defined in the file in the form of a single string
# of items separated by newlines.
# Arguments:
# The filepath to extract requires from.
# Outputs:
# Writes list of requires to stdout as items separated by newlines.
#######################################
getRequires() {
local filepath="$1"
# Extracts all strings that start with goog.require(' or goog.requireType('
# up until the ending single quote.
# Ex: "goog.require('Blockly.utils')" would extract "Blockly.utils"
local requires=$(perl -nle 'print $& while m{(?:(?<=^goog.require\('\'')|(?<=^goog.requireType\('\''))[^'\'']+}g' "${filepath}")
echo "${requires}"
}
#######################################
# Runs step 2 of the automated conversion.
# Arguments:
@@ -98,10 +192,10 @@ step2 () {
local filepath="$1"
inf "Updating goog.provide declaration..."
perl -pi -e 's/^goog\.provide(\([^\)]+\)\;)/goog\.module\1\ngoog.module.declareLegacyNamespace\(\)\;/g' "${filepath}"
perl -pi -e 's/^goog\.provide(\([^\)]+\)\;)/goog\.module\1\ngoog.module.declareLegacyNamespace\(\)\;/' "${filepath}"
inf "Extracting module name..."
local module_name=$(perl -nle'print $& while m{(?<=^goog\.module\('\'')([^'\'')]+)}g' "${filepath}")
local module_name=$(perl -ne 'print m/(?<=^goog\.module\('\'')([^'\'']+)/' "${filepath}")
if [[ -z "${module_name}" ]]; then
err "Could not extract module name"
return 1
@@ -109,7 +203,7 @@ step2 () {
inf "Extracted module name \"${module_name}\""
if [[ $(grep "${module_name} = " "${filepath}") ]]; then
local class_name=$(echo "${module_name}" | perl -nle'print $& while m{(\w+)$}g')
local class_name=$(echo "${module_name}" | perl -ne 'print m/(\w+)$/')
inf "Found class \"${class_name}\" in file."
inf "Updating class declaration..."
perl -pi -e 's/^('"${module_name}"') =/const '"${class_name}"' =/g' "${filepath}"
@@ -130,8 +224,18 @@ step2 () {
# No top level class.
inf 'Updating top-level property declarations...'
perl -pi -e 's/^'"${module_name}"'\.([^ ]+) =/const \1 =/g' "${filepath}"
# Extract specific properties accessed so that properties from requires that
# are children of the module aren't changed.
# Ex: The module Blockly.utils shouldn't update Blockly.utils.dom (since it is
# a require from another module.
local requires=$(getRequires "${filepath}")
local properties_accessed=$(getPropertiesAccessed "${module_name}" "${requires}" "${filepath}")
inf "Updating local references to module..."
perl -pi -e 's/'"${module_name}"'\.([^ ]+)/\1/g' "${filepath}"
for property in $(echo "${properties_accessed}"); do
inf "Updating references of ${module_name}.${property} to ${property}..."
perl -pi -e 's/'"${module_name}"'\.'"${property}"'(?!\w)/'"${property}"'/g' "${filepath}"
done
npm run build:deps
success "Completed automation for step 2. Please manually review and add exports for non-private top-level functions."
@@ -144,85 +248,68 @@ step2 () {
#######################################
step3() {
inf "Extracting module name..."
local module_name=$(perl -nle'print $& while m{(?<=^goog\.module\('\'')([^'\'')]+)}g' "${filepath}")
local module_name=$(perl -ne 'print m/(?<=^goog\.module\('\'')([^'\'']+)/' "${filepath}")
if [[ -z "${module_name}" ]]; then
err "Could not extract module name"
return 1
fi
inf "Extracted module name \"${module_name}\""
local requires=$(perl -nle'print $& while m{(?:(?<=^goog.require\('\'')|(?<=^goog.requireType\('\''))[^'\'']+}g' "${filepath}")
local requires=$(getRequires "${filepath}")
# Process each require
echo "${requires}" | while read -r require; do
inf "Processing require \"${require}\""
local usages=$(perl -nle'print $& while m{'"${require}"'(?!'\'')}g' "${filepath}" | wc -l)
local usages=$(perl -nle 'print $& while m{'"${require}"'(?!'\'')}g' "${filepath}" | wc -l)
if [[ "${usages}" -eq "0" ]]; then
warn "Unused require \"${require}\""
continue
fi
local direct_access_count=$(perl -nle'print $& while m{'"${require}"'[^\.'\'']}g' "${filepath}" | wc -l)
local properties_accessed=$(perl -nle'print $& while m{(?<='"${require}"'\.)(?!prototype)\w+}g' "${filepath}" | tr ' ' '\n' | sort -u)
# Detect requires overlap
# (ex: Blockly.utils require and Blockly.utils.dom also in requires)
local requires_overlap=$(echo "${requires}" | perl -nle'print $& while m{(?<='"${require}"'\.)\w+}g')
if [[ -n "${requires_overlap}" ]]; then
while read -r requires_overlap_prop; do
properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/'"${requires_overlap_prop}"'//g')
done <<<"${requires_overlap}"
fi
# Detect module name overlap
# (ex: Blockly require and Blockly.ContextMenuItems module being converted)
local module_overlap=$(echo "${module_name}" | perl -nle'print $& while m{(?<='"${require}"'\.)\w+}g')
if [[ -n "${module_overlap}" ]]; then
properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/'"${module_overlap}"'//g')
fi
properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/\s+/ /g' | xargs)
if [[ "${direct_access_count}" -eq "0" && -n "${properties_accessed}" ]]; then
local deconstructed_comma=$(echo "${properties_accessed}" | perl -pe 's/\s+/, /g' | perl -pe 's/, $//')
local confirm=''
while true; do
read -p "Would you like to deconstruct ${require} into \"{${deconstructed_comma}}\"? (y/n): " yn </dev/tty
case $yn in
[Yy]* )
confirm='true'
break
;;
[Nn]* )
confirm='false'
break
;;
* ) reenter_instructions "Please type y or n \"${yn}\"";;
esac
done
if [[ "${confirm}" == 'true' ]]; then
inf "Deconstructing ${require} into \"{${deconstructed_comma}}\"..."
perl -pi -e 's/^(goog\.(require|requireType)\('\'"${require}"\''\);)/const \{'"${deconstructed_comma}"'\} = \1/' "${filepath}"
for require_prop in $(echo "${properties_accessed}"); do
inf "Updating references of ${require}.${require_prop} to ${require_prop}..."
perl -pi -e 's/'"${require}"'\.'"${require_prop}"'([^'\''\w])/'"${require_prop}"'\1/g' "${filepath}"
done
continue
fi
fi
local require_name=$(echo "${require}" | perl -pe 's/(\w+\.)+(\w+)/\2/g')
inf "Updating require declaration for ${require}..."
perl -pi -e 's/^(goog\.(require|requireType)\('\'"${require}"\''\);)/const '"${require_name}"' = \1/' "${filepath}"
inf "Updating references of ${require} to ${require_name}..."
perl -pi -e 's/'"${require}"'([^'\''\w])/'"${require_name}"'\1/g' "${filepath}"
# Parse property access of module
local direct_access_count=$(perl -nle 'print $& while m{'"${require}"'[^\.'\'']}g' "${filepath}" | wc -l)
local properties_accessed=$(getPropertiesAccessed "${require}" "${requires}" "${filepath}")
# Remove $module_name in case it is a child of $require.
# Ex: Blockly.utils.dom would be a child of Blockly, module_overlap would be
# "utils"
local module_overlap=$(echo "${module_name}" | perl -nle 'print $& while m{(?<='"${require}"'\.)\w+}g')
if [[ -n "${module_overlap}" ]]; then
properties_accessed=$(echo "${properties_accessed}" | perl -pe 's/'"${module_overlap}"'//g')
# Trim any extra whitespace created.
properties_accessed=$(echo "${properties_accessed}" | xargs)
fi
if [[ -n "${properties_accessed}" ]]; then
local comma_properties=$(echo "${properties_accessed}" | perl -pe 's/\s+/, /g' | perl -pe 's/, $//')
inf "Detected references of ${require}: ${comma_properties}"
for require_prop in $(echo "${properties_accessed}"); do
inf "Updating references of ${require}.${require_prop} to ${require_name}.${require_prop}..."
perl -pi -e 's/'"${require}"'\.'"${require_prop}"'(?!\w)/'"${require_name}"'\.'"${require_prop}"'/g' "${filepath}"
done
fi
inf "Updating direct references of ${require} to ${require_name}..."
perl -pi -e 's/'"${require}"'(?!['\''\w\.])/'"${require_name}"'/g' "${filepath}"
done
local missing_requires=$(perl -nle'print $& while m{(?<!'\'')Blockly(\.\w+)+}g' "${filepath}")
missing_requires=$(echo "${missing_requires}" | tr ' ' '\n' | sort -u)
if [[ -n "${missing_requires}" ]]; then
err "Missing requires for:\n${missing_requires}\nPlease manually fix."
# Search for the string goog.require('Blockly') or goog.requireType('Blockly')
local has_blockly_require=$(perl -ne 'print m/goog\.(?:require|requireType)\('\''Blockly'\''\)/' "${filepath}")
if [[ -n "${has_blockly_require}" ]]; then
warn 'Blockly detected as a require.'
warn "Potentially missing requires for:\n${missing_requires}\nPlease manually review."
else
err "Missing requires for:\n${missing_requires}\nPlease manually fix."
fi
fi
success "Completed automation for step 3. Please manually review and reorder requires."
@@ -287,8 +374,8 @@ help() {
echo ""
echo "Usage: $0 [-h] [-c <step> <filepath>|-s <step> <filepath>]"
echo " -h Display help and exit"
echo " -c <step> <filepath> Create a commit for the specified step [2-4]"
echo " -s <step> <filepath> Run the specified step [1-4]"
echo " -c <step> <filepath> Create a commit for the specified step [1-4]"
echo " -s <step> <filepath> Run the specified step [2-4]"
}
#######################################

View File

@@ -10,7 +10,7 @@ goog.addDependency('../../blocks/variables_dynamic.js', ['Blockly.Constants.Vari
goog.addDependency('../../core/block.js', ['Blockly.Block'], ['Blockly.ASTNode', 'Blockly.Blocks', 'Blockly.Connection', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Extensions', 'Blockly.IASTNodeLocation', 'Blockly.IDeletable', 'Blockly.Input', 'Blockly.Tooltip', 'Blockly.Workspace', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.fieldRegistry', 'Blockly.inputTypes', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.object'], {'lang': 'es5'});
goog.addDependency('../../core/block_animations.js', ['Blockly.blockAnimations'], ['Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/block_drag_surface.js', ['Blockly.BlockDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/block_dragger.js', ['Blockly.BlockDragger'], ['Blockly.Events', 'Blockly.Events.BlockDrag', 'Blockly.Events.BlockMove', 'Blockly.IBlockDragger', 'Blockly.InsertionMarkerManager', 'Blockly.blockAnimations', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']);
goog.addDependency('../../core/block_dragger.js', ['Blockly.BlockDragger'], ['Blockly.Events', 'Blockly.Events.BlockDrag', 'Blockly.Events.BlockMove', 'Blockly.IBlockDragger', 'Blockly.InsertionMarkerManager', 'Blockly.blockAnimations', 'Blockly.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/block_svg.js', ['Blockly.BlockSvg'], ['Blockly.ASTNode', 'Blockly.Block', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Selected', 'Blockly.IASTNodeLocationSvg', 'Blockly.IBoundedElement', 'Blockly.ICopyable', 'Blockly.IDraggable', 'Blockly.Msg', 'Blockly.RenderedConnection', 'Blockly.TabNavigateCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Xml', 'Blockly.blockAnimations', 'Blockly.blockRendering.IPathObject', 'Blockly.browserEvents', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent']);
goog.addDependency('../../core/blockly.js', ['Blockly'], ['Blockly.ComponentManager', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.Ui', 'Blockly.Events.UiBase', 'Blockly.Events.VarCreate', 'Blockly.Procedures', 'Blockly.ShortcutRegistry', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.browserEvents', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.inject', 'Blockly.inputTypes', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.colour', 'Blockly.utils.deprecation', 'Blockly.utils.toolbox']);
goog.addDependency('../../core/blocks.js', ['Blockly.Blocks'], [], {'lang': 'es6', 'module': 'goog'});
@@ -53,24 +53,24 @@ goog.addDependency('../../core/field_angle.js', ['Blockly.FieldAngle'], ['Blockl
goog.addDependency('../../core/field_checkbox.js', ['Blockly.FieldCheckbox'], ['Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/field_colour.js', ['Blockly.FieldColour'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.browserEvents', 'Blockly.fieldRegistry', 'Blockly.utils.IdGenerator', 'Blockly.utils.KeyCodes', 'Blockly.utils.Size', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/field_dropdown.js', ['Blockly.FieldDropdown'], ['Blockly.DropDownDiv', 'Blockly.Field', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.string', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/field_image.js', ['Blockly.FieldImage'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object']);
goog.addDependency('../../core/field_label.js', ['Blockly.FieldLabel'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.object']);
goog.addDependency('../../core/field_label_serializable.js', ['Blockly.FieldLabelSerializable'], ['Blockly.FieldLabel', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.object']);
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_image.js', ['Blockly.FieldImage'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/field_label.js', ['Blockly.FieldLabel'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/field_label_serializable.js', ['Blockly.FieldLabelSerializable'], ['Blockly.FieldLabel', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
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': 'es6', 'module': 'goog'});
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'], {'lang': 'es6', 'module': 'goog'});
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', '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'], {'lang': 'es6', 'module': 'goog'});
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_base.js', ['Blockly.Flyout'], ['Blockly', 'Blockly.Block', 'Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutButton', 'Blockly.FlyoutMetricsManager', 'Blockly.Gesture', 'Blockly.IFlyout', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', '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'], {'lang': 'es6', 'module': 'goog'});
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': 'es6', 'module': 'goog'});
goog.addDependency('../../core/flyout_horizontal.js', ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.DropDownDiv', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.toolbox']);
goog.addDependency('../../core/flyout_vertical.js', ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.DropDownDiv', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.toolbox']);
goog.addDependency('../../core/generator.js', ['Blockly.Generator'], ['Blockly.Block', 'Blockly.internalConstants', 'Blockly.utils.deprecation']);
goog.addDependency('../../core/gesture.js', ['Blockly.Gesture'], ['Blockly.BlockDragger', 'Blockly.BubbleDragger', 'Blockly.Events', 'Blockly.Events.Click', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Workspace', 'Blockly.WorkspaceDragger', 'Blockly.blockAnimations', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Coordinate']);
goog.addDependency('../../core/grid.js', ['Blockly.Grid'], ['Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.userAgent']);
goog.addDependency('../../core/icon.js', ['Blockly.Icon'], ['Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom']);
goog.addDependency('../../core/flyout_horizontal.js', ['Blockly.HorizontalFlyout'], ['Blockly.DropDownDiv', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/flyout_vertical.js', ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.DropDownDiv', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/generator.js', ['Blockly.Generator'], ['Blockly', 'Blockly.internalConstants', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/gesture.js', ['Blockly.Gesture'], ['Blockly.BlockDragger', 'Blockly.BubbleDragger', 'Blockly.Events', 'Blockly.Events.Click', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Workspace', 'Blockly.WorkspaceDragger', 'Blockly.blockAnimations', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Coordinate'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/grid.js', ['Blockly.Grid'], ['Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/icon.js', ['Blockly.Icon'], ['Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/inject.js', ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Grid', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.WidgetDiv', 'Blockly.Workspace', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.WorkspaceSvg', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.userAgent']);
goog.addDependency('../../core/input.js', ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel', 'Blockly.fieldRegistry', 'Blockly.inputTypes'], {'lang': 'es5'});
goog.addDependency('../../core/input.js', ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel', 'Blockly.constants', 'Blockly.fieldRegistry', 'Blockly.inputTypes'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/input_types.js', ['Blockly.inputTypes'], ['Blockly.connectionTypes']);
goog.addDependency('../../core/insertion_marker_manager.js', ['Blockly.InsertionMarkerManager'], ['Blockly.ComponentManager', 'Blockly.Events', 'Blockly.blockAnimations', 'Blockly.connectionTypes', 'Blockly.internalConstants'], {'lang': 'es5'});
goog.addDependency('../../core/interfaces/i_ast_node_location.js', ['Blockly.IASTNodeLocation'], [], {'lang': 'es6', 'module': 'goog'});
@@ -91,7 +91,7 @@ goog.addDependency('../../core/interfaces/i_drag_target.js', ['Blockly.IDragTarg
goog.addDependency('../../core/interfaces/i_draggable.js', ['Blockly.IDraggable'], ['Blockly.IDeletable'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/interfaces/i_flyout.js', ['Blockly.IFlyout'], ['Blockly.IRegistrable'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/interfaces/i_keyboard_accessible.js', ['Blockly.IKeyboardAccessible'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/interfaces/i_metrics_manager.js', ['Blockly.IMetricsManager'], []);
goog.addDependency('../../core/interfaces/i_metrics_manager.js', ['Blockly.IMetricsManager'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/interfaces/i_movable.js', ['Blockly.IMovable'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/interfaces/i_positionable.js', ['Blockly.IPositionable'], ['Blockly.IComponent'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/interfaces/i_registrable.js', ['Blockly.IRegistrable'], [], {'lang': 'es6', 'module': 'goog'});
@@ -102,31 +102,31 @@ goog.addDependency('../../core/interfaces/i_styleable.js', ['Blockly.IStyleable'
goog.addDependency('../../core/interfaces/i_toolbox.js', ['Blockly.IToolbox'], ['Blockly.IRegistrable'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/interfaces/i_toolbox_item.js', ['Blockly.IToolboxItem'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/internal_constants.js', ['Blockly.internalConstants'], ['Blockly.connectionTypes'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/keyboard_nav/ast_node.js', ['Blockly.ASTNode'], ['Blockly.connectionTypes', 'Blockly.utils.Coordinate'], {'lang': 'es5'});
goog.addDependency('../../core/keyboard_nav/ast_node.js', ['Blockly.ASTNode'], ['Blockly.connectionTypes', 'Blockly.utils.Coordinate'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/keyboard_nav/basic_cursor.js', ['Blockly.BasicCursor'], ['Blockly.ASTNode', 'Blockly.Cursor', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/keyboard_nav/cursor.js', ['Blockly.Cursor'], ['Blockly.ASTNode', 'Blockly.Marker', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/keyboard_nav/marker.js', ['Blockly.Marker'], ['Blockly.ASTNode']);
goog.addDependency('../../core/keyboard_nav/marker.js', ['Blockly.Marker'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/keyboard_nav/tab_navigate_cursor.js', ['Blockly.TabNavigateCursor'], ['Blockly.ASTNode', 'Blockly.BasicCursor', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/marker_manager.js', ['Blockly.MarkerManager'], ['Blockly.Cursor', 'Blockly.Marker']);
goog.addDependency('../../core/menu.js', ['Blockly.Menu'], ['Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.style']);
goog.addDependency('../../core/marker_manager.js', ['Blockly.MarkerManager'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/menu.js', ['Blockly.Menu'], ['Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.style'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/menuitem.js', ['Blockly.MenuItem'], ['Blockly.utils.IdGenerator', 'Blockly.utils.aria', 'Blockly.utils.dom']);
goog.addDependency('../../core/metrics_manager.js', ['Blockly.FlyoutMetricsManager', 'Blockly.MetricsManager'], ['Blockly.IMetricsManager', 'Blockly.registry', 'Blockly.utils.Size', 'Blockly.utils.toolbox'], {'lang': 'es5'});
goog.addDependency('../../core/msg.js', ['Blockly.Msg'], ['Blockly.utils.global']);
goog.addDependency('../../core/mutator.js', ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BubbleOpen', 'Blockly.Icon', 'Blockly.Options', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox', 'Blockly.utils.xml']);
goog.addDependency('../../core/mutator.js', ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BubbleOpen', 'Blockly.Icon', 'Blockly.Options', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.internalConstants', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/names.js', ['Blockly.Names'], ['Blockly.Msg', 'Blockly.internalConstants']);
goog.addDependency('../../core/options.js', ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.registry', 'Blockly.utils.IdGenerator', 'Blockly.utils.Metrics', 'Blockly.utils.toolbox']);
goog.addDependency('../../core/positionable_helpers.js', ['Blockly.uiPosition'], ['Blockly.Scrollbar', 'Blockly.utils.Rect', 'Blockly.utils.toolbox']);
goog.addDependency('../../core/procedures.js', ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.Names', 'Blockly.Workspace', 'Blockly.Xml', 'Blockly.internalConstants', 'Blockly.utils.xml']);
goog.addDependency('../../core/registry.js', ['Blockly.registry'], []);
goog.addDependency('../../core/rendered_connection.js', ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.connectionTypes', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.object']);
goog.addDependency('../../core/registry.js', ['Blockly.registry'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/rendered_connection.js', ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.Events', 'Blockly.connectionTypes', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/renderers/common/block_rendering.js', ['Blockly.blockRendering'], ['Blockly.registry']);
goog.addDependency('../../core/renderers/common/constants.js', ['Blockly.blockRendering.ConstantProvider'], ['Blockly.connectionTypes', 'Blockly.utils', 'Blockly.utils.Svg', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.svgPaths', 'Blockly.utils.userAgent'], {'lang': 'es5'});
goog.addDependency('../../core/renderers/common/constants.js', ['Blockly.blockRendering.ConstantProvider'], ['Blockly.connectionTypes', 'Blockly.utils', 'Blockly.utils.Svg', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.svgPaths', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/renderers/common/debugger.js', ['Blockly.blockRendering.Debug'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.Types', 'Blockly.connectionTypes', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es5'});
goog.addDependency('../../core/renderers/common/drawer.js', ['Blockly.blockRendering.Drawer'], ['Blockly.blockRendering.Row', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/renderers/common/i_path_object.js', ['Blockly.blockRendering.IPathObject'], []);
goog.addDependency('../../core/renderers/common/info.js', ['Blockly.blockRendering.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.Field', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.Icon', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.JaggedEdge', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.constants', 'Blockly.inputTypes'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/renderers/common/marker_svg.js', ['Blockly.blockRendering.MarkerSvg'], ['Blockly.ASTNode', 'Blockly.Events', 'Blockly.Events.MarkerMove', 'Blockly.connectionTypes', 'Blockly.utils.Svg', 'Blockly.utils.dom']);
goog.addDependency('../../core/renderers/common/path_object.js', ['Blockly.blockRendering.PathObject'], ['Blockly.Theme', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.IPathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom']);
goog.addDependency('../../core/renderers/common/path_object.js', ['Blockly.blockRendering.PathObject'], ['Blockly.blockRendering.IPathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/renderers/common/renderer.js', ['Blockly.blockRendering.Renderer'], ['Blockly.IRegistrable', 'Blockly.InsertionMarkerManager', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Debug', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.MarkerSvg', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo', 'Blockly.connectionTypes']);
goog.addDependency('../../core/renderers/geras/constants.js', ['Blockly.geras.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object'], {'lang': 'es5'});
goog.addDependency('../../core/renderers/geras/drawer.js', ['Blockly.geras.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.geras.Highlighter', 'Blockly.geras.RenderInfo', 'Blockly.utils.object', 'Blockly.utils.svgPaths']);
@@ -160,13 +160,13 @@ goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Ren
goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.ContextMenuItems', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.ShortcutItems', 'Blockly.Themes.Classic', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer']);
goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Events', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.Svg', 'Blockly.utils.dom']);
goog.addDependency('../../core/shortcut_items.js', ['Blockly.ShortcutItems'], ['Blockly.Gesture', 'Blockly.ShortcutRegistry', 'Blockly.utils.KeyCodes']);
goog.addDependency('../../core/shortcut_registry.js', ['Blockly.ShortcutRegistry'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object']);
goog.addDependency('../../core/shortcut_registry.js', ['Blockly.ShortcutRegistry'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.registry', 'Blockly.utils', 'Blockly.utils.object']);
goog.addDependency('../../core/theme/classic.js', ['Blockly.Themes.Classic'], ['Blockly.Theme']);
goog.addDependency('../../core/theme/zelos.js', ['Blockly.Themes.Zelos'], ['Blockly.Theme']);
goog.addDependency('../../core/theme_manager.js', ['Blockly.ThemeManager'], ['Blockly.Theme']);
goog.addDependency('../../core/theme_manager.js', ['Blockly.ThemeManager'], ['Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/toolbox/category.js', ['Blockly.ToolboxCategory'], ['Blockly.ISelectableToolboxItem', 'Blockly.ToolboxItem', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es5'});
goog.addDependency('../../core/toolbox/collapsible_category.js', ['Blockly.CollapsibleToolboxCategory'], ['Blockly.ICollapsibleToolboxItem', 'Blockly.ToolboxCategory', 'Blockly.ToolboxItem', 'Blockly.ToolboxSeparator', 'Blockly.registry', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox']);
goog.addDependency('../../core/toolbox/collapsible_category.js', ['Blockly.CollapsibleToolboxCategory'], ['Blockly.ICollapsibleToolboxItem', 'Blockly.ToolboxCategory', 'Blockly.ToolboxSeparator', 'Blockly.registry', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/toolbox/separator.js', ['Blockly.ToolboxSeparator'], ['Blockly.IToolboxItem', 'Blockly.ToolboxItem', 'Blockly.registry', 'Blockly.utils.dom'], {'lang': 'es5'});
goog.addDependency('../../core/toolbox/toolbox.js', ['Blockly.Toolbox'], ['Blockly.BlockSvg', 'Blockly.CollapsibleToolboxCategory', 'Blockly.ComponentManager', 'Blockly.Css', 'Blockly.DeleteArea', 'Blockly.Events', 'Blockly.Events.ToolboxItemSelect', 'Blockly.IAutoHideable', 'Blockly.IKeyboardAccessible', 'Blockly.IStyleable', 'Blockly.IToolbox', 'Blockly.Options', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.toolbox'], {'lang': 'es5'});
goog.addDependency('../../core/toolbox/toolbox_item.js', ['Blockly.ToolboxItem'], ['Blockly.IToolboxItem']);
@@ -179,7 +179,7 @@ goog.addDependency('../../core/utils/aria.js', ['Blockly.utils.aria'], []);
goog.addDependency('../../core/utils/colour.js', ['Blockly.utils.colour'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/coordinate.js', ['Blockly.utils.Coordinate'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/deprecation.js', ['Blockly.utils.deprecation'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/dom.js', ['Blockly.utils.dom'], ['Blockly.utils.Svg', 'Blockly.utils.userAgent']);
goog.addDependency('../../core/utils/dom.js', ['Blockly.utils.dom'], ['Blockly.utils.Svg', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/global.js', ['Blockly.utils.global'], []);
goog.addDependency('../../core/utils/idgenerator.js', ['Blockly.utils.IdGenerator'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/keycodes.js', ['Blockly.utils.KeyCodes'], [], {'lang': 'es6', 'module': 'goog'});
@@ -187,7 +187,7 @@ goog.addDependency('../../core/utils/math.js', ['Blockly.utils.math'], [], {'lan
goog.addDependency('../../core/utils/metrics.js', ['Blockly.utils.Metrics'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/object.js', ['Blockly.utils.object'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/rect.js', ['Blockly.utils.Rect'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/size.js', ['Blockly.utils.Size'], []);
goog.addDependency('../../core/utils/size.js', ['Blockly.utils.Size'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/string.js', ['Blockly.utils.string'], []);
goog.addDependency('../../core/utils/style.js', ['Blockly.utils.style'], ['Blockly.utils.Coordinate', 'Blockly.utils.Size'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/utils/svg.js', ['Blockly.utils.Svg'], []);
@@ -199,17 +199,16 @@ goog.addDependency('../../core/variable_map.js', ['Blockly.VariableMap'], ['Bloc
goog.addDependency('../../core/variable_model.js', ['Blockly.VariableModel'], ['Blockly.Events', 'Blockly.Events.VarCreate', 'Blockly.utils']);
goog.addDependency('../../core/variables.js', ['Blockly.Variables'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Xml', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.xml']);
goog.addDependency('../../core/variables_dynamic.js', ['Blockly.VariablesDynamic'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.utils.xml']);
goog.addDependency('../../core/warning.js', ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BubbleOpen', 'Blockly.Icon', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object']);
goog.addDependency('../../core/warning.js', ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BubbleOpen', 'Blockly.Icon', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/widgetdiv.js', ['Blockly.WidgetDiv'], ['Blockly.utils.dom']);
goog.addDependency('../../core/workspace.js', ['Blockly.Workspace'], ['Blockly.ConnectionChecker', 'Blockly.Events', 'Blockly.IASTNodeLocation', 'Blockly.Options', 'Blockly.VariableMap', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.math']);
goog.addDependency('../../core/workspace_audio.js', ['Blockly.WorkspaceAudio'], ['Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.userAgent'], {'lang': 'es5'});
goog.addDependency('../../core/workspace_comment.js', ['Blockly.WorkspaceComment'], ['Blockly.Events', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.xml']);
goog.addDependency('../../core/workspace_comment_render_svg.js', ['Blockly.WorkspaceCommentSvg.render'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom']);
goog.addDependency('../../core/workspace_comment_svg.js', ['Blockly.WorkspaceCommentSvg'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.Selected', 'Blockly.WorkspaceComment', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object']);
goog.addDependency('../../core/workspace_audio.js', ['Blockly.WorkspaceAudio'], ['Blockly.internalConstants', 'Blockly.utils.global', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/workspace_comment.js', ['Blockly.WorkspaceComment'], ['Blockly.Events', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/workspace_comment_svg.js', ['Blockly.WorkspaceCommentSvg'], ['Blockly', 'Blockly.ContextMenu', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.Selected', 'Blockly.IBoundedElement', 'Blockly.IBubble', 'Blockly.ICopyable', 'Blockly.Touch', 'Blockly.WorkspaceComment', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/workspace_drag_surface_svg.js', ['Blockly.WorkspaceDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Svg', 'Blockly.utils.dom']);
goog.addDependency('../../core/workspace_dragger.js', ['Blockly.WorkspaceDragger'], ['Blockly.utils.Coordinate']);
goog.addDependency('../../core/workspace_svg.js', ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.ComponentManager', 'Blockly.ConnectionDB', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.ThemeChange', 'Blockly.Events.ViewportChange', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.IASTNodeLocationSvg', 'Blockly.MarkerManager', 'Blockly.MetricsManager', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.TouchGesture', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es5'});
goog.addDependency('../../core/xml.js', ['Blockly.Xml'], ['Blockly.Events', 'Blockly.inputTypes', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.dom', 'Blockly.utils.xml']);
goog.addDependency('../../core/zoom_controls.js', ['Blockly.ZoomControls'], ['Blockly.ComponentManager', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.Click', 'Blockly.IPositionable', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.uiPosition', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es5'});
goog.addDependency('../../core/zoom_controls.js', ['Blockly.ZoomControls'], ['Blockly.ComponentManager', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.Click', 'Blockly.IPositionable', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.uiPosition', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('base.js', [], []);

View File

@@ -71,7 +71,6 @@
<script>
// Custom requires for the playground.
goog.require('Blockly.WorkspaceCommentSvg');
goog.require('Blockly.WorkspaceCommentSvg.render');
</script>
<script>
'use strict';

View File

@@ -77,7 +77,6 @@ goog.require('Blockly.Themes.Zelos');
// Other.
goog.require('Blockly.WorkspaceCommentSvg');
goog.require('Blockly.WorkspaceCommentSvg.render');
</script>
<script>
'use strict';