Merge branch 'goog_module' of github.com:google/blockly into module_aria

This commit is contained in:
Maribeth Bottorff
2021-08-11 17:22:12 -07:00
166 changed files with 10378 additions and 9825 deletions

View File

@@ -19,17 +19,20 @@ jobs:
- uses: actions/github-script@a3e7071a34d7e1f219a8a4de9a5e0a34d1ee1293
with:
script: |
// 2021 q3 release milestone.
// https://github.com/google/blockly/milestone/18
const milestoneNumber = 18;
// Note that pull requests are accessed through the issues API.
const issuesUpdateParams = {
owner: context.repo.owner,
repo: context.repo.repo,
// Adds the milestone
milestone: milestoneNumber,
// Note that pull requests are considered issues and "shared"
// actions for both features, like manipulating labels and
// milestones are provided within the issues API.
await github.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
// 2021 q3 release milestone.
// https://github.com/google/blockly/milestone/18
milestone: 18
})
await github.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
// Sets the labels
labels: ['type: cleanup']
}
await github.issues.update(issuesUpdateParams)
})

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,9 @@
* @fileoverview A class that manages a surface for dragging blocks. When a
* block drag is started, we move the block (and children) to a separate DOM
* element that we move around using translate3d. At the end of the drag, the
* blocks are put back in into the SVG they came from. This helps performance by
* avoiding repainting the entire SVG on every mouse move while dragging blocks.
* blocks are put back in into the SVG they came from. This helps
* performance by avoiding repainting the entire SVG on every mouse move
* while dragging blocks.
* @author picklesrus
*/
@@ -19,9 +20,9 @@ goog.module('Blockly.BlockDragSurfaceSvg');
goog.module.declareLegacyNamespace();
const Coordinate = goog.require('Blockly.utils.Coordinate');
const {G, SVG} = goog.require('Blockly.utils.Svg');
const {createSvgElement, HTML_NS, setCssTransform, SVG_NS, XLINK_NS} = goog.require('Blockly.utils.dom');
const {getRelativeXY} = goog.require('Blockly.utils');
const Svg = goog.require('Blockly.utils.Svg');
const dom = goog.require('Blockly.utils.dom');
const utils = goog.require('Blockly.utils');
/**
@@ -94,16 +95,16 @@ BlockDragSurfaceSvg.prototype.createDom = function() {
if (this.SVG_) {
return; // Already created.
}
this.SVG_ = createSvgElement(
SVG, {
'xmlns': SVG_NS,
'xmlns:html': HTML_NS,
'xmlns:xlink': XLINK_NS,
this.SVG_ = dom.createSvgElement(
Svg.SVG, {
'xmlns': dom.SVG_NS,
'xmlns:html': dom.HTML_NS,
'xmlns:xlink': dom.XLINK_NS,
'version': '1.1',
'class': 'blocklyBlockDragSurface'
},
this.container_);
this.dragGroup_ = createSvgElement(G, {}, this.SVG_);
this.dragGroup_ = dom.createSvgElement(Svg.G, {}, this.SVG_);
};
/**
@@ -157,7 +158,7 @@ BlockDragSurfaceSvg.prototype.translateSurfaceInternal_ = function() {
y = y.toFixed(0);
this.SVG_.style.display = 'block';
setCssTransform(this.SVG_, 'translate3d(' + x + 'px, ' + y + 'px, 0)');
dom.setCssTransform(this.SVG_, 'translate3d(' + x + 'px, ' + y + 'px, 0)');
};
/**
@@ -191,7 +192,7 @@ BlockDragSurfaceSvg.prototype.translateSurface = function(x, y) {
* @return {!Coordinate} Current translation of the surface.
*/
BlockDragSurfaceSvg.prototype.getSurfaceTranslation = function() {
const xy = getRelativeXY(/** @type {!SVGElement} */ (this.SVG_));
const xy = utils.getRelativeXY(/** @type {!SVGElement} */ (this.SVG_));
return new Coordinate(xy.x / this.scale_, xy.y / this.scale_);
};

View File

@@ -17,13 +17,14 @@ goog.module.declareLegacyNamespace();
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');
const IBlockDragger = goog.requireType('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 common = goog.require('Blockly.common');
const dom = goog.require('Blockly.utils.dom');
const events = goog.require('Blockly.Events');
const registry = goog.require('Blockly.registry');
@@ -332,7 +333,7 @@ BlockDragger.prototype.maybeDeleteBlock_ = function() {
// Fire a move event, so we know where to go back to for an undo.
this.fireMoveEvent_();
this.draggingBlock_.dispose(false, true);
Blockly.draggingConnections = [];
common.draggingConnections.length = 0;
return true;
}
return false;

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,8 @@
goog.provide('Blockly');
goog.require('Blockly.browserEvents');
goog.require('Blockly.clipboard');
goog.require('Blockly.common');
goog.require('Blockly.ComponentManager');
goog.require('Blockly.connectionTypes');
goog.require('Blockly.constants');
@@ -55,7 +57,6 @@ goog.require('Blockly.WorkspaceSvg');
goog.require('Blockly.Xml');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.Connection');
goog.requireType('Blockly.ICopyable');
goog.requireType('Blockly.Workspace');
@@ -71,12 +72,20 @@ goog.requireType('Blockly.Workspace');
*/
Blockly.VERSION = 'uncompiled';
/**
* The main workspace most recently used.
* Set by Blockly.WorkspaceSvg.prototype.markFocused
* @type {Blockly.Workspace}
*/
Blockly.mainWorkspace = null;
// Add a getter and setter pair for Blockly.mainWorkspace, for legacy reasons.
Object.defineProperty(Blockly, 'mainWorkspace', {
set: function(x) {
Blockly.utils.deprecation.warn(
'Blockly.mainWorkspace', 'September 2021', 'September 2022');
Blockly.common.setMainWorkspace(x);
},
get: function() {
Blockly.utils.deprecation.warn(
'Blockly.mainWorkspace', 'September 2021', 'September 2022',
'Blockly.getMainWorkspace()');
return Blockly.common.getMainWorkspace();
}
});
/**
* Currently selected block.
@@ -84,48 +93,6 @@ Blockly.mainWorkspace = null;
*/
Blockly.selected = null;
/**
* All of the connections on blocks that are currently being dragged.
* @type {!Array<!Blockly.Connection>}
* @package
*/
Blockly.draggingConnections = [];
/**
* Contents of the local clipboard.
* @type {Element}
* @private
*/
Blockly.clipboardXml_ = null;
/**
* Source of the local clipboard.
* @type {Blockly.WorkspaceSvg}
* @private
*/
Blockly.clipboardSource_ = null;
/**
* Map of types to type counts for the clipboard object and descendants.
* @type {Object}
* @private
*/
Blockly.clipboardTypeCounts_ = null;
/**
* Cached value for whether 3D is supported.
* @type {?boolean}
* @private
*/
Blockly.cache3dSupported_ = null;
/**
* Container element to render the WidgetDiv, DropDownDiv and Tooltip.
* @type {?Element}
* @package
*/
Blockly.parentContainer = null;
/**
* Returns the dimensions of the specified SVG image.
* @param {!SVGElement} svg SVG image.
@@ -136,9 +103,7 @@ Blockly.svgSize = function(svg) {
// When removing this function, remove svg.cachedWidth_ and svg.cachedHeight_
// from setCachedParentSvgSize.
Blockly.utils.deprecation.warn(
'Blockly.svgSize',
'March 2021',
'March 2022',
'Blockly.svgSize', 'March 2021', 'March 2022',
'workspace.getCachedParentSvgSize');
svg = /** @type {?} */ (svg);
return new Blockly.utils.Size(svg.cachedWidth_, svg.cachedHeight_);
@@ -195,7 +160,7 @@ Blockly.svgResize = function(workspace) {
// TODO (https://github.com/google/blockly/issues/1998) handle cases where there
// are multiple workspaces and non-main workspaces are able to accept input.
Blockly.onKeyDown = function(e) {
var mainWorkspace = Blockly.mainWorkspace;
var mainWorkspace = Blockly.common.getMainWorkspace();
if (!mainWorkspace) {
return;
}
@@ -220,7 +185,8 @@ Blockly.deleteBlock = function(selected) {
Blockly.Events.setGroup(true);
Blockly.hideChaff();
if (selected.outputConnection) {
// Do not attempt to heal rows (https://github.com/google/blockly/issues/4832)
// Do not attempt to heal rows
// (https://github.com/google/blockly/issues/4832)
selected.dispose(false, true);
} else {
selected.dispose(/* heal */ true, true);
@@ -234,39 +200,14 @@ Blockly.deleteBlock = function(selected) {
* @param {!Blockly.ICopyable} toCopy Block or Workspace Comment to be copied.
* @package
*/
Blockly.copy = function(toCopy) {
var data = toCopy.toCopyData();
if (data) {
Blockly.clipboardXml_ = data.xml;
Blockly.clipboardSource_ = data.source;
Blockly.clipboardTypeCounts_ = data.typeCounts;
}
};
Blockly.copy = Blockly.clipboard.copy;
/**
* Paste a block or workspace comment on to the main workspace.
* @return {boolean} True if the paste was successful, false otherwise.
* @package
*/
Blockly.paste = function() {
if (!Blockly.clipboardXml_) {
return false;
}
// Pasting always pastes to the main workspace, even if the copy
// started in a flyout workspace.
var workspace = Blockly.clipboardSource_;
if (workspace.isFlyout) {
workspace = workspace.targetWorkspace;
}
if (Blockly.clipboardTypeCounts_ &&
workspace.isCapacityAvailable(Blockly.clipboardTypeCounts_)) {
Blockly.Events.setGroup(true);
workspace.paste(Blockly.clipboardXml_);
Blockly.Events.setGroup(false);
return true;
}
return false;
};
Blockly.paste = Blockly.clipboard.paste;
/**
* Duplicate this block and its children, or a workspace comment.
@@ -274,19 +215,7 @@ Blockly.paste = function() {
* copied.
* @package
*/
Blockly.duplicate = function(toDuplicate) {
// Save the clipboard.
var clipboardXml = Blockly.clipboardXml_;
var clipboardSource = Blockly.clipboardSource_;
// Create a duplicate via a copy/paste operation.
Blockly.copy(toDuplicate);
toDuplicate.workspace.paste(Blockly.clipboardXml_);
// Restore the clipboard.
Blockly.clipboardXml_ = clipboardXml;
Blockly.clipboardSource_ = clipboardSource;
};
Blockly.duplicate = Blockly.clipboard.duplicate;
/**
* Cancel the native context menu, unless the focus is on an HTML input widget.
@@ -310,7 +239,7 @@ Blockly.hideChaff = function(opt_onlyClosePopups) {
Blockly.DropDownDiv.hideWithoutAnimation();
var onlyClosePopups = !!opt_onlyClosePopups;
var workspace = Blockly.getMainWorkspace();
var workspace = Blockly.common.getMainWorkspace();
var autoHideables = workspace.getComponentManager().getComponents(
Blockly.ComponentManager.Capability.AUTOHIDEABLE, true);
autoHideables.forEach(function(autoHideable) {
@@ -324,9 +253,7 @@ Blockly.hideChaff = function(opt_onlyClosePopups) {
* Blockly instances on a page.
* @return {!Blockly.Workspace} The main workspace.
*/
Blockly.getMainWorkspace = function() {
return /** @type {!Blockly.Workspace} */ (Blockly.mainWorkspace);
};
Blockly.getMainWorkspace = Blockly.common.getMainWorkspace;
/**
* Wrapper to window.alert() that app developers may override to
@@ -402,9 +329,7 @@ Blockly.defineBlocksWithJsonArray = function(jsonArray) {
'Block definition #' + i + ' in JSON array' +
' overwrites prior definition of "' + typename + '".');
}
Blockly.Blocks[typename] = {
init: Blockly.jsonInitFactory_(elem)
};
Blockly.Blocks[typename] = {init: Blockly.jsonInitFactory_(elem)};
}
}
}
@@ -430,78 +355,6 @@ Blockly.hueToHex = function(hue) {
Blockly.internalConstants.HSV_VALUE * 255);
};
/**
* Checks old colour constants are not overwritten by the host application.
* If a constant is overwritten, it prints a console warning directing the
* developer to use the equivalent Msg constant.
* @package
*/
Blockly.checkBlockColourConstants = function() {
Blockly.checkBlockColourConstant_(
'LOGIC_HUE', ['Blocks', 'logic', 'HUE'], undefined);
Blockly.checkBlockColourConstant_(
'LOGIC_HUE', ['Constants', 'Logic', 'HUE'], 210);
Blockly.checkBlockColourConstant_(
'LOOPS_HUE', ['Blocks', 'loops', 'HUE'], undefined);
Blockly.checkBlockColourConstant_(
'LOOPS_HUE', ['Constants', 'Loops', 'HUE'], 120);
Blockly.checkBlockColourConstant_(
'MATH_HUE', ['Blocks', 'math', 'HUE'], undefined);
Blockly.checkBlockColourConstant_(
'MATH_HUE', ['Constants', 'Math', 'HUE'], 230);
Blockly.checkBlockColourConstant_(
'TEXTS_HUE', ['Blocks', 'texts', 'HUE'], undefined);
Blockly.checkBlockColourConstant_(
'TEXTS_HUE', ['Constants', 'Text', 'HUE'], 160);
Blockly.checkBlockColourConstant_(
'LISTS_HUE', ['Blocks', 'lists', 'HUE'], undefined);
Blockly.checkBlockColourConstant_(
'LISTS_HUE', ['Constants', 'Lists', 'HUE'], 260);
Blockly.checkBlockColourConstant_(
'COLOUR_HUE', ['Blocks', 'colour', 'HUE'], undefined);
Blockly.checkBlockColourConstant_(
'COLOUR_HUE', ['Constants', 'Colour', 'HUE'], 20);
Blockly.checkBlockColourConstant_(
'VARIABLES_HUE', ['Blocks', 'variables', 'HUE'], undefined);
Blockly.checkBlockColourConstant_(
'VARIABLES_HUE', ['Constants', 'Variables', 'HUE'], 330);
// Blockly.Blocks.variables_dynamic.HUE never existed.
Blockly.checkBlockColourConstant_(
'VARIABLES_DYNAMIC_HUE', ['Constants', 'VariablesDynamic', 'HUE'], 310);
Blockly.checkBlockColourConstant_(
'PROCEDURES_HUE', ['Blocks', 'procedures', 'HUE'], undefined);
// Blockly.Constants.Procedures.HUE never existed.
};
/**
* Checks for a constant in the Blockly namespace, verifying it is undefined or
* has the old/original value. Prints a warning if this is not true.
* @param {string} msgName The Msg constant identifier.
* @param {!Array<string>} blocklyNamePath The name parts of the tested
* constant.
* @param {number|undefined} expectedValue The expected value of the constant.
* @private
*/
Blockly.checkBlockColourConstant_ = function(
msgName, blocklyNamePath, expectedValue) {
var namePath = 'Blockly';
var value = Blockly;
for (var i = 0; i < blocklyNamePath.length; ++i) {
namePath += '.' + blocklyNamePath[i];
if (value) {
value = value[blocklyNamePath[i]];
}
}
if (value && value !== expectedValue) {
var warningPattern = (expectedValue === undefined) ?
'%1 has been removed. Use Blockly.Msg["%2"].' :
'%1 is deprecated and unused. Override Blockly.Msg["%2"].';
var warning = warningPattern.replace('%1', namePath).replace('%2', msgName);
console.warn(warning);
}
};
/**
* Set the parent container. This is the container element that the WidgetDiv,
* DropDownDiv, and Tooltip are rendered into the first time `Blockly.inject`
@@ -509,9 +362,7 @@ Blockly.checkBlockColourConstant_ = function(
* This method is a NOP if called after the first ``Blockly.inject``.
* @param {!Element} container The container element.
*/
Blockly.setParentContainer = function(container) {
Blockly.parentContainer = container;
};
Blockly.setParentContainer = Blockly.common.setParentContainer;
/** Aliases. */

25
core/blockly_options.js Normal file
View File

@@ -0,0 +1,25 @@
/**
* @license
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Object that defines user-specified options for the workspace.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.BlocklyOptions');
goog.module.declareLegacyNamespace();
/**
* Blockly options.
* This interface is further described in
* `typings/parts/blockly-interfaces.d.ts`.
* @interface
*/
const BlocklyOptions = function() {};
exports = BlocklyOptions;

View File

@@ -10,19 +10,20 @@
*/
'use strict';
goog.provide('Blockly.browserEvents');
goog.module('Blockly.browserEvents');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Touch');
goog.require('Blockly.utils.global');
const Touch = goog.require('Blockly.Touch');
const global = goog.require('Blockly.utils.global');
/**
* Blockly opaque event data used to unbind events when using
* `Blockly.browserEvents.bind` and
* `Blockly.browserEvents.conditionalBind`.
* `bind` and `conditionalBind`.
* @typedef {!Array<!Array>}
*/
Blockly.browserEvents.Data;
let Data;
exports.Data = Data;
/**
* Bind an event handler that can be ignored if it is not part of the active
@@ -40,24 +41,24 @@ Blockly.browserEvents.Data;
* should prevent the default handler. False by default. If
* opt_noPreventDefault is provided, opt_noCaptureIdentifier must also be
* provided.
* @return {!Blockly.browserEvents.Data} Opaque data that can be passed to
* @return {!Data} Opaque data that can be passed to
* unbindEvent_.
* @public
*/
Blockly.browserEvents.conditionalBind = function(
const conditionalBind = function(
node, name, thisObject, func, opt_noCaptureIdentifier,
opt_noPreventDefault) {
var handled = false;
var wrapFunc = function(e) {
var captureIdentifier = !opt_noCaptureIdentifier;
let handled = false;
const wrapFunc = function(e) {
const captureIdentifier = !opt_noCaptureIdentifier;
// Handle each touch point separately. If the event was a mouse event, this
// will hand back an array with one element, which we're fine handling.
var events = Blockly.Touch.splitEventByTouches(e);
for (var i = 0, event; (event = events[i]); i++) {
if (captureIdentifier && !Blockly.Touch.shouldHandleEvent(event)) {
const events = Touch.splitEventByTouches(e);
for (let i = 0; i < events.length; i++) {
const event = events[i];
if (captureIdentifier && !Touch.shouldHandleEvent(event)) {
continue;
}
Blockly.Touch.setClientFromTouch(event);
Touch.setClientFromTouch(event);
if (thisObject) {
func.call(thisObject, event);
} else {
@@ -67,10 +68,10 @@ Blockly.browserEvents.conditionalBind = function(
}
};
var bindData = [];
if (Blockly.utils.global['PointerEvent'] &&
(name in Blockly.Touch.TOUCH_MAP)) {
for (var i = 0, type; (type = Blockly.Touch.TOUCH_MAP[name][i]); i++) {
const bindData = [];
if (global['PointerEvent'] && (name in Touch.TOUCH_MAP)) {
for (let i = 0; i < Touch.TOUCH_MAP[name].length; i++) {
const type = Touch.TOUCH_MAP[name][i];
node.addEventListener(type, wrapFunc, false);
bindData.push([node, type, wrapFunc]);
}
@@ -79,17 +80,18 @@ Blockly.browserEvents.conditionalBind = function(
bindData.push([node, name, wrapFunc]);
// Add equivalent touch event.
if (name in Blockly.Touch.TOUCH_MAP) {
var touchWrapFunc = function(e) {
if (name in Touch.TOUCH_MAP) {
const touchWrapFunc = function(e) {
wrapFunc(e);
// Calling preventDefault stops the browser from scrolling/zooming the
// page.
var preventDef = !opt_noPreventDefault;
const preventDef = !opt_noPreventDefault;
if (handled && preventDef) {
e.preventDefault();
}
};
for (var i = 0, type; (type = Blockly.Touch.TOUCH_MAP[name][i]); i++) {
for (let i = 0; i < Touch.TOUCH_MAP[name].length; i++) {
const type = Touch.TOUCH_MAP[name][i];
node.addEventListener(type, touchWrapFunc, false);
bindData.push([node, type, touchWrapFunc]);
}
@@ -97,6 +99,7 @@ Blockly.browserEvents.conditionalBind = function(
}
return bindData;
};
exports.conditionalBind = conditionalBind;
/**
@@ -108,12 +111,11 @@ Blockly.browserEvents.conditionalBind = function(
* @param {string} name Event name to listen to (e.g. 'mousedown').
* @param {?Object} thisObject The value of 'this' in the function.
* @param {!Function} func Function to call when event is triggered.
* @return {!Blockly.browserEvents.Data} Opaque data that can be passed to
* @return {!Data} Opaque data that can be passed to
* unbindEvent_.
* @public
*/
Blockly.browserEvents.bind = function(node, name, thisObject, func) {
var wrapFunc = function(e) {
const bind = function(node, name, thisObject, func) {
const wrapFunc = function(e) {
if (thisObject) {
func.call(thisObject, e);
} else {
@@ -121,10 +123,10 @@ Blockly.browserEvents.bind = function(node, name, thisObject, func) {
}
};
var bindData = [];
if (Blockly.utils.global['PointerEvent'] &&
(name in Blockly.Touch.TOUCH_MAP)) {
for (var i = 0, type; (type = Blockly.Touch.TOUCH_MAP[name][i]); i++) {
const bindData = [];
if (global['PointerEvent'] && (name in Touch.TOUCH_MAP)) {
for (let i = 0; i < Touch.TOUCH_MAP[name].length; i++) {
const type = Touch.TOUCH_MAP[name][i];
node.addEventListener(type, wrapFunc, false);
bindData.push([node, type, wrapFunc]);
}
@@ -133,12 +135,12 @@ Blockly.browserEvents.bind = function(node, name, thisObject, func) {
bindData.push([node, name, wrapFunc]);
// Add equivalent touch event.
if (name in Blockly.Touch.TOUCH_MAP) {
var touchWrapFunc = function(e) {
if (name in Touch.TOUCH_MAP) {
const touchWrapFunc = function(e) {
// Punt on multitouch events.
if (e.changedTouches && e.changedTouches.length == 1) {
// Map the touch event's properties to the event.
var touchPoint = e.changedTouches[0];
const touchPoint = e.changedTouches[0];
e.clientX = touchPoint.clientX;
e.clientY = touchPoint.clientY;
}
@@ -147,7 +149,8 @@ Blockly.browserEvents.bind = function(node, name, thisObject, func) {
// Stop the browser from scrolling/zooming the page.
e.preventDefault();
};
for (var i = 0, type; (type = Blockly.Touch.TOUCH_MAP[name][i]); i++) {
for (let i = 0; i < Touch.TOUCH_MAP[name].length; i++) {
const type = Touch.TOUCH_MAP[name][i];
node.addEventListener(type, touchWrapFunc, false);
bindData.push([node, type, touchWrapFunc]);
}
@@ -155,21 +158,23 @@ Blockly.browserEvents.bind = function(node, name, thisObject, func) {
}
return bindData;
};
exports.bind = bind;
/**
* Unbind one or more events event from a function call.
* @param {!Blockly.browserEvents.Data} bindData Opaque data from bindEvent_.
* @param {!Data} bindData Opaque data from bindEvent_.
* This list is emptied during the course of calling this function.
* @return {!Function} The function call.
* @public
*/
Blockly.browserEvents.unbind = function(bindData) {
const unbind = function(bindData) {
let func;
while (bindData.length) {
var bindDatum = bindData.pop();
var node = bindDatum[0];
var name = bindDatum[1];
var func = bindDatum[2];
const bindDatum = bindData.pop();
const node = bindDatum[0];
const name = bindDatum[1];
func = bindDatum[2];
node.removeEventListener(name, func, false);
}
return func;
};
exports.unbind = unbind;

View File

@@ -10,42 +10,49 @@
*/
'use strict';
goog.provide('Blockly.Bubble');
goog.module('Blockly.Bubble');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.IBubble');
goog.require('Blockly.Scrollbar');
goog.require('Blockly.Touch');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.math');
goog.require('Blockly.utils.Size');
goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.userAgent');
// TODO(#5073): Fix Blockly requires for Blockly.hideChaff()
// const Blockly = goog.require('Blockly');
/* eslint-disable-next-line no-unused-vars */
const BlockDragSurfaceSvg = goog.requireType('Blockly.BlockDragSurfaceSvg');
/* 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 IBubble = goog.requireType('Blockly.IBubble');
/* eslint-disable-next-line no-unused-vars */
const MetricsManager = goog.requireType('Blockly.MetricsManager');
const Scrollbar = goog.require('Blockly.Scrollbar');
const Size = goog.require('Blockly.utils.Size');
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 math = goog.require('Blockly.utils.math');
const userAgent = goog.require('Blockly.utils.userAgent');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.Workspace');
goog.requireType('Blockly.BlockDragSurfaceSvg');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.MetricsManager');
goog.requireType('Blockly.WorkspaceSvg');
/**
* Class for UI bubble.
* @param {!Blockly.WorkspaceSvg} workspace The workspace on which to draw the
* @param {!WorkspaceSvg} workspace The workspace on which to draw the
* bubble.
* @param {!Element} content SVG content for the bubble.
* @param {!Element} shape SVG element to avoid eclipsing.
* @param {!Blockly.utils.Coordinate} anchorXY Absolute position of bubble's
* @param {!Coordinate} anchorXY Absolute position of bubble's
* anchor point.
* @param {?number} bubbleWidth Width of bubble, or null if not resizable.
* @param {?number} bubbleHeight Height of bubble, or null if not resizable.
* @implements {Blockly.IBubble}
* @implements {IBubble}
* @constructor
*/
Blockly.Bubble = function(
const Bubble = function(
workspace, content, shape, anchorXY, bubbleWidth, bubbleHeight) {
this.workspace_ = workspace;
this.content_ = content;
@@ -67,14 +74,14 @@ Blockly.Bubble = function(
/**
* Mouse down on bubbleBack_ event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.onMouseDownBubbleWrapper_ = null;
/**
* Mouse down on resizeGroup_ event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.onMouseDownResizeWrapper_ = null;
@@ -87,20 +94,20 @@ Blockly.Bubble = function(
*/
this.disposed = false;
var angle = Blockly.Bubble.ARROW_ANGLE;
let angle = Bubble.ARROW_ANGLE;
if (this.workspace_.RTL) {
angle = -angle;
}
this.arrow_radians_ = Blockly.utils.math.toRadians(angle);
this.arrow_radians_ = math.toRadians(angle);
var canvas = workspace.getBubbleCanvas();
const canvas = workspace.getBubbleCanvas();
canvas.appendChild(this.createDom_(content, !!(bubbleWidth && bubbleHeight)));
this.setAnchorLocation(anchorXY);
if (!bubbleWidth || !bubbleHeight) {
var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox();
bubbleWidth = bBox.width + 2 * Blockly.Bubble.BORDER_WIDTH;
bubbleHeight = bBox.height + 2 * Blockly.Bubble.BORDER_WIDTH;
const bBox = /** @type {SVGLocatable} */ (this.content_).getBBox();
bubbleWidth = bBox.width + 2 * Bubble.BORDER_WIDTH;
bubbleHeight = bBox.height + 2 * Bubble.BORDER_WIDTH;
}
this.setBubbleSize(bubbleWidth, bubbleHeight);
@@ -113,55 +120,55 @@ Blockly.Bubble = function(
/**
* Width of the border around the bubble.
*/
Blockly.Bubble.BORDER_WIDTH = 6;
Bubble.BORDER_WIDTH = 6;
/**
* Determines the thickness of the base of the arrow in relation to the size
* of the bubble. Higher numbers result in thinner arrows.
*/
Blockly.Bubble.ARROW_THICKNESS = 5;
Bubble.ARROW_THICKNESS = 5;
/**
* The number of degrees that the arrow bends counter-clockwise.
*/
Blockly.Bubble.ARROW_ANGLE = 20;
Bubble.ARROW_ANGLE = 20;
/**
* The sharpness of the arrow's bend. Higher numbers result in smoother arrows.
*/
Blockly.Bubble.ARROW_BEND = 4;
Bubble.ARROW_BEND = 4;
/**
* Distance between arrow point and anchor point.
*/
Blockly.Bubble.ANCHOR_RADIUS = 8;
Bubble.ANCHOR_RADIUS = 8;
/**
* Mouse up event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
Blockly.Bubble.onMouseUpWrapper_ = null;
Bubble.onMouseUpWrapper_ = null;
/**
* Mouse move event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
Blockly.Bubble.onMouseMoveWrapper_ = null;
Bubble.onMouseMoveWrapper_ = null;
/**
* Stop binding to the global mouseup and mousemove events.
* @private
*/
Blockly.Bubble.unbindDragEvents_ = function() {
if (Blockly.Bubble.onMouseUpWrapper_) {
Blockly.browserEvents.unbind(Blockly.Bubble.onMouseUpWrapper_);
Blockly.Bubble.onMouseUpWrapper_ = null;
Bubble.unbindDragEvents_ = function() {
if (Bubble.onMouseUpWrapper_) {
browserEvents.unbind(Bubble.onMouseUpWrapper_);
Bubble.onMouseUpWrapper_ = null;
}
if (Blockly.Bubble.onMouseMoveWrapper_) {
Blockly.browserEvents.unbind(Blockly.Bubble.onMouseMoveWrapper_);
Blockly.Bubble.onMouseMoveWrapper_ = null;
if (Bubble.onMouseMoveWrapper_) {
browserEvents.unbind(Bubble.onMouseMoveWrapper_);
Bubble.onMouseMoveWrapper_ = null;
}
};
@@ -170,23 +177,23 @@ Blockly.Bubble.unbindDragEvents_ = function() {
* @param {!Event} _e Mouse up event.
* @private
*/
Blockly.Bubble.bubbleMouseUp_ = function(_e) {
Blockly.Touch.clearTouchIdentifier();
Blockly.Bubble.unbindDragEvents_();
Bubble.bubbleMouseUp_ = function(_e) {
Touch.clearTouchIdentifier();
Bubble.unbindDragEvents_();
};
/**
* Flag to stop incremental rendering during construction.
* @private
*/
Blockly.Bubble.prototype.rendered_ = false;
Bubble.prototype.rendered_ = false;
/**
* Absolute coordinate of anchor point, in workspace coordinates.
* @type {Blockly.utils.Coordinate}
* @type {Coordinate}
* @private
*/
Blockly.Bubble.prototype.anchorXY_ = null;
Bubble.prototype.anchorXY_ = null;
/**
* Relative X coordinate of bubble with respect to the anchor's centre,
@@ -194,32 +201,32 @@ Blockly.Bubble.prototype.anchorXY_ = null;
* In RTL mode the initial value is negated.
* @private
*/
Blockly.Bubble.prototype.relativeLeft_ = 0;
Bubble.prototype.relativeLeft_ = 0;
/**
* Relative Y coordinate of bubble with respect to the anchor's centre, in
* workspace units.
* @private
*/
Blockly.Bubble.prototype.relativeTop_ = 0;
Bubble.prototype.relativeTop_ = 0;
/**
* Width of bubble, in workspace units.
* @private
*/
Blockly.Bubble.prototype.width_ = 0;
Bubble.prototype.width_ = 0;
/**
* Height of bubble, in workspace units.
* @private
*/
Blockly.Bubble.prototype.height_ = 0;
Bubble.prototype.height_ = 0;
/**
* Automatically position and reposition the bubble.
* @private
*/
Blockly.Bubble.prototype.autoLayout_ = true;
Bubble.prototype.autoLayout_ = true;
/**
* Create the bubble's DOM.
@@ -228,7 +235,7 @@ Blockly.Bubble.prototype.autoLayout_ = true;
* @return {!SVGElement} The bubble's SVG group.
* @private
*/
Blockly.Bubble.prototype.createDom_ = function(content, hasResize) {
Bubble.prototype.createDom_ = function(content, hasResize) {
/* Create the bubble. Here's the markup that will be generated:
<g>
<g filter="url(#blocklyEmbossFilter837493)">
@@ -243,42 +250,39 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) {
[...content goes here...]
</g>
*/
this.bubbleGroup_ =
Blockly.utils.dom.createSvgElement(Blockly.utils.Svg.G, {}, null);
var filter = {
this.bubbleGroup_ = dom.createSvgElement(Svg.G, {}, null);
let filter = {
'filter': 'url(#' +
this.workspace_.getRenderer().getConstants().embossFilterId + ')'
};
if (Blockly.utils.userAgent.JAVA_FX) {
if (userAgent.JAVA_FX) {
// Multiple reports that JavaFX can't handle filters.
// https://github.com/google/blockly/issues/99
filter = {};
}
var bubbleEmboss = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G, filter, this.bubbleGroup_);
this.bubbleArrow_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATH, {}, bubbleEmboss);
this.bubbleBack_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT, {
const bubbleEmboss = dom.createSvgElement(Svg.G, filter, this.bubbleGroup_);
this.bubbleArrow_ = dom.createSvgElement(Svg.PATH, {}, bubbleEmboss);
this.bubbleBack_ = dom.createSvgElement(
Svg.RECT, {
'class': 'blocklyDraggable',
'x': 0,
'y': 0,
'rx': Blockly.Bubble.BORDER_WIDTH,
'ry': Blockly.Bubble.BORDER_WIDTH
'rx': Bubble.BORDER_WIDTH,
'ry': Bubble.BORDER_WIDTH
},
bubbleEmboss);
if (hasResize) {
this.resizeGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G,
this.resizeGroup_ = dom.createSvgElement(
Svg.G,
{'class': this.workspace_.RTL ? 'blocklyResizeSW' : 'blocklyResizeSE'},
this.bubbleGroup_);
var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH;
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.POLYGON,
const resizeSize = 2 * Bubble.BORDER_WIDTH;
dom.createSvgElement(
Svg.POLYGON,
{'points': '0,x x,x x,0'.replace(/x/g, resizeSize.toString())},
this.resizeGroup_);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE, {
dom.createSvgElement(
Svg.LINE, {
'class': 'blocklyResizeLine',
'x1': resizeSize / 3,
'y1': resizeSize - 1,
@@ -286,8 +290,8 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) {
'y2': resizeSize / 3
},
this.resizeGroup_);
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.LINE, {
dom.createSvgElement(
Svg.LINE, {
'class': 'blocklyResizeLine',
'x1': resizeSize * 2 / 3,
'y1': resizeSize - 1,
@@ -300,10 +304,10 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) {
}
if (!this.workspace_.options.readOnly) {
this.onMouseDownBubbleWrapper_ = Blockly.browserEvents.conditionalBind(
this.onMouseDownBubbleWrapper_ = browserEvents.conditionalBind(
this.bubbleBack_, 'mousedown', this, this.bubbleMouseDown_);
if (this.resizeGroup_) {
this.onMouseDownResizeWrapper_ = Blockly.browserEvents.conditionalBind(
this.onMouseDownResizeWrapper_ = browserEvents.conditionalBind(
this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_);
}
}
@@ -315,7 +319,7 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) {
* Return the root node of the bubble's SVG group.
* @return {!SVGElement} The root SVG node of the bubble's group.
*/
Blockly.Bubble.prototype.getSvgRoot = function() {
Bubble.prototype.getSvgRoot = function() {
return this.bubbleGroup_;
};
@@ -323,7 +327,7 @@ Blockly.Bubble.prototype.getSvgRoot = function() {
* Expose the block's ID on the bubble's top-level SVG group.
* @param {string} id ID of block.
*/
Blockly.Bubble.prototype.setSvgId = function(id) {
Bubble.prototype.setSvgId = function(id) {
if (this.bubbleGroup_.dataset) {
this.bubbleGroup_.dataset['blockId'] = id;
}
@@ -334,8 +338,8 @@ Blockly.Bubble.prototype.setSvgId = function(id) {
* @param {!Event} e Mouse down event.
* @private
*/
Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) {
var gesture = this.workspace_.getGesture(e);
Bubble.prototype.bubbleMouseDown_ = function(e) {
const gesture = this.workspace_.getGesture(e);
if (gesture) {
gesture.handleBubbleStart(e, this);
}
@@ -346,7 +350,7 @@ Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) {
* @param {!Event} _e Mouse event.
* @package
*/
Blockly.Bubble.prototype.showContextMenu = function(_e) {
Bubble.prototype.showContextMenu = function(_e) {
// NOP on bubbles, but used by the bubble dragger to pass events to
// workspace comments.
};
@@ -356,7 +360,7 @@ Blockly.Bubble.prototype.showContextMenu = function(_e) {
* @return {boolean} True if deletable.
* @package
*/
Blockly.Bubble.prototype.isDeletable = function() {
Bubble.prototype.isDeletable = function() {
return false;
};
@@ -365,7 +369,7 @@ Blockly.Bubble.prototype.isDeletable = function() {
* @param {boolean} _enable True if the bubble is about to be deleted, false
* otherwise.
*/
Blockly.Bubble.prototype.setDeleteStyle = function(_enable) {
Bubble.prototype.setDeleteStyle = function(_enable) {
// NOP if bubble is not deletable.
};
@@ -374,10 +378,10 @@ Blockly.Bubble.prototype.setDeleteStyle = function(_enable) {
* @param {!Event} e Mouse down event.
* @private
*/
Blockly.Bubble.prototype.resizeMouseDown_ = function(e) {
Bubble.prototype.resizeMouseDown_ = function(e) {
this.promote();
Blockly.Bubble.unbindDragEvents_();
if (Blockly.utils.isRightButton(e)) {
Bubble.unbindDragEvents_();
if (utils.isRightButton(e)) {
// No right-click.
e.stopPropagation();
return;
@@ -385,12 +389,12 @@ Blockly.Bubble.prototype.resizeMouseDown_ = function(e) {
// Left-click (or middle click)
this.workspace_.startDrag(
e,
new Blockly.utils.Coordinate(
new Coordinate(
this.workspace_.RTL ? -this.width_ : this.width_, this.height_));
Blockly.Bubble.onMouseUpWrapper_ = Blockly.browserEvents.conditionalBind(
document, 'mouseup', this, Blockly.Bubble.bubbleMouseUp_);
Blockly.Bubble.onMouseMoveWrapper_ = Blockly.browserEvents.conditionalBind(
Bubble.onMouseUpWrapper_ = browserEvents.conditionalBind(
document, 'mouseup', this, Bubble.bubbleMouseUp_);
Bubble.onMouseMoveWrapper_ = browserEvents.conditionalBind(
document, 'mousemove', this, this.resizeMouseMove_);
Blockly.hideChaff();
// This event has been handled. No need to bubble up to the document.
@@ -402,9 +406,9 @@ Blockly.Bubble.prototype.resizeMouseDown_ = function(e) {
* @param {!Event} e Mouse move event.
* @private
*/
Blockly.Bubble.prototype.resizeMouseMove_ = function(e) {
Bubble.prototype.resizeMouseMove_ = function(e) {
this.autoLayout_ = false;
var newXY = this.workspace_.moveDrag(e);
const newXY = this.workspace_.moveDrag(e);
this.setBubbleSize(this.workspace_.RTL ? -newXY.x : newXY.x, newXY.y);
if (this.workspace_.RTL) {
// RTL requires the bubble to move its left edge.
@@ -416,7 +420,7 @@ Blockly.Bubble.prototype.resizeMouseMove_ = function(e) {
* Register a function as a callback event for when the bubble is resized.
* @param {!Function} callback The function to call on resize.
*/
Blockly.Bubble.prototype.registerResizeEvent = function(callback) {
Bubble.prototype.registerResizeEvent = function(callback) {
this.resizeCallback_ = callback;
};
@@ -424,7 +428,7 @@ Blockly.Bubble.prototype.registerResizeEvent = function(callback) {
* Register a function as a callback event for when the bubble is moved.
* @param {!Function} callback The function to call on move.
*/
Blockly.Bubble.prototype.registerMoveEvent = function(callback) {
Bubble.prototype.registerMoveEvent = function(callback) {
this.moveCallback_ = callback;
};
@@ -433,8 +437,8 @@ Blockly.Bubble.prototype.registerMoveEvent = function(callback) {
* @return {boolean} Whether or not the bubble has been moved.
* @package
*/
Blockly.Bubble.prototype.promote = function() {
var svgGroup = this.bubbleGroup_.parentNode;
Bubble.prototype.promote = function() {
const svgGroup = this.bubbleGroup_.parentNode;
if (svgGroup.lastChild !== this.bubbleGroup_) {
svgGroup.appendChild(this.bubbleGroup_);
return true;
@@ -445,9 +449,9 @@ Blockly.Bubble.prototype.promote = function() {
/**
* Notification that the anchor has moved.
* Update the arrow and bubble accordingly.
* @param {!Blockly.utils.Coordinate} xy Absolute location.
* @param {!Coordinate} xy Absolute location.
*/
Blockly.Bubble.prototype.setAnchorLocation = function(xy) {
Bubble.prototype.setAnchorLocation = function(xy) {
this.anchorXY_ = xy;
if (this.rendered_) {
this.positionBubble_();
@@ -458,34 +462,36 @@ Blockly.Bubble.prototype.setAnchorLocation = function(xy) {
* Position the bubble so that it does not fall off-screen.
* @private
*/
Blockly.Bubble.prototype.layoutBubble_ = function() {
Bubble.prototype.layoutBubble_ = function() {
// Get the metrics in workspace units.
var viewMetrics = this.workspace_.getMetricsManager().getViewMetrics(true);
const viewMetrics = this.workspace_.getMetricsManager().getViewMetrics(true);
var optimalLeft = this.getOptimalRelativeLeft_(viewMetrics);
var optimalTop = this.getOptimalRelativeTop_(viewMetrics);
var bbox = this.shape_.getBBox();
const optimalLeft = this.getOptimalRelativeLeft_(viewMetrics);
const optimalTop = this.getOptimalRelativeTop_(viewMetrics);
const bbox = this.shape_.getBBox();
var topPosition = {
const topPosition = {
x: optimalLeft,
y: -this.height_ -
this.workspace_.getRenderer().getConstants().MIN_BLOCK_HEIGHT
};
var startPosition = {x: -this.width_ - 30, y: optimalTop};
var endPosition = {x: bbox.width, y: optimalTop};
var bottomPosition = {x: optimalLeft, y: bbox.height};
const startPosition = {x: -this.width_ - 30, y: optimalTop};
const endPosition = {x: bbox.width, y: optimalTop};
const bottomPosition = {x: optimalLeft, y: bbox.height};
var closerPosition = bbox.width < bbox.height ? endPosition : bottomPosition;
var fartherPosition = bbox.width < bbox.height ? bottomPosition : endPosition;
const closerPosition =
bbox.width < bbox.height ? endPosition : bottomPosition;
const fartherPosition =
bbox.width < bbox.height ? bottomPosition : endPosition;
var topPositionOverlap = this.getOverlap_(topPosition, viewMetrics);
var startPositionOverlap = this.getOverlap_(startPosition, viewMetrics);
var closerPositionOverlap = this.getOverlap_(closerPosition, viewMetrics);
var fartherPositionOverlap = this.getOverlap_(fartherPosition, viewMetrics);
const topPositionOverlap = this.getOverlap_(topPosition, viewMetrics);
const startPositionOverlap = this.getOverlap_(startPosition, viewMetrics);
const closerPositionOverlap = this.getOverlap_(closerPosition, viewMetrics);
const fartherPositionOverlap = this.getOverlap_(fartherPosition, viewMetrics);
// Set the position to whichever position shows the most of the bubble,
// with tiebreaks going in the order: top > start > close > far.
var mostOverlap = Math.max(
const mostOverlap = Math.max(
topPositionOverlap, startPositionOverlap, closerPositionOverlap,
fartherPositionOverlap);
if (topPositionOverlap == mostOverlap) {
@@ -515,20 +521,23 @@ Blockly.Bubble.prototype.layoutBubble_ = function() {
* workspace (what percentage of the bubble is visible).
* @param {!{x: number, y: number}} relativeMin The position of the top-left
* corner of the bubble relative to the anchor point.
* @param {!Blockly.MetricsManager.ContainerRegion} viewMetrics The view metrics
* @param {!MetricsManager.ContainerRegion} viewMetrics The view metrics
* of the workspace the bubble will appear in.
* @return {number} The percentage of the bubble that is visible.
* @private
*/
Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, viewMetrics) {
Bubble.prototype.getOverlap_ = function(relativeMin, viewMetrics) {
// The position of the top-left corner of the bubble in workspace units.
var bubbleMin = {
const bubbleMin = {
x: this.workspace_.RTL ? (this.anchorXY_.x - relativeMin.x - this.width_) :
(relativeMin.x + this.anchorXY_.x),
y: relativeMin.y + this.anchorXY_.y
};
// The position of the bottom-right corner of the bubble in workspace units.
var bubbleMax = {x: bubbleMin.x + this.width_, y: bubbleMin.y + this.height_};
const bubbleMax = {
x: bubbleMin.x + this.width_,
y: bubbleMin.y + this.height_
};
// We could adjust these values to account for the scrollbars, but the
// bubbles should have been adjusted to not collide with them anyway, so
@@ -536,16 +545,16 @@ Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, viewMetrics) {
// calculation.
// The position of the top-left corner of the workspace.
var workspaceMin = {x: viewMetrics.left, y: viewMetrics.top};
const workspaceMin = {x: viewMetrics.left, y: viewMetrics.top};
// The position of the bottom-right corner of the workspace.
var workspaceMax = {
const workspaceMax = {
x: viewMetrics.left + viewMetrics.width,
y: viewMetrics.top + viewMetrics.height
};
var overlapWidth = Math.min(bubbleMax.x, workspaceMax.x) -
const overlapWidth = Math.min(bubbleMax.x, workspaceMax.x) -
Math.max(bubbleMin.x, workspaceMin.x);
var overlapHeight = Math.min(bubbleMax.y, workspaceMax.y) -
const overlapHeight = Math.min(bubbleMax.y, workspaceMax.y) -
Math.max(bubbleMin.y, workspaceMin.y);
return Math.max(
0,
@@ -557,14 +566,14 @@ Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, viewMetrics) {
* Calculate what the optimal horizontal position of the top-left corner of the
* bubble is (relative to the anchor point) so that the most area of the
* bubble is shown.
* @param {!Blockly.MetricsManager.ContainerRegion} viewMetrics The view metrics
* @param {!MetricsManager.ContainerRegion} viewMetrics The view metrics
* of the workspace the bubble will appear in.
* @return {number} The optimal horizontal position of the top-left corner
* of the bubble.
* @private
*/
Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(viewMetrics) {
var relativeLeft = -this.width_ / 4;
Bubble.prototype.getOptimalRelativeLeft_ = function(viewMetrics) {
let relativeLeft = -this.width_ / 4;
// No amount of sliding left or right will give us a better overlap.
if (this.width_ > viewMetrics.width) {
@@ -573,24 +582,14 @@ Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(viewMetrics) {
if (this.workspace_.RTL) {
// Bubble coordinates are flipped in RTL.
var bubbleRight = this.anchorXY_.x - relativeLeft;
var bubbleLeft = bubbleRight - this.width_;
const bubbleRight = this.anchorXY_.x - relativeLeft;
const bubbleLeft = bubbleRight - this.width_;
var workspaceRight = viewMetrics.left + viewMetrics.width;
var workspaceLeft = viewMetrics.left +
const workspaceRight = viewMetrics.left + viewMetrics.width;
const workspaceLeft = viewMetrics.left +
// Thickness in workspace units.
(Blockly.Scrollbar.scrollbarThickness / this.workspace_.scale);
} else {
var bubbleLeft = relativeLeft + this.anchorXY_.x;
var bubbleRight = bubbleLeft + this.width_;
(Scrollbar.scrollbarThickness / this.workspace_.scale);
var workspaceLeft = viewMetrics.left;
var workspaceRight = viewMetrics.left + viewMetrics.width -
// Thickness in workspace units.
(Blockly.Scrollbar.scrollbarThickness / this.workspace_.scale);
}
if (this.workspace_.RTL) {
if (bubbleLeft < workspaceLeft) {
// Slide the bubble right until it is onscreen.
relativeLeft = -(workspaceLeft - this.anchorXY_.x + this.width_);
@@ -599,6 +598,14 @@ Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(viewMetrics) {
relativeLeft = -(workspaceRight - this.anchorXY_.x);
}
} else {
const bubbleLeft = relativeLeft + this.anchorXY_.x;
const bubbleRight = bubbleLeft + this.width_;
const workspaceLeft = viewMetrics.left;
const workspaceRight = viewMetrics.left + viewMetrics.width -
// Thickness in workspace units.
(Scrollbar.scrollbarThickness / this.workspace_.scale);
if (bubbleLeft < workspaceLeft) {
// Slide the bubble right until it is onscreen.
relativeLeft = workspaceLeft - this.anchorXY_.x;
@@ -615,28 +622,28 @@ Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(viewMetrics) {
* Calculate what the optimal vertical position of the top-left corner of
* the bubble is (relative to the anchor point) so that the most area of the
* bubble is shown.
* @param {!Blockly.MetricsManager.ContainerRegion} viewMetrics The view metrics
* @param {!MetricsManager.ContainerRegion} viewMetrics The view metrics
* of the workspace the bubble will appear in.
* @return {number} The optimal vertical position of the top-left corner
* of the bubble.
* @private
*/
Blockly.Bubble.prototype.getOptimalRelativeTop_ = function(viewMetrics) {
var relativeTop = -this.height_ / 4;
Bubble.prototype.getOptimalRelativeTop_ = function(viewMetrics) {
let relativeTop = -this.height_ / 4;
// No amount of sliding up or down will give us a better overlap.
if (this.height_ > viewMetrics.height) {
return relativeTop;
}
var bubbleTop = this.anchorXY_.y + relativeTop;
var bubbleBottom = bubbleTop + this.height_;
var workspaceTop = viewMetrics.top;
var workspaceBottom = viewMetrics.top + viewMetrics.height -
const bubbleTop = this.anchorXY_.y + relativeTop;
const bubbleBottom = bubbleTop + this.height_;
const workspaceTop = viewMetrics.top;
const workspaceBottom = viewMetrics.top + viewMetrics.height -
// Thickness in workspace units.
(Blockly.Scrollbar.scrollbarThickness / this.workspace_.scale);
(Scrollbar.scrollbarThickness / this.workspace_.scale);
var anchorY = this.anchorXY_.y;
const anchorY = this.anchorXY_.y;
if (bubbleTop < workspaceTop) {
// Slide the bubble down until it is onscreen.
relativeTop = workspaceTop - anchorY;
@@ -652,14 +659,14 @@ Blockly.Bubble.prototype.getOptimalRelativeTop_ = function(viewMetrics) {
* Move the bubble to a location relative to the anchor's centre.
* @private
*/
Blockly.Bubble.prototype.positionBubble_ = function() {
var left = this.anchorXY_.x;
Bubble.prototype.positionBubble_ = function() {
let left = this.anchorXY_.x;
if (this.workspace_.RTL) {
left -= this.relativeLeft_ + this.width_;
} else {
left += this.relativeLeft_;
}
var top = this.relativeTop_ + this.anchorXY_.y;
const top = this.relativeTop_ + this.anchorXY_.y;
this.moveTo(left, top);
};
@@ -669,7 +676,7 @@ Blockly.Bubble.prototype.positionBubble_ = function() {
* @param {number} y The y position to move to.
* @package
*/
Blockly.Bubble.prototype.moveTo = function(x, y) {
Bubble.prototype.moveTo = function(x, y) {
this.bubbleGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')');
};
@@ -678,7 +685,7 @@ Blockly.Bubble.prototype.moveTo = function(x, y) {
* @param {boolean} adding True if adding, false if removing.
* @package
*/
Blockly.Bubble.prototype.setDragging = function(adding) {
Bubble.prototype.setDragging = function(adding) {
if (!adding && this.moveCallback_) {
this.moveCallback_();
}
@@ -686,10 +693,10 @@ Blockly.Bubble.prototype.setDragging = function(adding) {
/**
* Get the dimensions of this bubble.
* @return {!Blockly.utils.Size} The height and width of the bubble.
* @return {!Size} The height and width of the bubble.
*/
Blockly.Bubble.prototype.getBubbleSize = function() {
return new Blockly.utils.Size(this.width_, this.height_);
Bubble.prototype.getBubbleSize = function() {
return new Size(this.width_, this.height_);
};
/**
@@ -697,8 +704,8 @@ Blockly.Bubble.prototype.getBubbleSize = function() {
* @param {number} width Width of the bubble.
* @param {number} height Height of the bubble.
*/
Blockly.Bubble.prototype.setBubbleSize = function(width, height) {
var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH;
Bubble.prototype.setBubbleSize = function(width, height) {
const doubleBorderWidth = 2 * Bubble.BORDER_WIDTH;
// Minimum size of a bubble.
width = Math.max(width, doubleBorderWidth + 45);
height = Math.max(height, doubleBorderWidth + 20);
@@ -709,7 +716,7 @@ Blockly.Bubble.prototype.setBubbleSize = function(width, height) {
if (this.resizeGroup_) {
if (this.workspace_.RTL) {
// Mirror the resize group.
var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH;
const resizeSize = 2 * Bubble.BORDER_WIDTH;
this.resizeGroup_.setAttribute(
'transform',
'translate(' + resizeSize + ',' + (height - doubleBorderWidth) +
@@ -737,64 +744,62 @@ Blockly.Bubble.prototype.setBubbleSize = function(width, height) {
* Draw the arrow between the bubble and the origin.
* @private
*/
Blockly.Bubble.prototype.renderArrow_ = function() {
var steps = [];
Bubble.prototype.renderArrow_ = function() {
const steps = [];
// Find the relative coordinates of the center of the bubble.
var relBubbleX = this.width_ / 2;
var relBubbleY = this.height_ / 2;
const relBubbleX = this.width_ / 2;
const relBubbleY = this.height_ / 2;
// Find the relative coordinates of the center of the anchor.
var relAnchorX = -this.relativeLeft_;
var relAnchorY = -this.relativeTop_;
let relAnchorX = -this.relativeLeft_;
let relAnchorY = -this.relativeTop_;
if (relBubbleX == relAnchorX && relBubbleY == relAnchorY) {
// Null case. Bubble is directly on top of the anchor.
// Short circuit this rather than wade through divide by zeros.
steps.push('M ' + relBubbleX + ',' + relBubbleY);
} else {
// Compute the angle of the arrow's line.
var rise = relAnchorY - relBubbleY;
var run = relAnchorX - relBubbleX;
const rise = relAnchorY - relBubbleY;
let run = relAnchorX - relBubbleX;
if (this.workspace_.RTL) {
run *= -1;
}
var hypotenuse = Math.sqrt(rise * rise + run * run);
var angle = Math.acos(run / hypotenuse);
const hypotenuse = Math.sqrt(rise * rise + run * run);
let angle = Math.acos(run / hypotenuse);
if (rise < 0) {
angle = 2 * Math.PI - angle;
}
// Compute a line perpendicular to the arrow.
var rightAngle = angle + Math.PI / 2;
let rightAngle = angle + Math.PI / 2;
if (rightAngle > Math.PI * 2) {
rightAngle -= Math.PI * 2;
}
var rightRise = Math.sin(rightAngle);
var rightRun = Math.cos(rightAngle);
const rightRise = Math.sin(rightAngle);
const rightRun = Math.cos(rightAngle);
// Calculate the thickness of the base of the arrow.
var bubbleSize = this.getBubbleSize();
var thickness =
(bubbleSize.width + bubbleSize.height) / Blockly.Bubble.ARROW_THICKNESS;
const bubbleSize = this.getBubbleSize();
let thickness =
(bubbleSize.width + bubbleSize.height) / Bubble.ARROW_THICKNESS;
thickness = Math.min(thickness, bubbleSize.width, bubbleSize.height) / 4;
// Back the tip of the arrow off of the anchor.
var backoffRatio = 1 - Blockly.Bubble.ANCHOR_RADIUS / hypotenuse;
const backoffRatio = 1 - Bubble.ANCHOR_RADIUS / hypotenuse;
relAnchorX = relBubbleX + backoffRatio * run;
relAnchorY = relBubbleY + backoffRatio * rise;
// Coordinates for the base of the arrow.
var baseX1 = relBubbleX + thickness * rightRun;
var baseY1 = relBubbleY + thickness * rightRise;
var baseX2 = relBubbleX - thickness * rightRun;
var baseY2 = relBubbleY - thickness * rightRise;
const baseX1 = relBubbleX + thickness * rightRun;
const baseY1 = relBubbleY + thickness * rightRise;
const baseX2 = relBubbleX - thickness * rightRun;
const baseY2 = relBubbleY - thickness * rightRise;
// Distortion to curve the arrow.
var swirlAngle = angle + this.arrow_radians_;
let swirlAngle = angle + this.arrow_radians_;
if (swirlAngle > Math.PI * 2) {
swirlAngle -= Math.PI * 2;
}
var swirlRise =
Math.sin(swirlAngle) * hypotenuse / Blockly.Bubble.ARROW_BEND;
var swirlRun =
Math.cos(swirlAngle) * hypotenuse / Blockly.Bubble.ARROW_BEND;
const swirlRise = Math.sin(swirlAngle) * hypotenuse / Bubble.ARROW_BEND;
const swirlRun = Math.cos(swirlAngle) * hypotenuse / Bubble.ARROW_BEND;
steps.push('M' + baseX1 + ',' + baseY1);
steps.push(
@@ -812,7 +817,7 @@ Blockly.Bubble.prototype.renderArrow_ = function() {
* Change the colour of a bubble.
* @param {string} hexColour Hex code of colour.
*/
Blockly.Bubble.prototype.setColour = function(hexColour) {
Bubble.prototype.setColour = function(hexColour) {
this.bubbleBack_.setAttribute('fill', hexColour);
this.bubbleArrow_.setAttribute('fill', hexColour);
};
@@ -820,28 +825,28 @@ Blockly.Bubble.prototype.setColour = function(hexColour) {
/**
* Dispose of this bubble.
*/
Blockly.Bubble.prototype.dispose = function() {
Bubble.prototype.dispose = function() {
if (this.onMouseDownBubbleWrapper_) {
Blockly.browserEvents.unbind(this.onMouseDownBubbleWrapper_);
browserEvents.unbind(this.onMouseDownBubbleWrapper_);
}
if (this.onMouseDownResizeWrapper_) {
Blockly.browserEvents.unbind(this.onMouseDownResizeWrapper_);
browserEvents.unbind(this.onMouseDownResizeWrapper_);
}
Blockly.Bubble.unbindDragEvents_();
Blockly.utils.dom.removeNode(this.bubbleGroup_);
Bubble.unbindDragEvents_();
dom.removeNode(this.bubbleGroup_);
this.disposed = true;
};
/**
* Move this bubble during a drag, taking into account whether or not there is
* a drag surface.
* @param {Blockly.BlockDragSurfaceSvg} dragSurface The surface that carries
* @param {BlockDragSurfaceSvg} dragSurface The surface that carries
* rendered items during a drag, or null if no drag surface is in use.
* @param {!Blockly.utils.Coordinate} newLoc The location to translate to, in
* @param {!Coordinate} newLoc The location to translate to, in
* workspace coordinates.
* @package
*/
Blockly.Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) {
Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) {
if (dragSurface) {
dragSurface.translateSurface(newLoc.x, newLoc.y);
} else {
@@ -859,10 +864,10 @@ Blockly.Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) {
/**
* Return the coordinates of the top-left corner of this bubble's body relative
* to the drawing surface's origin (0,0), in workspace units.
* @return {!Blockly.utils.Coordinate} Object with .x and .y properties.
* @return {!Coordinate} Object with .x and .y properties.
*/
Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() {
return new Blockly.utils.Coordinate(
Bubble.prototype.getRelativeToSurfaceXY = function() {
return new Coordinate(
this.workspace_.RTL ?
-this.relativeLeft_ + this.anchorXY_.x - this.width_ :
this.anchorXY_.x + this.relativeLeft_,
@@ -877,7 +882,7 @@ Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() {
* otherwise.
* @package
*/
Blockly.Bubble.prototype.setAutoLayout = function(enable) {
Bubble.prototype.setAutoLayout = function(enable) {
this.autoLayout_ = enable;
};
@@ -887,19 +892,18 @@ Blockly.Bubble.prototype.setAutoLayout = function(enable) {
* @return {!SVGTextElement} The top-level node of the text.
* @package
*/
Blockly.Bubble.textToDom = function(text) {
var paragraph = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.TEXT, {
Bubble.textToDom = function(text) {
const paragraph = dom.createSvgElement(
Svg.TEXT, {
'class': 'blocklyText blocklyBubbleText blocklyNoPointerEvents',
'y': Blockly.Bubble.BORDER_WIDTH
'y': Bubble.BORDER_WIDTH
},
null);
var lines = text.split('\n');
for (var i = 0; i < lines.length; i++) {
var tspanElement = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.TSPAN,
{'dy': '1em', 'x': Blockly.Bubble.BORDER_WIDTH}, paragraph);
var textNode = document.createTextNode(lines[i]);
const lines = text.split('\n');
for (let i = 0; i < lines.length; i++) {
const tspanElement = dom.createSvgElement(
Svg.TSPAN, {'dy': '1em', 'x': Bubble.BORDER_WIDTH}, paragraph);
const textNode = document.createTextNode(lines[i]);
tspanElement.appendChild(textNode);
}
return paragraph;
@@ -909,28 +913,29 @@ Blockly.Bubble.textToDom = function(text) {
* Creates a bubble that can not be edited.
* @param {!SVGTextElement} paragraphElement The text element for the non
* editable bubble.
* @param {!Blockly.BlockSvg} block The block that the bubble is attached to.
* @param {!Blockly.utils.Coordinate} iconXY The coordinate of the icon.
* @return {!Blockly.Bubble} The non editable bubble.
* @param {!BlockSvg} block The block that the bubble is attached to.
* @param {!Coordinate} iconXY The coordinate of the icon.
* @return {!Bubble} The non editable bubble.
* @package
*/
Blockly.Bubble.createNonEditableBubble = function(
paragraphElement, block, iconXY) {
var bubble = new Blockly.Bubble(
/** @type {!Blockly.WorkspaceSvg} */ (block.workspace), paragraphElement,
Bubble.createNonEditableBubble = function(paragraphElement, block, iconXY) {
const bubble = new Bubble(
/** @type {!WorkspaceSvg} */ (block.workspace), paragraphElement,
block.pathObject.svgPath,
/** @type {!Blockly.utils.Coordinate} */ (iconXY), null, null);
/** @type {!Coordinate} */ (iconXY), null, null);
// Expose this bubble's block's ID on its top-level SVG group.
bubble.setSvgId(block.id);
if (block.RTL) {
// Right-align the paragraph.
// This cannot be done until the bubble is rendered on screen.
var maxWidth = paragraphElement.getBBox().width;
for (var i = 0, textElement; (textElement = paragraphElement.childNodes[i]);
i++) {
const maxWidth = paragraphElement.getBBox().width;
for (let i = 0, textElement; (textElement = paragraphElement.childNodes[i]);
i++) {
textElement.setAttribute('text-anchor', 'end');
textElement.setAttribute('x', maxWidth + Blockly.Bubble.BORDER_WIDTH);
textElement.setAttribute('x', maxWidth + Bubble.BORDER_WIDTH);
}
}
return bubble;
};
exports = Bubble;

View File

@@ -25,6 +25,8 @@ const IDeleteArea = goog.requireType('Blockly.IDeleteArea');
/* eslint-disable-next-line no-unused-vars */
const IDragTarget = goog.requireType('Blockly.IDragTarget');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceCommentSvg = goog.requireType('Blockly.WorkspaceCommentSvg');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
@@ -236,7 +238,7 @@ BubbleDragger.prototype.fireMoveEvent_ = function() {
if (this.draggingBubble_.isComment) {
// TODO (adodson): Resolve build errors when requiring WorkspaceCommentSvg.
const event = new (Events.get(Events.COMMENT_MOVE))(
/** @type {!Blockly.WorkspaceCommentSvg} */ (this.draggingBubble_));
/** @type {!WorkspaceCommentSvg} */ (this.draggingBubble_));
event.setOldCoordinate(this.startXY_);
event.recordNew();
Events.fire(event);

80
core/clipboard.js Normal file
View File

@@ -0,0 +1,80 @@
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Blockly's internal clipboard for managing copy-paste.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.clipboard');
goog.module.declareLegacyNamespace();
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const ICopyable = goog.requireType('Blockly.ICopyable');
/**
* Metadata about the object that is currently on the clipboard.
* @type {?ICopyable.CopyData}
*/
let copyData = null;
/**
* Copy a block or workspace comment onto the local clipboard.
* @param {!ICopyable} toCopy Block or Workspace Comment to be copied.
*/
const copy = function(toCopy) {
copyData = toCopy.toCopyData();
};
/** @package */
exports.copy = copy;
/**
* Paste a block or workspace comment on to the main workspace.
* @return {boolean} True if the paste was successful, false otherwise.
*/
const paste = function() {
if (!copyData.xml) {
return false;
}
// Pasting always pastes to the main workspace, even if the copy
// started in a flyout workspace.
var workspace = copyData.source;
if (workspace.isFlyout) {
workspace = workspace.targetWorkspace;
}
if (copyData.typeCounts &&
workspace.isCapacityAvailable(copyData.typeCounts)) {
Events.setGroup(true);
workspace.paste(copyData.xml);
Events.setGroup(false);
return true;
}
return false;
};
/** @package */
exports.paste = paste;
/**
* Duplicate this block and its children, or a workspace comment.
* @param {!ICopyable} toDuplicate Block or Workspace Comment to be
* duplicated.
*/
const duplicate = function(toDuplicate) {
// Save the clipboard.
const oldCopyData = copyData;
// Create a duplicate via a copy/paste operation.
copy(toDuplicate);
// copy() replaced the value of copyData.
toDuplicate.workspace.paste(copyData.xml);
// Restore the clipboard.
copyData = oldCopyData;
};
/** @package */
exports.duplicate = duplicate;

View File

@@ -20,6 +20,7 @@ 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 Css = goog.require('Blockly.Css');
const Events = goog.require('Blockly.Events');
const Icon = goog.require('Blockly.Icon');
/* eslint-disable-next-line no-unused-vars */
@@ -27,12 +28,10 @@ const Size = goog.requireType('Blockly.utils.Size');
const Svg = goog.require('Blockly.utils.Svg');
/* 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 object = goog.require('Blockly.utils.object');
const userAgent = goog.require('Blockly.utils.userAgent');
/* eslint-disable-next-line no-unused-vars */
const {conditionalBind, Data, unbind} = goog.require('Blockly.browserEvents');
const {createSvgElement, HTML_NS} = goog.require('Blockly.utils.dom');
const {inherits} = goog.require('Blockly.utils.object');
const {register} = goog.require('Blockly.Css');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
/** @suppress {extraRequire} */
@@ -70,35 +69,35 @@ const Comment = function(block) {
/**
* Mouse up event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onMouseUpWrapper_ = null;
/**
* Wheel event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onWheelWrapper_ = null;
/**
* Change event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onChangeWrapper_ = null;
/**
* Input event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onInputWrapper_ = null;
this.createIcon();
};
inherits(Comment, Icon);
object.inherits(Comment, Icon);
/**
* Draw the comment icon.
@@ -107,13 +106,13 @@ inherits(Comment, Icon);
*/
Comment.prototype.drawIcon_ = function(group) {
// Circle.
createSvgElement(
dom.createSvgElement(
Svg.CIRCLE, {'class': 'blocklyIconShape', 'r': '8', 'cx': '8', 'cy': '8'},
group);
// Can't use a real '?' text character since different browsers and operating
// systems render it differently.
// Body of question mark.
createSvgElement(
dom.createSvgElement(
Svg.PATH, {
'class': 'blocklyIconSymbol',
'd': 'm6.8,10h2c0.003,-0.617 0.271,-0.962 0.633,-1.266 2.875,-2.405' +
@@ -122,7 +121,7 @@ Comment.prototype.drawIcon_ = function(group) {
},
group);
// Dot of question mark.
createSvgElement(
dom.createSvgElement(
Svg.RECT, {
'class': 'blocklyIconSymbol',
'x': '6.8',
@@ -151,15 +150,15 @@ Comment.prototype.createEditor_ = function() {
* For non-editable mode see Warning.textToDom_.
*/
this.foreignObject_ = createSvgElement(
this.foreignObject_ = dom.createSvgElement(
Svg.FOREIGNOBJECT, {'x': Bubble.BORDER_WIDTH, 'y': Bubble.BORDER_WIDTH},
null);
const body = document.createElementNS(HTML_NS, 'body');
body.setAttribute('xmlns', HTML_NS);
const body = document.createElementNS(dom.HTML_NS, 'body');
body.setAttribute('xmlns', dom.HTML_NS);
body.className = 'blocklyMinimalBody';
this.textarea_ = document.createElementNS(HTML_NS, 'textarea');
this.textarea_ = document.createElementNS(dom.HTML_NS, 'textarea');
const textarea = this.textarea_;
textarea.className = 'blocklyCommentTextarea';
textarea.setAttribute('dir', this.block_.RTL ? 'RTL' : 'LTR');
@@ -172,23 +171,25 @@ Comment.prototype.createEditor_ = function() {
// Ideally this would be hooked to the focus event for the comment.
// However doing so in Firefox swallows the cursor for unknown reasons.
// So this is hooked to mouseup instead. No big deal.
this.onMouseUpWrapper_ =
conditionalBind(textarea, 'mouseup', this, this.startEdit_, true, true);
this.onMouseUpWrapper_ = browserEvents.conditionalBind(
textarea, 'mouseup', this, this.startEdit_, true, true);
// Don't zoom with mousewheel.
this.onWheelWrapper_ = conditionalBind(textarea, 'wheel', this, function(e) {
e.stopPropagation();
});
this.onWheelWrapper_ =
browserEvents.conditionalBind(textarea, 'wheel', this, function(e) {
e.stopPropagation();
});
this.onChangeWrapper_ =
conditionalBind(textarea, 'change', this, function(_e) {
browserEvents.conditionalBind(textarea, 'change', this, function(_e) {
if (this.cachedText_ != this.model_.text) {
Events.fire(new (Events.get(Events.BLOCK_CHANGE))(
this.block_, 'comment', null, this.cachedText_,
this.model_.text));
}
});
this.onInputWrapper_ = conditionalBind(textarea, 'input', this, function(_e) {
this.model_.text = textarea.value;
});
this.onInputWrapper_ =
browserEvents.conditionalBind(textarea, 'input', this, function(_e) {
this.model_.text = textarea.value;
});
setTimeout(textarea.focus.bind(textarea), 0);
@@ -307,19 +308,19 @@ Comment.prototype.createNonEditableBubble_ = function() {
*/
Comment.prototype.disposeBubble_ = function() {
if (this.onMouseUpWrapper_) {
unbind(this.onMouseUpWrapper_);
browserEvents.unbind(this.onMouseUpWrapper_);
this.onMouseUpWrapper_ = null;
}
if (this.onWheelWrapper_) {
unbind(this.onWheelWrapper_);
browserEvents.unbind(this.onWheelWrapper_);
this.onWheelWrapper_ = null;
}
if (this.onChangeWrapper_) {
unbind(this.onChangeWrapper_);
browserEvents.unbind(this.onChangeWrapper_);
this.onChangeWrapper_ = null;
}
if (this.onInputWrapper_) {
unbind(this.onInputWrapper_);
browserEvents.unbind(this.onInputWrapper_);
this.onInputWrapper_ = null;
}
this.bubble_.dispose();
@@ -397,7 +398,7 @@ Comment.prototype.dispose = function() {
/**
* CSS for block comment. See css.js for use.
*/
register([
Css.register([
/* eslint-disable indent */
'.blocklyCommentTextarea {', 'background-color: #fef49c;', 'border: 0;',
'outline: 0;', 'margin: 0;', 'padding: 3px;', 'resize: none;',

82
core/common.js Normal file
View File

@@ -0,0 +1,82 @@
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Common functions used both internally and externally, but which
* must not be at the top level to avoid circular dependencies.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.common');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Connection = goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
/**
* The main workspace most recently used.
* Set by Blockly.WorkspaceSvg.prototype.markFocused
* @type {!Workspace}
*/
let mainWorkspace;
/**
* Returns the last used top level workspace (based on focus). Try not to use
* this function, particularly if there are multiple Blockly instances on a
* page.
* @return {!Workspace} The main workspace.
*/
const getMainWorkspace = function() {
return mainWorkspace;
};
exports.getMainWorkspace = getMainWorkspace;
/**
* Sets last used main workspace.
* @param {!Workspace} workspace The most recently used top level workspace.
*/
const setMainWorkspace = function(workspace) {
mainWorkspace = workspace;
};
exports.setMainWorkspace = setMainWorkspace;
/**
* Container element in which to render the WidgetDiv, DropDownDiv and Tooltip.
* @type {?Element}
*/
let parentContainer;
/**
* Get the container element in which to render the WidgetDiv, DropDownDiv and\
* Tooltip.
* @return {?Element} The parent container.
*/
const getParentContainer = function() {
return parentContainer;
};
exports.getParentContainer = getParentContainer;
/**
* Set the parent container. This is the container element that the WidgetDiv,
* DropDownDiv, and Tooltip are rendered into the first time `Blockly.inject`
* is called.
* This method is a NOP if called after the first ``Blockly.inject``.
* @param {!Element} newParent The container element.
*/
const setParentContainer = function(newParent) {
parentContainer = newParent;
};
exports.setParentContainer = setParentContainer;
/**
* All of the connections on blocks that are currently being dragged.
* @type {!Array<!Connection>}
*/
exports.draggingConnections = [];

View File

@@ -17,7 +17,7 @@ goog.module.declareLegacyNamespace();
const Block = goog.requireType('Blockly.Block');
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const IASTNodeLocationWithBlock = goog.require('Blockly.IASTNodeLocationWithBlock');
const IASTNodeLocationWithBlock = goog.requireType('Blockly.IASTNodeLocationWithBlock');
/* eslint-disable-next-line no-unused-vars */
const IConnectionChecker = goog.requireType('Blockly.IConnectionChecker');
/* eslint-disable-next-line no-unused-vars */

View File

@@ -16,12 +16,13 @@ goog.module.declareLegacyNamespace();
const Connection = goog.require('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const IConnectionChecker = goog.require('Blockly.IConnectionChecker');
const IConnectionChecker = goog.requireType('Blockly.IConnectionChecker');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
const common = goog.require('Blockly.common');
const connectionTypes = goog.require('Blockly.connectionTypes');
const internalConstants = goog.require('Blockly.internalConstants');
const registry = goog.require('Blockly.registry');
const {OPPOSITE_TYPE} = goog.require('Blockly.internalConstants');
/**
@@ -145,7 +146,7 @@ ConnectionChecker.prototype.doSafetyChecks = function(a, b) {
}
if (blockA == blockB) {
return Connection.REASON_SELF_CONNECTION;
} else if (b.type != OPPOSITE_TYPE[a.type]) {
} else if (b.type != internalConstants.OPPOSITE_TYPE[a.type]) {
return Connection.REASON_WRONG_TYPE;
} else if (blockA.workspace !== blockB.workspace) {
return Connection.REASON_DIFFERENT_WORKSPACES;
@@ -239,7 +240,7 @@ ConnectionChecker.prototype.doDragChecks = function(a, b, distance) {
}
// Don't let blocks try to connect to themselves or ones they nest.
if (Blockly.draggingConnections.indexOf(b) != -1) {
if (common.draggingConnections.indexOf(b) != -1) {
return false;
}
@@ -263,7 +264,7 @@ ConnectionChecker.prototype.canConnectToPrevious_ = function(a, b) {
}
// Don't let blocks try to connect to themselves or ones they nest.
if (Blockly.draggingConnections.indexOf(b) != -1) {
if (common.draggingConnections.indexOf(b) != -1) {
return false;
}

View File

@@ -20,7 +20,7 @@ const Coordinate = goog.requireType('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const IConnectionChecker = goog.requireType('Blockly.IConnectionChecker');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.require('Blockly.RenderedConnection');
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
const connectionTypes = goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');

View File

@@ -10,45 +10,70 @@
*/
'use strict';
/**
* @name Blockly.ContextMenu
* @namespace
*/
goog.provide('Blockly.ContextMenu');
goog.module('Blockly.ContextMenu');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.Events');
// TODO(#5073): Add Blockly require after fixing circular dependency.
// goog.require('Blockly');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
const Menu = goog.require('Blockly.Menu');
const MenuItem = goog.require('Blockly.MenuItem');
const Msg = goog.require('Blockly.Msg');
const Rect = goog.require('Blockly.utils.Rect');
const WidgetDiv = goog.require('Blockly.WidgetDiv');
const Xml = goog.require('Blockly.Xml');
const aria = goog.require('Blockly.utils.aria');
const browserEvents = goog.require('Blockly.browserEvents');
const dom = goog.require('Blockly.utils.dom');
const internalConstants = goog.require('Blockly.internalConstants');
const userAgent = goog.require('Blockly.utils.userAgent');
const utils = goog.require('Blockly.utils');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceCommentSvg = goog.requireType('Blockly.WorkspaceCommentSvg');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockCreate');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Menu');
goog.require('Blockly.MenuItem');
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.Rect');
goog.require('Blockly.utils.userAgent');
goog.require('Blockly.WidgetDiv');
goog.require('Blockly.Xml');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.WorkspaceSvg');
/**
* Which block is the context menu attached to?
* @type {Blockly.Block}
* @type {?Block}
*/
Blockly.ContextMenu.currentBlock = null;
let currentBlock = null;
/**
* Gets the block the context menu is currently attached to.
* @return {?Block} The block the context menu is attached to.
*/
const getCurrentBlock = function() {
return currentBlock;
};
exports.getCurrentBlock = getCurrentBlock;
/**
* Sets the block the context menu is currently attached to.
* @param {?Block} block The block the context menu is attached to.
*/
const setCurrentBlock = function(block) {
currentBlock = block;
};
exports.setCurrentBlock = setCurrentBlock;
// Ad JS accessors for backwards compatibility.
Object.defineProperty(exports, 'currentBlock', {
get: getCurrentBlock,
set: setCurrentBlock,
});
/**
* Menu object.
* @type {Blockly.Menu}
* @private
* @type {Menu}
*/
Blockly.ContextMenu.menu_ = null;
let menu_ = null;
/**
* Construct the menu based on the list of options and show the menu.
@@ -56,47 +81,51 @@ Blockly.ContextMenu.menu_ = null;
* @param {!Array<!Object>} options Array of menu options.
* @param {boolean} rtl True if RTL, false if LTR.
*/
Blockly.ContextMenu.show = function(e, options, rtl) {
Blockly.WidgetDiv.show(Blockly.ContextMenu, rtl, Blockly.ContextMenu.dispose);
const show = function(e, options, rtl) {
WidgetDiv.show(exports, rtl, dispose);
if (!options.length) {
Blockly.ContextMenu.hide();
hide();
return;
}
var menu = Blockly.ContextMenu.populate_(options, rtl);
Blockly.ContextMenu.menu_ = menu;
const menu = populate_(options, rtl);
menu_ = menu;
Blockly.ContextMenu.position_(menu, e, rtl);
position_(menu, e, rtl);
// 1ms delay is required for focusing on context menus because some other
// mouse event is still waiting in the queue and clears focus.
setTimeout(function() {menu.focus();}, 1);
Blockly.ContextMenu.currentBlock = null; // May be set by Blockly.Block.
setTimeout(function() {
menu.focus();
}, 1);
currentBlock = null; // May be set by Blockly.Block.
};
exports.show = show;
/**
* Create the context menu object and populate it with the given options.
* @param {!Array<!Object>} options Array of menu options.
* @param {boolean} rtl True if RTL, false if LTR.
* @return {!Blockly.Menu} The menu that will be shown on right click.
* @return {!Menu} The menu that will be shown on right click.
* @private
*/
Blockly.ContextMenu.populate_ = function(options, rtl) {
const populate_ = function(options, rtl) {
/* Here's what one option object looks like:
{text: 'Make It So',
enabled: true,
callback: Blockly.MakeItSo}
*/
var menu = new Blockly.Menu();
menu.setRole(Blockly.utils.aria.Role.MENU);
for (var i = 0, option; (option = options[i]); i++) {
var menuItem = new Blockly.MenuItem(option.text);
const menu = new Menu();
menu.setRole(aria.Role.MENU);
for (let i = 0; i < options.length; i++) {
const option = options[i];
const menuItem = new MenuItem(option.text);
menuItem.setRightToLeft(rtl);
menuItem.setRole(Blockly.utils.aria.Role.MENUITEM);
menuItem.setRole(aria.Role.MENUITEM);
menu.addChild(menuItem);
menuItem.setEnabled(option.enabled);
if (option.enabled) {
var actionHandler = function(_menuItem) {
var option = this;
Blockly.ContextMenu.hide();
const actionHandler = function(_menuItem) {
const option = this;
hide();
option.callback(option.scope);
};
menuItem.onAction(actionHandler, option);
@@ -107,26 +136,23 @@ Blockly.ContextMenu.populate_ = function(options, rtl) {
/**
* Add the menu to the page and position it correctly.
* @param {!Blockly.Menu} menu The menu to add and position.
* @param {!Menu} menu The menu to add and position.
* @param {!Event} e Mouse event for the right click that is making the context
* menu appear.
* @param {boolean} rtl True if RTL, false if LTR.
* @private
*/
Blockly.ContextMenu.position_ = function(menu, e, rtl) {
const position_ = function(menu, e, rtl) {
// Record windowSize and scrollOffset before adding menu.
var viewportBBox = Blockly.utils.getViewportBBox();
const viewportBBox = utils.getViewportBBox();
// This one is just a point, but we'll pretend that it's a rect so we can use
// some helper functions.
var anchorBBox = new Blockly.utils.Rect(
e.clientY + viewportBBox.top,
e.clientY + viewportBBox.top,
e.clientX + viewportBBox.left,
e.clientX + viewportBBox.left
);
const anchorBBox = new Rect(
e.clientY + viewportBBox.top, e.clientY + viewportBBox.top,
e.clientX + viewportBBox.left, e.clientX + viewportBBox.left);
Blockly.ContextMenu.createWidget_(menu);
var menuSize = menu.getSize();
createWidget_(menu);
const menuSize = menu.getSize();
if (rtl) {
anchorBBox.left += menuSize.width;
@@ -135,7 +161,7 @@ Blockly.ContextMenu.position_ = function(menu, e, rtl) {
viewportBBox.right += menuSize.width;
}
Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, menuSize, rtl);
WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, menuSize, rtl);
// Calling menuDom.focus() has to wait until after the menu has been placed
// correctly. Otherwise it will cause a page scroll to get the misplaced menu
// in view. See issue #1329.
@@ -144,19 +170,19 @@ Blockly.ContextMenu.position_ = function(menu, e, rtl) {
/**
* Create and render the menu widget inside Blockly's widget div.
* @param {!Blockly.Menu} menu The menu to add to the widget div.
* @param {!Menu} menu The menu to add to the widget div.
* @private
*/
Blockly.ContextMenu.createWidget_ = function(menu) {
var div = Blockly.WidgetDiv.DIV;
const createWidget_ = function(menu) {
const div = WidgetDiv.DIV;
menu.render(div);
var menuDom = menu.getElement();
Blockly.utils.dom.addClass(
const menuDom = menu.getElement();
dom.addClass(
/** @type {!Element} */ (menuDom), 'blocklyContextMenu');
// Prevent system context menu when right-clicking a Blockly context menu.
Blockly.browserEvents.conditionalBind(
browserEvents.conditionalBind(
/** @type {!EventTarget} */ (menuDom), 'contextmenu', null,
Blockly.utils.noEvent);
utils.noEvent);
// Focus only after the initial render to avoid issue #1329.
menu.focus();
};
@@ -164,85 +190,88 @@ Blockly.ContextMenu.createWidget_ = function(menu) {
/**
* Hide the context menu.
*/
Blockly.ContextMenu.hide = function() {
Blockly.WidgetDiv.hideIfOwner(Blockly.ContextMenu);
Blockly.ContextMenu.currentBlock = null;
const hide = function() {
WidgetDiv.hideIfOwner(exports);
currentBlock = null;
};
exports.hide = hide;
/**
* Dispose of the menu.
*/
Blockly.ContextMenu.dispose = function() {
if (Blockly.ContextMenu.menu_) {
Blockly.ContextMenu.menu_.dispose();
Blockly.ContextMenu.menu_ = null;
const dispose = function() {
if (menu_) {
menu_.dispose();
menu_ = null;
}
};
exports.dispose = dispose;
/**
* Create a callback function that creates and configures a block,
* then places the new block next to the original.
* @param {!Blockly.Block} block Original block.
* @param {!Block} block Original block.
* @param {!Element} xml XML representation of new block.
* @return {!Function} Function that creates a block.
*/
Blockly.ContextMenu.callbackFactory = function(block, xml) {
const callbackFactory = function(block, xml) {
return function() {
Blockly.Events.disable();
Events.disable();
let newBlock;
try {
var newBlock = Blockly.Xml.domToBlock(xml, block.workspace);
newBlock = Xml.domToBlock(xml, block.workspace);
// Move the new block next to the old block.
var xy = block.getRelativeToSurfaceXY();
const xy = block.getRelativeToSurfaceXY();
if (block.RTL) {
xy.x -= Blockly.internalConstants.SNAP_RADIUS;
xy.x -= internalConstants.SNAP_RADIUS;
} else {
xy.x += Blockly.internalConstants.SNAP_RADIUS;
xy.x += internalConstants.SNAP_RADIUS;
}
xy.y += Blockly.internalConstants.SNAP_RADIUS * 2;
xy.y += internalConstants.SNAP_RADIUS * 2;
newBlock.moveBy(xy.x, xy.y);
} finally {
Blockly.Events.enable();
Events.enable();
}
if (Blockly.Events.isEnabled() && !newBlock.isShadow()) {
Blockly.Events.fire(
new (Blockly.Events.get(Blockly.Events.BLOCK_CREATE))(newBlock));
if (Events.isEnabled() && !newBlock.isShadow()) {
Events.fire(new (Events.get(Events.BLOCK_CREATE))(newBlock));
}
newBlock.select();
};
};
exports.callbackFactory = callbackFactory;
// Helper functions for creating context menu options.
/**
* Make a context menu option for deleting the current workspace comment.
* @param {!Blockly.WorkspaceCommentSvg} comment The workspace comment where the
* @param {!WorkspaceCommentSvg} comment The workspace comment where the
* right-click originated.
* @return {!Object} A menu option, containing text, enabled, and a callback.
* @package
*/
Blockly.ContextMenu.commentDeleteOption = function(comment) {
var deleteOption = {
text: Blockly.Msg['REMOVE_COMMENT'],
const commentDeleteOption = function(comment) {
const deleteOption = {
text: Msg['REMOVE_COMMENT'],
enabled: true,
callback: function() {
Blockly.Events.setGroup(true);
comment.dispose(true, true);
Blockly.Events.setGroup(false);
Events.setGroup(true);
comment.dispose();
Events.setGroup(false);
}
};
return deleteOption;
};
/** @package */
exports.commentDeleteOption = commentDeleteOption;
/**
* Make a context menu option for duplicating the current workspace comment.
* @param {!Blockly.WorkspaceCommentSvg} comment The workspace comment where the
* @param {!WorkspaceCommentSvg} comment The workspace comment where the
* right-click originated.
* @return {!Object} A menu option, containing text, enabled, and a callback.
* @package
*/
Blockly.ContextMenu.commentDuplicateOption = function(comment) {
var duplicateOption = {
text: Blockly.Msg['DUPLICATE_COMMENT'],
const commentDuplicateOption = function(comment) {
const duplicateOption = {
text: Msg['DUPLICATE_COMMENT'],
enabled: true,
callback: function() {
Blockly.duplicate(comment);
@@ -250,10 +279,12 @@ Blockly.ContextMenu.commentDuplicateOption = function(comment) {
};
return duplicateOption;
};
/** @package */
exports.commentDuplicateOption = commentDuplicateOption;
/**
* Make a context menu option for adding a comment on the workspace.
* @param {!Blockly.WorkspaceSvg} ws The workspace where the right-click
* @param {!WorkspaceSvg} ws The workspace where the right-click
* originated.
* @param {!Event} e The right-click mouse event.
* @return {!Object} A menu option, containing text, enabled, and a callback.
@@ -261,41 +292,42 @@ Blockly.ContextMenu.commentDuplicateOption = function(comment) {
* @suppress {strictModuleDepCheck,checkTypes} Suppress checks while workspace
* comments are not bundled in.
*/
Blockly.ContextMenu.workspaceCommentOption = function(ws, e) {
if (!Blockly.WorkspaceCommentSvg) {
const workspaceCommentOption = function(ws, e) {
const WorkspaceCommentSvg = goog.module.get('Blockly.WorkspaceCommentSvg');
if (!WorkspaceCommentSvg) {
throw Error('Missing require for Blockly.WorkspaceCommentSvg');
}
// Helper function to create and position a comment correctly based on the
// location of the mouse event.
var addWsComment = function() {
var comment = new Blockly.WorkspaceCommentSvg(
ws, Blockly.Msg['WORKSPACE_COMMENT_DEFAULT_TEXT'],
Blockly.WorkspaceCommentSvg.DEFAULT_SIZE,
Blockly.WorkspaceCommentSvg.DEFAULT_SIZE);
const addWsComment = function() {
const comment = new WorkspaceCommentSvg(
ws, Msg['WORKSPACE_COMMENT_DEFAULT_TEXT'],
WorkspaceCommentSvg.DEFAULT_SIZE,
WorkspaceCommentSvg.DEFAULT_SIZE);
var injectionDiv = ws.getInjectionDiv();
const injectionDiv = ws.getInjectionDiv();
// Bounding rect coordinates are in client coordinates, meaning that they
// are in pixels relative to the upper left corner of the visible browser
// window. These coordinates change when you scroll the browser window.
var boundingRect = injectionDiv.getBoundingClientRect();
const boundingRect = injectionDiv.getBoundingClientRect();
// The client coordinates offset by the injection div's upper left corner.
var clientOffsetPixels = new Blockly.utils.Coordinate(
const clientOffsetPixels = new Coordinate(
e.clientX - boundingRect.left, e.clientY - boundingRect.top);
// The offset in pixels between the main workspace's origin and the upper
// left corner of the injection div.
var mainOffsetPixels = ws.getOriginOffsetInPixels();
const mainOffsetPixels = ws.getOriginOffsetInPixels();
// The position of the new comment in pixels relative to the origin of the
// main workspace.
var finalOffset = Blockly.utils.Coordinate.difference(clientOffsetPixels,
mainOffsetPixels);
const finalOffset =
Coordinate.difference(clientOffsetPixels, mainOffsetPixels);
// The position of the new comment in main workspace coordinates.
finalOffset.scale(1 / ws.scale);
var commentX = finalOffset.x;
var commentY = finalOffset.y;
const commentX = finalOffset.x;
const commentY = finalOffset.y;
comment.moveBy(commentX, commentY);
if (ws.rendered) {
comment.initSvg();
@@ -304,14 +336,16 @@ Blockly.ContextMenu.workspaceCommentOption = function(ws, e) {
}
};
var wsCommentOption = {
const wsCommentOption = {
// Foreign objects don't work in IE. Don't let the user create comments
// that they won't be able to edit.
enabled: !Blockly.utils.userAgent.IE
enabled: !userAgent.IE
};
wsCommentOption.text = Blockly.Msg['ADD_COMMENT'];
wsCommentOption.text = Msg['ADD_COMMENT'];
wsCommentOption.callback = function() {
addWsComment();
};
return wsCommentOption;
};
/** @package */
exports.workspaceCommentOption = workspaceCommentOption;

View File

@@ -10,73 +10,84 @@
*/
'use strict';
/**
* @name Blockly.ContextMenuItems
* @namespace
*/
goog.provide('Blockly.ContextMenuItems');
goog.module('Blockly.ContextMenuItems');
goog.module.declareLegacyNamespace();
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.ContextMenuRegistry');
goog.require('Blockly.Events');
goog.require('Blockly.inputTypes');
goog.requireType('Blockly.BlockSvg');
const Blockly = goog.require('Blockly');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const ContextMenuRegistry = goog.require('Blockly.ContextMenuRegistry');
const Events = goog.require('Blockly.Events');
const Msg = goog.require('Blockly.Msg');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const clipboard = goog.require('Blockly.clipboard');
const inputTypes = goog.require('Blockly.inputTypes');
const userAgent = goog.require('Blockly.utils.userAgent');
const utils = goog.require('Blockly.utils');
/** Option to undo previous action. */
Blockly.ContextMenuItems.registerUndo = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var undoOption = {
const registerUndo = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const undoOption = {
displayText: function() {
return Blockly.Msg['UNDO'];
return Msg['UNDO'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (scope.workspace.getUndoStack().length > 0) {
return 'enabled';
}
return 'disabled';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
scope.workspace.undo(false);
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
scopeType: ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'undoWorkspace',
weight: 1,
};
Blockly.ContextMenuRegistry.registry.register(undoOption);
ContextMenuRegistry.registry.register(undoOption);
};
exports.registerUndo = registerUndo;
/** Option to redo previous action. */
Blockly.ContextMenuItems.registerRedo = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var redoOption = {
displayText: function() { return Blockly.Msg['REDO']; },
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
const registerRedo = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const redoOption = {
displayText: function() {
return Msg['REDO'];
},
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (scope.workspace.getRedoStack().length > 0) {
return 'enabled';
}
return 'disabled';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
scope.workspace.undo(true);
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
scopeType: ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'redoWorkspace',
weight: 2,
};
Blockly.ContextMenuRegistry.registry.register(redoOption);
ContextMenuRegistry.registry.register(redoOption);
};
exports.registerRedo = registerRedo;
/** Option to clean up blocks. */
Blockly.ContextMenuItems.registerCleanup = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var cleanOption = {
const registerCleanup = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const cleanOption = {
displayText: function() {
return Blockly.Msg['CLEAN_UP'];
return Msg['CLEAN_UP'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (scope.workspace.isMovable()) {
if (scope.workspace.getTopBlocks(false).length > 1) {
return 'enabled';
@@ -85,27 +96,29 @@ Blockly.ContextMenuItems.registerCleanup = function() {
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
scope.workspace.cleanUp();
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
scopeType: ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'cleanWorkspace',
weight: 3,
};
Blockly.ContextMenuRegistry.registry.register(cleanOption);
ContextMenuRegistry.registry.register(cleanOption);
};
exports.registerCleanup = registerCleanup;
/**
* Creates a callback to collapse or expand top blocks.
* @param {boolean} shouldCollapse Whether a block should collapse.
* @param {!Array<Blockly.BlockSvg>} topBlocks Top blocks in the workspace.
* @param {!Array<BlockSvg>} topBlocks Top blocks in the workspace.
* @private
*/
Blockly.ContextMenuItems.toggleOption_ = function(shouldCollapse, topBlocks) {
var DELAY = 10;
var ms = 0;
for (var i = 0; i < topBlocks.length; i++) {
var block = topBlocks[i];
const toggleOption_ = function(shouldCollapse, topBlocks) {
const DELAY = 10;
let ms = 0;
for (let i = 0; i < topBlocks.length; i++) {
let block = topBlocks[i];
while (block) {
setTimeout(block.setCollapsed.bind(block, shouldCollapse), ms);
block = block.getNextBlock();
@@ -115,17 +128,18 @@ Blockly.ContextMenuItems.toggleOption_ = function(shouldCollapse, topBlocks) {
};
/** Option to collapse all blocks. */
Blockly.ContextMenuItems.registerCollapse = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var collapseOption = {
displayText: function() {
return Blockly.Msg['COLLAPSE_ALL'];
const registerCollapse = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const collapseOption = {
displayText: function() {
return Msg['COLLAPSE_ALL'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (scope.workspace.options.collapse) {
var topBlocks = scope.workspace.getTopBlocks(false);
for (var i = 0; i < topBlocks.length; i++) {
var block = topBlocks[i];
const topBlocks = scope.workspace.getTopBlocks(false);
for (let i = 0; i < topBlocks.length; i++) {
let block = topBlocks[i];
while (block) {
if (!block.isCollapsed()) {
return 'enabled';
@@ -137,28 +151,31 @@ Blockly.ContextMenuItems.registerCollapse = function() {
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
Blockly.ContextMenuItems.toggleOption_(true, scope.workspace.getTopBlocks(true));
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
toggleOption_(true, scope.workspace.getTopBlocks(true));
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'collapseWorkspace',
weight: 4,
scopeType: ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'collapseWorkspace',
weight: 4,
};
Blockly.ContextMenuRegistry.registry.register(collapseOption);
ContextMenuRegistry.registry.register(collapseOption);
};
exports.registerCollapse = registerCollapse;
/** Option to expand all blocks. */
Blockly.ContextMenuItems.registerExpand = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var expandOption = {
const registerExpand = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const expandOption = {
displayText: function() {
return Blockly.Msg['EXPAND_ALL'];
return Msg['EXPAND_ALL'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (scope.workspace.options.collapse) {
var topBlocks = scope.workspace.getTopBlocks(false);
for (var i = 0; i < topBlocks.length; i++) {
var block = topBlocks[i];
const topBlocks = scope.workspace.getTopBlocks(false);
for (let i = 0; i < topBlocks.length; i++) {
let block = topBlocks[i];
while (block) {
if (block.isCollapsed()) {
return 'enabled';
@@ -170,142 +187,151 @@ Blockly.ContextMenuItems.registerExpand = function() {
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
Blockly.ContextMenuItems.toggleOption_(false, scope.workspace.getTopBlocks(true));
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
toggleOption_(false, scope.workspace.getTopBlocks(true));
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
scopeType: ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'expandWorkspace',
weight: 5,
};
Blockly.ContextMenuRegistry.registry.register(expandOption);
ContextMenuRegistry.registry.register(expandOption);
};
exports.registerExpand = registerExpand;
/**
* Adds a block and its children to a list of deletable blocks.
* @param {!Blockly.BlockSvg} block to delete.
* @param {!Array<!Blockly.BlockSvg>} deleteList list of blocks that can be deleted. This will be
* @param {!BlockSvg} block to delete.
* @param {!Array<!BlockSvg>} deleteList list of blocks that can be deleted.
* This will be
* modifed in place with the given block and its descendants.
* @private
*/
Blockly.ContextMenuItems.addDeletableBlocks_ = function(block, deleteList) {
const addDeletableBlocks_ = function(block, deleteList) {
if (block.isDeletable()) {
Array.prototype.push.apply(deleteList, block.getDescendants(false));
} else {
var children = /* eslint-disable-next-line indent */
/** @type {!Array<!Blockly.BlockSvg>} */ (block.getChildren(false));
for (var i = 0; i < children.length; i++) {
Blockly.ContextMenuItems.addDeletableBlocks_(children[i], deleteList);
const children = /* eslint-disable-next-line indent */
/** @type {!Array<!BlockSvg>} */ (block.getChildren(false));
for (let i = 0; i < children.length; i++) {
addDeletableBlocks_(children[i], deleteList);
}
}
};
/**
* Constructs a list of blocks that can be deleted in the given workspace.
* @param {!Blockly.WorkspaceSvg} workspace to delete all blocks from.
* @return {!Array<!Blockly.BlockSvg>} list of blocks to delete.
* @param {!WorkspaceSvg} workspace to delete all blocks from.
* @return {!Array<!BlockSvg>} list of blocks to delete.
* @private
*/
Blockly.ContextMenuItems.getDeletableBlocks_ = function(workspace) {
var deleteList = [];
var topBlocks = workspace.getTopBlocks(true);
for (var i = 0; i < topBlocks.length; i++) {
Blockly.ContextMenuItems.addDeletableBlocks_(topBlocks[i], deleteList);
const getDeletableBlocks_ = function(workspace) {
const deleteList = [];
const topBlocks = workspace.getTopBlocks(true);
for (let i = 0; i < topBlocks.length; i++) {
addDeletableBlocks_(topBlocks[i], deleteList);
}
return deleteList;
};
/** Deletes the given blocks. Used to delete all blocks in the workspace.
* @param {!Array<!Blockly.BlockSvg>} deleteList list of blocks to delete.
* @param {string} eventGroup event group ID with which all delete events should be associated.
/**
* Deletes the given blocks. Used to delete all blocks in the workspace.
* @param {!Array<!BlockSvg>} deleteList list of blocks to delete.
* @param {string} eventGroup event group ID with which all delete events should
* be associated.
* @private
*/
Blockly.ContextMenuItems.deleteNext_ = function(deleteList, eventGroup) {
var DELAY = 10;
Blockly.Events.setGroup(eventGroup);
var block = deleteList.shift();
const deleteNext_ = function(deleteList, eventGroup) {
const DELAY = 10;
Events.setGroup(eventGroup);
const block = deleteList.shift();
if (block) {
if (block.workspace) {
block.dispose(false, true);
setTimeout(Blockly.ContextMenuItems.deleteNext_, DELAY, deleteList, eventGroup);
setTimeout(deleteNext_, DELAY, deleteList, eventGroup);
} else {
Blockly.ContextMenuItems.deleteNext_(deleteList, eventGroup);
deleteNext_(deleteList, eventGroup);
}
}
Blockly.Events.setGroup(false);
Events.setGroup(false);
};
/** Option to delete all blocks. */
Blockly.ContextMenuItems.registerDeleteAll = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var deleteOption = {
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
const registerDeleteAll = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const deleteOption = {
displayText: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (!scope.workspace) {
return;
}
var deletableBlocksLength =
Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace).length;
const deletableBlocksLength = getDeletableBlocks_(scope.workspace).length;
if (deletableBlocksLength == 1) {
return Blockly.Msg['DELETE_BLOCK'];
return Msg['DELETE_BLOCK'];
} else {
return Blockly.Msg['DELETE_X_BLOCKS'].replace('%1', String(deletableBlocksLength));
return Msg['DELETE_X_BLOCKS'].replace(
'%1', String(deletableBlocksLength));
}
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (!scope.workspace) {
return;
}
var deletableBlocksLength =
Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace).length;
const deletableBlocksLength = getDeletableBlocks_(scope.workspace).length;
return deletableBlocksLength > 0 ? 'enabled' : 'disabled';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (!scope.workspace) {
return;
}
scope.workspace.cancelCurrentGesture();
var deletableBlocks = Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace);
var eventGroup = Blockly.utils.genUid();
const deletableBlocks = getDeletableBlocks_(scope.workspace);
const eventGroup = utils.genUid();
if (deletableBlocks.length < 2) {
Blockly.ContextMenuItems.deleteNext_(deletableBlocks, eventGroup);
deleteNext_(deletableBlocks, eventGroup);
} else {
Blockly.confirm(
Blockly.Msg['DELETE_ALL_BLOCKS'].replace('%1', deletableBlocks.length),
Msg['DELETE_ALL_BLOCKS'].replace('%1', deletableBlocks.length),
function(ok) {
if (ok) {
Blockly.ContextMenuItems.deleteNext_(deletableBlocks, eventGroup);
deleteNext_(deletableBlocks, eventGroup);
}
});
}
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
scopeType: ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'workspaceDelete',
weight: 6,
};
Blockly.ContextMenuRegistry.registry.register(deleteOption);
ContextMenuRegistry.registry.register(deleteOption);
};
exports.registerDeleteAll = registerDeleteAll;
/**
* Registers all workspace-scoped context menu items.
* @private
*/
Blockly.ContextMenuItems.registerWorkspaceOptions_ = function() {
Blockly.ContextMenuItems.registerUndo();
Blockly.ContextMenuItems.registerRedo();
Blockly.ContextMenuItems.registerCleanup();
Blockly.ContextMenuItems.registerCollapse();
Blockly.ContextMenuItems.registerExpand();
Blockly.ContextMenuItems.registerDeleteAll();
const registerWorkspaceOptions_ = function() {
registerUndo();
registerRedo();
registerCleanup();
registerCollapse();
registerExpand();
registerDeleteAll();
};
/** Option to duplicate a block. */
Blockly.ContextMenuItems.registerDuplicate = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var duplicateOption = {
const registerDuplicate = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const duplicateOption = {
displayText: function() {
return Blockly.Msg['DUPLICATE_BLOCK'];
return Msg['DUPLICATE_BLOCK'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
if (!block.isInFlyout && block.isDeletable() && block.isMovable()) {
if (block.isDuplicatable()) {
return 'enabled';
@@ -314,121 +340,141 @@ Blockly.ContextMenuItems.registerDuplicate = function() {
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (scope.block) {
Blockly.duplicate(scope.block);
clipboard.duplicate(scope.block);
}
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockDuplicate',
weight: 1,
};
Blockly.ContextMenuRegistry.registry.register(duplicateOption);
ContextMenuRegistry.registry.register(duplicateOption);
};
exports.registerDuplicate = registerDuplicate;
/** Option to add or remove block-level comment. */
Blockly.ContextMenuItems.registerComment = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var commentOption = {
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
const registerComment = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const commentOption = {
displayText: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (scope.block.getCommentIcon()) {
// If there's already a comment, option is to remove.
return Blockly.Msg['REMOVE_COMMENT'];
return Msg['REMOVE_COMMENT'];
}
// If there's no comment yet, option is to add.
return Blockly.Msg['ADD_COMMENT'];
return Msg['ADD_COMMENT'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
// IE doesn't support necessary features for comment editing.
if (!Blockly.utils.userAgent.IE && !block.isInFlyout && block.workspace.options.comments &&
!block.isCollapsed() && block.isEditable()) {
if (!userAgent.IE && !block.isInFlyout &&
block.workspace.options.comments && !block.isCollapsed() &&
block.isEditable()) {
return 'enabled';
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
if (block.getCommentIcon()) {
block.setCommentText(null);
} else {
block.setCommentText('');
}
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockComment',
weight: 2,
};
Blockly.ContextMenuRegistry.registry.register(commentOption);
ContextMenuRegistry.registry.register(commentOption);
};
exports.registerComment = registerComment;
/** Option to inline variables. */
Blockly.ContextMenuItems.registerInline = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var inlineOption = {
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
return (scope.block.getInputsInline()) ?
Blockly.Msg['EXTERNAL_INPUTS'] : Blockly.Msg['INLINE_INPUTS'];
const registerInline = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const inlineOption = {
displayText: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
return (scope.block.getInputsInline()) ? Msg['EXTERNAL_INPUTS'] :
Msg['INLINE_INPUTS'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
if (!block.isInFlyout && block.isMovable() && !block.isCollapsed()) {
for (var i = 1; i < block.inputList.length; i++) {
// Only display this option if there are two value or dummy inputs next to each other.
if (block.inputList[i - 1].type != Blockly.inputTypes.STATEMENT &&
block.inputList[i].type != Blockly.inputTypes.STATEMENT) {
for (let i = 1; i < block.inputList.length; i++) {
// Only display this option if there are two value or dummy inputs
// next to each other.
if (block.inputList[i - 1].type != inputTypes.STATEMENT &&
block.inputList[i].type != inputTypes.STATEMENT) {
return 'enabled';
}
}
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
scope.block.setInputsInline(!scope.block.getInputsInline());
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockInline',
weight: 3,
};
Blockly.ContextMenuRegistry.registry.register(inlineOption);
ContextMenuRegistry.registry.register(inlineOption);
};
exports.registerInline = registerInline;
/** Option to collapse or expand a block. */
Blockly.ContextMenuItems.registerCollapseExpandBlock = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var collapseExpandOption = {
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
return scope.block.isCollapsed() ?
Blockly.Msg['EXPAND_BLOCK'] : Blockly.Msg['COLLAPSE_BLOCK'];
const registerCollapseExpandBlock = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const collapseExpandOption = {
displayText: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
return scope.block.isCollapsed() ? Msg['EXPAND_BLOCK'] :
Msg['COLLAPSE_BLOCK'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
if (!block.isInFlyout && block.isMovable() && block.workspace.options.collapse) {
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
if (!block.isInFlyout && block.isMovable() &&
block.workspace.options.collapse) {
return 'enabled';
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
scope.block.setCollapsed(!scope.block.isCollapsed());
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockCollapseExpand',
weight: 4,
};
Blockly.ContextMenuRegistry.registry.register(collapseExpandOption);
ContextMenuRegistry.registry.register(collapseExpandOption);
};
exports.registerCollapseExpandBlock = registerCollapseExpandBlock;
/** Option to disable or enable a block. */
Blockly.ContextMenuItems.registerDisable = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var disableOption = {
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
return (scope.block.isEnabled()) ?
Blockly.Msg['DISABLE_BLOCK'] : Blockly.Msg['ENABLE_BLOCK'];
const registerDisable = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const disableOption = {
displayText: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
return (scope.block.isEnabled()) ? Msg['DISABLE_BLOCK'] :
Msg['ENABLE_BLOCK'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
if (!block.isInFlyout && block.workspace.options.disable && block.isEditable()) {
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
if (!block.isInFlyout && block.workspace.options.disable &&
block.isEditable()) {
if (block.getInheritedDisabled()) {
return 'disabled';
}
@@ -436,108 +482,119 @@ Blockly.ContextMenuItems.registerDisable = function() {
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
var group = Blockly.Events.getGroup();
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
const group = Events.getGroup();
if (!group) {
Blockly.Events.setGroup(true);
Events.setGroup(true);
}
block.setEnabled(!block.isEnabled());
if (!group) {
Blockly.Events.setGroup(false);
Events.setGroup(false);
}
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockDisable',
weight: 5,
};
Blockly.ContextMenuRegistry.registry.register(disableOption);
ContextMenuRegistry.registry.register(disableOption);
};
exports.registerDisable = registerDisable;
/** Option to delete a block. */
Blockly.ContextMenuItems.registerDelete = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var deleteOption = {
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
const registerDelete = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const deleteOption = {
displayText: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
// Count the number of blocks that are nested in this block.
var descendantCount = block.getDescendants(false).length;
var nextBlock = block.getNextBlock();
let descendantCount = block.getDescendants(false).length;
const nextBlock = block.getNextBlock();
if (nextBlock) {
// Blocks in the current stack would survive this block's deletion.
descendantCount -= nextBlock.getDescendants(false).length;
}
return (descendantCount == 1) ? Blockly.Msg['DELETE_BLOCK'] :
Blockly.Msg['DELETE_X_BLOCKS'].replace('%1', String(descendantCount));
return (descendantCount == 1) ?
Msg['DELETE_BLOCK'] :
Msg['DELETE_X_BLOCKS'].replace('%1', String(descendantCount));
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
if (!scope.block.isInFlyout && scope.block.isDeletable()) {
return 'enabled';
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
Blockly.Events.setGroup(true);
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
Events.setGroup(true);
if (scope.block) {
Blockly.deleteBlock(scope.block);
}
Blockly.Events.setGroup(false);
Events.setGroup(false);
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockDelete',
weight: 6,
};
Blockly.ContextMenuRegistry.registry.register(deleteOption);
ContextMenuRegistry.registry.register(deleteOption);
};
exports.registerDelete = registerDelete;
/** Option to open help for a block. */
Blockly.ContextMenuItems.registerHelp = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var helpOption = {
const registerHelp = function() {
/** @type {!ContextMenuRegistry.RegistryItem} */
const helpOption = {
displayText: function() {
return Blockly.Msg['HELP'];
return Msg['HELP'];
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
var block = scope.block;
var url = (typeof block.helpUrl == 'function') ?
block.helpUrl() : block.helpUrl;
preconditionFn: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
const block = scope.block;
const url = (typeof block.helpUrl == 'function') ? block.helpUrl() :
block.helpUrl;
if (url) {
return 'enabled';
}
return 'hidden';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
callback: function(/** @type {!ContextMenuRegistry.Scope} */
scope) {
scope.block.showHelp();
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
scopeType: ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockHelp',
weight: 7,
};
Blockly.ContextMenuRegistry.registry.register(helpOption);
ContextMenuRegistry.registry.register(helpOption);
};
exports.registerHelp = registerHelp;
/**
* Registers all block-scoped context menu items.
* @private
*/
Blockly.ContextMenuItems.registerBlockOptions_ = function() {
Blockly.ContextMenuItems.registerDuplicate();
Blockly.ContextMenuItems.registerComment();
Blockly.ContextMenuItems.registerInline();
Blockly.ContextMenuItems.registerCollapseExpandBlock();
Blockly.ContextMenuItems.registerDisable();
Blockly.ContextMenuItems.registerDelete();
Blockly.ContextMenuItems.registerHelp();
const registerBlockOptions_ = function() {
registerDuplicate();
registerComment();
registerInline();
registerCollapseExpandBlock();
registerDisable();
registerDelete();
registerHelp();
};
/**
* Registers all default context menu items. This should be called once per instance of
* ContextMenuRegistry.
* Registers all default context menu items. This should be called once per
* instance of ContextMenuRegistry.
* @package
*/
Blockly.ContextMenuItems.registerDefaultOptions = function() {
Blockly.ContextMenuItems.registerWorkspaceOptions_();
Blockly.ContextMenuItems.registerBlockOptions_();
const registerDefaultOptions = function() {
registerWorkspaceOptions_();
registerBlockOptions_();
};
exports.registerDefaultOptions = registerDefaultOptions;
Blockly.ContextMenuItems.registerDefaultOptions();
registerDefaultOptions();

View File

@@ -11,91 +11,97 @@
'use strict';
/**
* @name Blockly.ContextMenuRegistry
* @name ContextMenuRegistry
* @namespace
*/
goog.provide('Blockly.ContextMenuRegistry');
goog.module('Blockly.ContextMenuRegistry');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
/**
* Class for the registry of context menu items. This is intended to be a
* singleton. You should not create a new instance, and only access this class
* from Blockly.ContextMenuRegistry.registry.
* from ContextMenuRegistry.registry.
* @constructor
* @private
*/
Blockly.ContextMenuRegistry = function() {
const ContextMenuRegistry = function() {
// Singleton instance should be registered once.
Blockly.ContextMenuRegistry.registry = this;
ContextMenuRegistry.registry = this;
/**
* Registry of all registered RegistryItems, keyed by ID.
* @type {!Object<string, !Blockly.ContextMenuRegistry.RegistryItem>}
* @type {!Object<string, !ContextMenuRegistry.RegistryItem>}
* @private
*/
this.registry_ = Object.create(null);
};
/**
* Where this menu item should be rendered. If the menu item should be rendered in multiple
* scopes, e.g. on both a block and a workspace, it should be registered for each scope.
* Where this menu item should be rendered. If the menu item should be rendered
* in multiple scopes, e.g. on both a block and a workspace, it should be
* registered for each scope.
* @enum {string}
*/
Blockly.ContextMenuRegistry.ScopeType = {
ContextMenuRegistry.ScopeType = {
BLOCK: 'block',
WORKSPACE: 'workspace',
};
/**
* The actual workspace/block where the menu is being rendered. This is passed to callback and
* displayText functions that depend on this information.
* The actual workspace/block where the menu is being rendered. This is passed
* to callback and displayText functions that depend on this information.
* @typedef {{
* block: (Blockly.BlockSvg|undefined),
* workspace: (Blockly.WorkspaceSvg|undefined)
* block: (BlockSvg|undefined),
* workspace: (WorkspaceSvg|undefined)
* }}
*/
Blockly.ContextMenuRegistry.Scope;
ContextMenuRegistry.Scope;
/**
* A menu item as entered in the registry.
* @typedef {{
* callback: function(!Blockly.ContextMenuRegistry.Scope),
* scopeType: !Blockly.ContextMenuRegistry.ScopeType,
* displayText: ((function(!Blockly.ContextMenuRegistry.Scope):string)|string),
* preconditionFn: function(!Blockly.ContextMenuRegistry.Scope):string,
* callback: function(!ContextMenuRegistry.Scope),
* scopeType: !ContextMenuRegistry.ScopeType,
* displayText: ((function(!ContextMenuRegistry.Scope):string)|string),
* preconditionFn: function(!ContextMenuRegistry.Scope):string,
* weight: number,
* id: string
* }}
*/
Blockly.ContextMenuRegistry.RegistryItem;
*/
ContextMenuRegistry.RegistryItem;
/**
* A menu item as presented to contextmenu.js.
* @typedef {{
* text: string,
* enabled: boolean,
* callback: function(!Blockly.ContextMenuRegistry.Scope),
* scope: !Blockly.ContextMenuRegistry.Scope,
* callback: function(!ContextMenuRegistry.Scope),
* scope: !ContextMenuRegistry.Scope,
* weight: number
* }}
*/
Blockly.ContextMenuRegistry.ContextMenuOption;
ContextMenuRegistry.ContextMenuOption;
/**
* Singleton instance of this class. All interactions with this class should be
* done on this object.
* @type {?Blockly.ContextMenuRegistry}
* @type {?ContextMenuRegistry}
*/
Blockly.ContextMenuRegistry.registry = null;
ContextMenuRegistry.registry = null;
/**
* Registers a RegistryItem.
* @param {!Blockly.ContextMenuRegistry.RegistryItem} item Context menu item to register.
* @param {!ContextMenuRegistry.RegistryItem} item Context menu item to
* register.
* @throws {Error} if an item with the given ID already exists.
*/
Blockly.ContextMenuRegistry.prototype.register = function(item) {
ContextMenuRegistry.prototype.register = function(item) {
if (this.registry_[item.id]) {
throw Error('Menu item with ID "' + item.id + '" is already registered.');
}
@@ -107,7 +113,7 @@ Blockly.ContextMenuRegistry.prototype.register = function(item) {
* @param {string} id The ID of the RegistryItem to remove.
* @throws {Error} if an item with the given ID does not exist.
*/
Blockly.ContextMenuRegistry.prototype.unregister = function(id) {
ContextMenuRegistry.prototype.unregister = function(id) {
if (!this.registry_[id]) {
throw new Error('Menu item with ID "' + id + '" not found.');
}
@@ -116,33 +122,37 @@ Blockly.ContextMenuRegistry.prototype.unregister = function(id) {
/**
* @param {string} id The ID of the RegistryItem to get.
* @return {?Blockly.ContextMenuRegistry.RegistryItem} RegistryItem or null if not found
* @return {?ContextMenuRegistry.RegistryItem} RegistryItem or null if not found
*/
Blockly.ContextMenuRegistry.prototype.getItem = function(id) {
ContextMenuRegistry.prototype.getItem = function(id) {
return this.registry_[id] || null;
};
/**
* Gets the valid context menu options for the given scope type (e.g. block or workspace) and scope.
* Blocks are only shown if the preconditionFn shows they should not be hidden.
* @param {!Blockly.ContextMenuRegistry.ScopeType} scopeType Type of scope where menu should be
* shown (e.g. on a block or on a workspace)
* @param {!Blockly.ContextMenuRegistry.Scope} scope Current scope of context menu
* Gets the valid context menu options for the given scope type (e.g. block or
* workspace) and scope. Blocks are only shown if the preconditionFn shows they
* should not be hidden.
* @param {!ContextMenuRegistry.ScopeType} scopeType Type of scope where menu
* should be shown (e.g. on a block or on a workspace)
* @param {!ContextMenuRegistry.Scope} scope Current scope of context menu
* (i.e., the exact workspace or block being clicked on)
* @return {!Array<!Blockly.ContextMenuRegistry.ContextMenuOption>} the list of ContextMenuOptions
* @return {!Array<!ContextMenuRegistry.ContextMenuOption>} the list of
* ContextMenuOptions
*/
Blockly.ContextMenuRegistry.prototype.getContextMenuOptions = function(scopeType, scope) {
var menuOptions = [];
var registry = this.registry_;
ContextMenuRegistry.prototype.getContextMenuOptions = function(
scopeType, scope) {
const menuOptions = [];
const registry = this.registry_;
Object.keys(registry).forEach(function(id) {
var item = registry[id];
const item = registry[id];
if (scopeType == item.scopeType) {
var precondition = item.preconditionFn(scope);
const precondition = item.preconditionFn(scope);
if (precondition != 'hidden') {
var displayText = typeof item.displayText == 'function' ?
item.displayText(scope) : item.displayText;
/** @type {!Blockly.ContextMenuRegistry.ContextMenuOption} */
var menuOption = {
const displayText = typeof item.displayText == 'function' ?
item.displayText(scope) :
item.displayText;
/** @type {!ContextMenuRegistry.ContextMenuOption} */
const menuOption = {
text: displayText,
enabled: (precondition == 'enabled'),
callback: item.callback,
@@ -160,4 +170,6 @@ Blockly.ContextMenuRegistry.prototype.getContextMenuOptions = function(scopeType
};
// Creates and assigns the singleton instance.
new Blockly.ContextMenuRegistry();
new ContextMenuRegistry();
exports = ContextMenuRegistry;

View File

@@ -18,10 +18,10 @@ goog.module.declareLegacyNamespace();
const BlockSvg = goog.require('Blockly.BlockSvg');
const DragTarget = goog.require('Blockly.DragTarget');
/* eslint-disable-next-line no-unused-vars */
const IDeleteArea = goog.require('Blockly.IDeleteArea');
const IDeleteArea = goog.requireType('Blockly.IDeleteArea');
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.requireType('Blockly.IDraggable');
const {inherits} = goog.require('Blockly.utils.object');
const object = goog.require('Blockly.utils.object');
/**
* Abstract class for a component that can delete a block or bubble that is
@@ -42,7 +42,7 @@ const DeleteArea = function() {
*/
this.wouldDelete_ = false;
};
inherits(DeleteArea, DragTarget);
object.inherits(DeleteArea, DragTarget);
/**
* Returns whether the provided block or bubble would be deleted if dropped on

View File

@@ -16,7 +16,7 @@ goog.module('Blockly.DragTarget');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IDragTarget = goog.require('Blockly.IDragTarget');
const IDragTarget = goog.requireType('Blockly.IDragTarget');
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.requireType('Blockly.IDraggable');
/* eslint-disable-next-line no-unused-vars */

View File

@@ -15,6 +15,7 @@
goog.provide('Blockly.DropDownDiv');
goog.require('Blockly.common');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.math');
goog.require('Blockly.utils.Rect');
@@ -162,7 +163,7 @@ Blockly.DropDownDiv.createDom = function() {
}
var div = document.createElement('div');
div.className = 'blocklyDropDownDiv';
var container = Blockly.parentContainer || document.body;
var container = Blockly.common.getParentContainer() || document.body;
container.appendChild(div);
/**
* The div element.
@@ -381,7 +382,7 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY,
div.style.direction = rtl ? 'rtl' : 'ltr';
var mainWorkspace =
/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace());
/** @type {!Blockly.WorkspaceSvg} */ (Blockly.common.getMainWorkspace());
Blockly.DropDownDiv.rendererClassName_ =
mainWorkspace.getRenderer().getClassName();
Blockly.DropDownDiv.themeClassName_ = mainWorkspace.getTheme().getClassName();
@@ -694,7 +695,7 @@ Blockly.DropDownDiv.hideWithoutAnimation = function() {
Blockly.DropDownDiv.themeClassName_ = '';
}
(/** @type {!Blockly.WorkspaceSvg} */ (
Blockly.getMainWorkspace())).markFocused();
Blockly.common.getMainWorkspace())).markFocused();
};
/**

View File

@@ -1,573 +0,0 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Classes for all types of block events.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.provide('Blockly.Events.BlockBase');
goog.provide('Blockly.Events.BlockChange');
goog.provide('Blockly.Events.BlockCreate');
goog.provide('Blockly.Events.BlockDelete');
goog.provide('Blockly.Events.BlockMove');
goog.provide('Blockly.Events.Change'); // Deprecated.
goog.provide('Blockly.Events.Create'); // Deprecated.
goog.provide('Blockly.Events.Delete'); // Deprecated.
goog.provide('Blockly.Events.Move'); // Deprecated.
goog.require('Blockly.connectionTypes');
goog.require('Blockly.Events');
goog.require('Blockly.Events.Abstract');
goog.require('Blockly.registry');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.xml');
goog.require('Blockly.Xml');
goog.requireType('Blockly.Block');
/**
* Abstract class for a block event.
* @param {!Blockly.Block=} opt_block The block this event corresponds to.
* Undefined for a blank event.
* @extends {Blockly.Events.Abstract}
* @constructor
*/
Blockly.Events.BlockBase = function(opt_block) {
Blockly.Events.BlockBase.superClass_.constructor.call(this);
this.isBlank = typeof opt_block == 'undefined';
/**
* The block ID for the block this event pertains to
* @type {string}
*/
this.blockId = this.isBlank ? '' : opt_block.id;
/**
* The workspace identifier for this event.
* @type {string}
*/
this.workspaceId = this.isBlank ? '' : opt_block.workspace.id;
};
Blockly.utils.object.inherits(Blockly.Events.BlockBase,
Blockly.Events.Abstract);
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.BlockBase.prototype.toJson = function() {
var json = Blockly.Events.BlockBase.superClass_.toJson.call(this);
json['blockId'] = this.blockId;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.BlockBase.prototype.fromJson = function(json) {
Blockly.Events.BlockBase.superClass_.fromJson.call(this, json);
this.blockId = json['blockId'];
};
/**
* Class for a block change event.
* @param {!Blockly.Block=} opt_block The changed block. Undefined for a blank
* event.
* @param {string=} opt_element One of 'field', 'comment', 'disabled', etc.
* @param {?string=} opt_name Name of input or field affected, or null.
* @param {*=} opt_oldValue Previous value of element.
* @param {*=} opt_newValue New value of element.
* @extends {Blockly.Events.BlockBase}
* @constructor
*/
Blockly.Events.BlockChange = function(opt_block, opt_element, opt_name, opt_oldValue,
opt_newValue) {
Blockly.Events.Change.superClass_.constructor.call(this, opt_block);
if (!opt_block) {
return; // Blank event to be populated by fromJson.
}
this.element = typeof opt_element == 'undefined' ? '' : opt_element;
this.name = typeof opt_name == 'undefined' ? '' : opt_name;
this.oldValue = typeof opt_oldValue == 'undefined' ? '' : opt_oldValue;
this.newValue = typeof opt_newValue == 'undefined' ? '' : opt_newValue;
};
Blockly.utils.object.inherits(Blockly.Events.BlockChange, Blockly.Events.BlockBase);
/**
* Class for a block change event.
* @param {!Blockly.Block=} opt_block The changed block. Undefined for a blank
* event.
* @param {string=} opt_element One of 'field', 'comment', 'disabled', etc.
* @param {?string=} opt_name Name of input or field affected, or null.
* @param {*=} opt_oldValue Previous value of element.
* @param {*=} opt_newValue New value of element.
* @extends {Blockly.Events.BlockBase}
* @constructor
*/
Blockly.Events.Change = Blockly.Events.BlockChange;
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.BlockChange.prototype.type = Blockly.Events.CHANGE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.BlockChange.prototype.toJson = function() {
var json = Blockly.Events.BlockChange.superClass_.toJson.call(this);
json['element'] = this.element;
if (this.name) {
json['name'] = this.name;
}
json['oldValue'] = this.oldValue;
json['newValue'] = this.newValue;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.BlockChange.prototype.fromJson = function(json) {
Blockly.Events.BlockChange.superClass_.fromJson.call(this, json);
this.element = json['element'];
this.name = json['name'];
this.oldValue = json['oldValue'];
this.newValue = json['newValue'];
};
/**
* Does this event record any change of state?
* @return {boolean} False if something changed.
*/
Blockly.Events.BlockChange.prototype.isNull = function() {
return this.oldValue == this.newValue;
};
/**
* Run a change event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
Blockly.Events.BlockChange.prototype.run = function(forward) {
var workspace = this.getEventWorkspace_();
var block = workspace.getBlockById(this.blockId);
if (!block) {
console.warn("Can't change non-existent block: " + this.blockId);
return;
}
if (block.mutator) {
// Close the mutator (if open) since we don't want to update it.
block.mutator.setVisible(false);
}
var value = forward ? this.newValue : this.oldValue;
switch (this.element) {
case 'field':
var field = block.getField(this.name);
if (field) {
field.setValue(value);
} else {
console.warn("Can't set non-existent field: " + this.name);
}
break;
case 'comment':
block.setCommentText(/** @type {string} */ (value) || null);
break;
case 'collapsed':
block.setCollapsed(!!value);
break;
case 'disabled':
block.setEnabled(!value);
break;
case 'inline':
block.setInputsInline(!!value);
break;
case 'mutation':
var oldMutation = '';
if (block.mutationToDom) {
var oldMutationDom = block.mutationToDom();
oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
}
if (block.domToMutation) {
var dom = Blockly.Xml.textToDom(/** @type {string} */ (value) || '<mutation/>');
block.domToMutation(dom);
}
Blockly.Events.fire(new Blockly.Events.BlockChange(
block, 'mutation', null, oldMutation, value));
break;
default:
console.warn('Unknown change type: ' + this.element);
}
};
/**
* Class for a block creation event.
* @param {!Blockly.Block=} opt_block The created block. Undefined for a blank
* event.
* @extends {Blockly.Events.BlockBase}
* @constructor
*/
Blockly.Events.Create = function(opt_block) {
Blockly.Events.Create.superClass_.constructor.call(this, opt_block);
if (!opt_block) {
return; // Blank event to be populated by fromJson.
}
if (opt_block.isShadow()) {
// Moving shadow blocks is handled via disconnection.
this.recordUndo = false;
}
if (opt_block.workspace.rendered) {
this.xml = Blockly.Xml.blockToDomWithXY(opt_block);
} else {
this.xml = Blockly.Xml.blockToDom(opt_block);
}
this.ids = Blockly.Events.getDescendantIds(opt_block);
};
Blockly.utils.object.inherits(Blockly.Events.Create, Blockly.Events.BlockBase);
/**
* Class for a block creation event.
* @param {!Blockly.Block=} block The created block. Undefined for a blank
* event.
* @extends {Blockly.Events.BlockBase}
* @constructor
*/
Blockly.Events.BlockCreate = Blockly.Events.Create;
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.Create.prototype.type = Blockly.Events.CREATE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.Create.prototype.toJson = function() {
var json = Blockly.Events.Create.superClass_.toJson.call(this);
json['xml'] = Blockly.Xml.domToText(this.xml);
json['ids'] = this.ids;
if (!this.recordUndo) {
json['recordUndo'] = this.recordUndo;
}
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.Create.prototype.fromJson = function(json) {
Blockly.Events.Create.superClass_.fromJson.call(this, json);
this.xml = Blockly.Xml.textToDom(json['xml']);
this.ids = json['ids'];
if (json['recordUndo'] !== undefined) {
this.recordUndo = json['recordUndo'];
}
};
/**
* Run a creation event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
Blockly.Events.Create.prototype.run = function(forward) {
var workspace = this.getEventWorkspace_();
if (forward) {
var xml = Blockly.utils.xml.createElement('xml');
xml.appendChild(this.xml);
Blockly.Xml.domToWorkspace(xml, workspace);
} else {
for (var i = 0, id; (id = this.ids[i]); i++) {
var block = workspace.getBlockById(id);
if (block) {
block.dispose(false);
} else if (id == this.blockId) {
// Only complain about root-level block.
console.warn("Can't uncreate non-existent block: " + id);
}
}
}
};
/**
* Class for a block deletion event.
* @param {!Blockly.Block=} opt_block The deleted block. Undefined for a blank
* event.
* @extends {Blockly.Events.BlockBase}
* @constructor
*/
Blockly.Events.Delete = function(opt_block) {
Blockly.Events.Delete.superClass_.constructor.call(this, opt_block);
if (!opt_block) {
return; // Blank event to be populated by fromJson.
}
if (opt_block.getParent()) {
throw Error('Connected blocks cannot be deleted.');
}
if (opt_block.isShadow()) {
// Respawning shadow blocks is handled via disconnection.
this.recordUndo = false;
}
if (opt_block.workspace.rendered) {
this.oldXml = Blockly.Xml.blockToDomWithXY(opt_block);
} else {
this.oldXml = Blockly.Xml.blockToDom(opt_block);
}
this.ids = Blockly.Events.getDescendantIds(opt_block);
};
Blockly.utils.object.inherits(Blockly.Events.Delete, Blockly.Events.BlockBase);
/**
* Class for a block deletion event.
* @param {?Blockly.Block} block The deleted block. Null for a blank event.
* @extends {Blockly.Events.BlockBase}
* @constructor
*/
Blockly.Events.BlockDelete = Blockly.Events.Delete;
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.Delete.prototype.type = Blockly.Events.DELETE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.Delete.prototype.toJson = function() {
var json = Blockly.Events.Delete.superClass_.toJson.call(this);
json['oldXml'] = Blockly.Xml.domToText(this.oldXml);
json['ids'] = this.ids;
if (!this.recordUndo) {
json['recordUndo'] = this.recordUndo;
}
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.Delete.prototype.fromJson = function(json) {
Blockly.Events.Delete.superClass_.fromJson.call(this, json);
this.oldXml = Blockly.Xml.textToDom(json['oldXml']);
this.ids = json['ids'];
if (json['recordUndo'] !== undefined) {
this.recordUndo = json['recordUndo'];
}
};
/**
* Run a deletion event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
Blockly.Events.Delete.prototype.run = function(forward) {
var workspace = this.getEventWorkspace_();
if (forward) {
for (var i = 0, id; (id = this.ids[i]); i++) {
var block = workspace.getBlockById(id);
if (block) {
block.dispose(false);
} else if (id == this.blockId) {
// Only complain about root-level block.
console.warn("Can't delete non-existent block: " + id);
}
}
} else {
var xml = Blockly.utils.xml.createElement('xml');
xml.appendChild(this.oldXml);
Blockly.Xml.domToWorkspace(xml, workspace);
}
};
/**
* Class for a block move event. Created before the move.
* @param {!Blockly.Block=} opt_block The moved block. Undefined for a blank
* event.
* @extends {Blockly.Events.BlockBase}
* @constructor
*/
Blockly.Events.Move = function(opt_block) {
Blockly.Events.Move.superClass_.constructor.call(this, opt_block);
if (!opt_block) {
return; // Blank event to be populated by fromJson.
}
if (opt_block.isShadow()) {
// Moving shadow blocks is handled via disconnection.
this.recordUndo = false;
}
var location = this.currentLocation_();
this.oldParentId = location.parentId;
this.oldInputName = location.inputName;
this.oldCoordinate = location.coordinate;
};
Blockly.utils.object.inherits(Blockly.Events.Move, Blockly.Events.BlockBase);
/**
* Class for a block move event. Created before the move.
* @param {?Blockly.Block} block The moved block. Null for a blank event.
* @extends {Blockly.Events.BlockBase}
* @constructor
*/
Blockly.Events.BlockMove = Blockly.Events.Move;
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.Move.prototype.type = Blockly.Events.MOVE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.Move.prototype.toJson = function() {
var json = Blockly.Events.Move.superClass_.toJson.call(this);
if (this.newParentId) {
json['newParentId'] = this.newParentId;
}
if (this.newInputName) {
json['newInputName'] = this.newInputName;
}
if (this.newCoordinate) {
json['newCoordinate'] = Math.round(this.newCoordinate.x) + ',' +
Math.round(this.newCoordinate.y);
}
if (!this.recordUndo) {
json['recordUndo'] = this.recordUndo;
}
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.Move.prototype.fromJson = function(json) {
Blockly.Events.Move.superClass_.fromJson.call(this, json);
this.newParentId = json['newParentId'];
this.newInputName = json['newInputName'];
if (json['newCoordinate']) {
var xy = json['newCoordinate'].split(',');
this.newCoordinate =
new Blockly.utils.Coordinate(Number(xy[0]), Number(xy[1]));
}
if (json['recordUndo'] !== undefined) {
this.recordUndo = json['recordUndo'];
}
};
/**
* Record the block's new location. Called after the move.
*/
Blockly.Events.Move.prototype.recordNew = function() {
var location = this.currentLocation_();
this.newParentId = location.parentId;
this.newInputName = location.inputName;
this.newCoordinate = location.coordinate;
};
/**
* Returns the parentId and input if the block is connected,
* or the XY location if disconnected.
* @return {!Object} Collection of location info.
* @private
*/
Blockly.Events.Move.prototype.currentLocation_ = function() {
var workspace = this.getEventWorkspace_();
var block = workspace.getBlockById(this.blockId);
var location = {};
var parent = block.getParent();
if (parent) {
location.parentId = parent.id;
var input = parent.getInputWithBlock(block);
if (input) {
location.inputName = input.name;
}
} else {
location.coordinate = block.getRelativeToSurfaceXY();
}
return location;
};
/**
* Does this event record any change of state?
* @return {boolean} False if something changed.
*/
Blockly.Events.Move.prototype.isNull = function() {
return this.oldParentId == this.newParentId &&
this.oldInputName == this.newInputName &&
Blockly.utils.Coordinate.equals(this.oldCoordinate, this.newCoordinate);
};
/**
* Run a move event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
Blockly.Events.Move.prototype.run = function(forward) {
var workspace = this.getEventWorkspace_();
var block = workspace.getBlockById(this.blockId);
if (!block) {
console.warn("Can't move non-existent block: " + this.blockId);
return;
}
var parentId = forward ? this.newParentId : this.oldParentId;
var inputName = forward ? this.newInputName : this.oldInputName;
var coordinate = forward ? this.newCoordinate : this.oldCoordinate;
var parentBlock = null;
if (parentId) {
parentBlock = workspace.getBlockById(parentId);
if (!parentBlock) {
console.warn("Can't connect to non-existent block: " + parentId);
return;
}
}
if (block.getParent()) {
block.unplug();
}
if (coordinate) {
var xy = block.getRelativeToSurfaceXY();
block.moveBy(coordinate.x - xy.x, coordinate.y - xy.y);
} else {
var blockConnection = block.outputConnection || block.previousConnection;
var parentConnection;
var connectionType = blockConnection.type;
if (inputName) {
var input = parentBlock.getInput(inputName);
if (input) {
parentConnection = input.connection;
}
} else if (connectionType == Blockly.connectionTypes.PREVIOUS_STATEMENT) {
parentConnection = parentBlock.nextConnection;
}
if (parentConnection) {
blockConnection.connect(parentConnection);
} else {
console.warn("Can't connect to non-existent input: " + inputName);
}
}
};
Blockly.registry.register(Blockly.registry.Type.EVENT, Blockly.Events.CREATE,
Blockly.Events.Create);
Blockly.registry.register(Blockly.registry.Type.EVENT, Blockly.Events.DELETE,
Blockly.Events.Delete);
Blockly.registry.register(Blockly.registry.Type.EVENT, Blockly.Events.CHANGE,
Blockly.Events.BlockChange);
Blockly.registry.register(Blockly.registry.Type.EVENT, Blockly.Events.MOVE,
Blockly.Events.Move);

View File

@@ -11,19 +11,19 @@
*/
'use strict';
goog.provide('Blockly.Events.Abstract');
goog.module('Blockly.Events.Abstract');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.requireType('Blockly.Workspace');
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
/**
* Abstract class for an event.
* @constructor
*/
Blockly.Events.Abstract = function() {
const Abstract = function() {
/**
* Whether or not the event is blank (to be populated by fromJson).
* @type {?boolean}
@@ -42,29 +42,27 @@ Blockly.Events.Abstract = function() {
* perspective, and should be undone together.
* @type {string}
*/
this.group = Blockly.Events.getGroup();
this.group = Events.getGroup();
/**
* Sets whether the event should be added to the undo stack.
* @type {boolean}
*/
this.recordUndo = Blockly.Events.recordUndo;
this.recordUndo = Events.recordUndo;
};
/**
* Whether or not the event is a UI event.
* @type {boolean}
*/
Blockly.Events.Abstract.prototype.isUiEvent = false;
Abstract.prototype.isUiEvent = false;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.Abstract.prototype.toJson = function() {
var json = {
'type': this.type
};
Abstract.prototype.toJson = function() {
const json = {'type': this.type};
if (this.group) {
json['group'] = this.group;
}
@@ -75,7 +73,7 @@ Blockly.Events.Abstract.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.Abstract.prototype.fromJson = function(json) {
Abstract.prototype.fromJson = function(json) {
this.isBlank = false;
this.group = json['group'];
};
@@ -84,7 +82,7 @@ Blockly.Events.Abstract.prototype.fromJson = function(json) {
* Does this event record any change of state?
* @return {boolean} True if null, false if something changed.
*/
Blockly.Events.Abstract.prototype.isNull = function() {
Abstract.prototype.isNull = function() {
return false;
};
@@ -92,23 +90,27 @@ Blockly.Events.Abstract.prototype.isNull = function() {
* Run an event.
* @param {boolean} _forward True if run forward, false if run backward (undo).
*/
Blockly.Events.Abstract.prototype.run = function(_forward) {
Abstract.prototype.run = function(_forward) {
// Defined by subclasses.
};
/**
* Get workspace the event belongs to.
* @return {!Blockly.Workspace} The workspace the event belongs to.
* @return {!Workspace} The workspace the event belongs to.
* @throws {Error} if workspace is null.
* @protected
*/
Blockly.Events.Abstract.prototype.getEventWorkspace_ = function() {
Abstract.prototype.getEventWorkspace_ = function() {
let workspace;
if (this.workspaceId) {
var workspace = Blockly.Workspace.getById(this.workspaceId);
workspace = goog.module.get('Blockly.Workspace').getById(this.workspaceId);
}
if (!workspace) {
throw Error('Workspace is null. Event must have been generated from real' +
throw Error(
'Workspace is null. Event must have been generated from real' +
' Blockly events.');
}
return workspace;
};
exports = Abstract;

View File

@@ -0,0 +1,66 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Base class for all types of block events.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.BlockBase');
goog.module.declareLegacyNamespace();
const Abstract = goog.require('Blockly.Events.Abstract');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const object = goog.require('Blockly.utils.object');
/**
* Abstract class for a block event.
* @param {!Block=} opt_block The block this event corresponds to.
* Undefined for a blank event.
* @extends {Abstract}
* @constructor
*/
const BlockBase = function(opt_block) {
BlockBase.superClass_.constructor.call(this);
this.isBlank = typeof opt_block == 'undefined';
/**
* The block ID for the block this event pertains to
* @type {string}
*/
this.blockId = this.isBlank ? '' : opt_block.id;
/**
* The workspace identifier for this event.
* @type {string}
*/
this.workspaceId = this.isBlank ? '' : opt_block.workspace.id;
};
object.inherits(BlockBase, Abstract);
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
BlockBase.prototype.toJson = function() {
const json = BlockBase.superClass_.toJson.call(this);
json['blockId'] = this.blockId;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
BlockBase.prototype.fromJson = function(json) {
BlockBase.superClass_.fromJson.call(this, json);
this.blockId = json['blockId'];
};
exports = BlockBase;

View File

@@ -0,0 +1,148 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Class for a block change event.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.BlockChange');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const Events = goog.require('Blockly.Events');
const Xml = goog.require('Blockly.Xml');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a block change event.
* @param {!Block=} opt_block The changed block. Undefined for a blank
* event.
* @param {string=} opt_element One of 'field', 'comment', 'disabled', etc.
* @param {?string=} opt_name Name of input or field affected, or null.
* @param {*=} opt_oldValue Previous value of element.
* @param {*=} opt_newValue New value of element.
* @extends {Events.BlockBase}
* @constructor
*/
const BlockChange = function(
opt_block, opt_element, opt_name, opt_oldValue, opt_newValue) {
BlockChange.superClass_.constructor.call(this, opt_block);
if (!opt_block) {
return; // Blank event to be populated by fromJson.
}
this.element = typeof opt_element == 'undefined' ? '' : opt_element;
this.name = typeof opt_name == 'undefined' ? '' : opt_name;
this.oldValue = typeof opt_oldValue == 'undefined' ? '' : opt_oldValue;
this.newValue = typeof opt_newValue == 'undefined' ? '' : opt_newValue;
};
object.inherits(BlockChange, Events.BlockBase);
/**
* Type of this event.
* @type {string}
*/
BlockChange.prototype.type = Events.BLOCK_CHANGE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
BlockChange.prototype.toJson = function() {
const json = BlockChange.superClass_.toJson.call(this);
json['element'] = this.element;
if (this.name) {
json['name'] = this.name;
}
json['oldValue'] = this.oldValue;
json['newValue'] = this.newValue;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
BlockChange.prototype.fromJson = function(json) {
BlockChange.superClass_.fromJson.call(this, json);
this.element = json['element'];
this.name = json['name'];
this.oldValue = json['oldValue'];
this.newValue = json['newValue'];
};
/**
* Does this event record any change of state?
* @return {boolean} False if something changed.
*/
BlockChange.prototype.isNull = function() {
return this.oldValue == this.newValue;
};
/**
* Run a change event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
BlockChange.prototype.run = function(forward) {
const workspace = this.getEventWorkspace_();
const block = workspace.getBlockById(this.blockId);
if (!block) {
console.warn('Can\'t change non-existent block: ' + this.blockId);
return;
}
if (block.mutator) {
// Close the mutator (if open) since we don't want to update it.
block.mutator.setVisible(false);
}
const value = forward ? this.newValue : this.oldValue;
switch (this.element) {
case 'field': {
const field = block.getField(this.name);
if (field) {
field.setValue(value);
} else {
console.warn('Can\'t set non-existent field: ' + this.name);
}
break;
}
case 'comment':
block.setCommentText(/** @type {string} */ (value) || null);
break;
case 'collapsed':
block.setCollapsed(!!value);
break;
case 'disabled':
block.setEnabled(!value);
break;
case 'inline':
block.setInputsInline(!!value);
break;
case 'mutation': {
let oldMutation = '';
if (block.mutationToDom) {
const oldMutationDom = block.mutationToDom();
oldMutation = oldMutationDom && Xml.domToText(oldMutationDom);
}
if (block.domToMutation) {
const dom = Xml.textToDom(/** @type {string} */
(value) || '<mutation/>');
block.domToMutation(dom);
}
Events.fire(new BlockChange(block, 'mutation', null, oldMutation, value));
break;
}
default:
console.warn('Unknown change type: ' + this.element);
}
};
registry.register(registry.Type.EVENT, Events.CHANGE, BlockChange);
exports = BlockChange;

View File

@@ -0,0 +1,111 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Class for a block creation event.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.BlockCreate');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const BlockBase = goog.require('Blockly.Events.BlockBase');
const Events = goog.require('Blockly.Events');
const Xml = goog.require('Blockly.Xml');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
const xml = goog.require('Blockly.utils.xml');
/**
* Class for a block creation event.
* @param {!Block=} opt_block The created block. Undefined for a blank
* event.
* @extends {BlockBase}
* @constructor
*/
const BlockCreate = function(opt_block) {
BlockCreate.superClass_.constructor.call(this, opt_block);
if (!opt_block) {
return; // Blank event to be populated by fromJson.
}
if (opt_block.isShadow()) {
// Moving shadow blocks is handled via disconnection.
this.recordUndo = false;
}
if (opt_block.workspace.rendered) {
this.xml = Xml.blockToDomWithXY(opt_block);
} else {
this.xml = Xml.blockToDom(opt_block);
}
this.ids = Events.getDescendantIds(opt_block);
};
object.inherits(BlockCreate, BlockBase);
/**
* Type of this event.
* @type {string}
*/
BlockCreate.prototype.type = Events.BLOCK_CREATE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
BlockCreate.prototype.toJson = function() {
const json = BlockCreate.superClass_.toJson.call(this);
json['xml'] = Xml.domToText(this.xml);
json['ids'] = this.ids;
if (!this.recordUndo) {
json['recordUndo'] = this.recordUndo;
}
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
BlockCreate.prototype.fromJson = function(json) {
BlockCreate.superClass_.fromJson.call(this, json);
this.xml = Xml.textToDom(json['xml']);
this.ids = json['ids'];
if (json['recordUndo'] !== undefined) {
this.recordUndo = json['recordUndo'];
}
};
/**
* Run a creation event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
BlockCreate.prototype.run = function(forward) {
const workspace = this.getEventWorkspace_();
if (forward) {
const xmlEl = xml.createElement('xml');
xmlEl.appendChild(this.xml);
Xml.domToWorkspace(xmlEl, workspace);
} else {
for (let i = 0; i < this.ids.length; i++) {
const id = this.ids[i];
const block = workspace.getBlockById(id);
if (block) {
block.dispose(false);
} else if (id == this.blockId) {
// Only complain about root-level block.
console.warn('Can\'t uncreate non-existent block: ' + id);
}
}
}
};
registry.register(registry.Type.EVENT, Events.CREATE, BlockCreate);
exports = BlockCreate;

View File

@@ -0,0 +1,114 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Class for a block delete event.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.BlockDelete');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const BlockBase = goog.require('Blockly.Events.BlockBase');
const Events = goog.require('Blockly.Events');
const Xml = goog.require('Blockly.Xml');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
const xml = goog.require('Blockly.utils.xml');
/**
* Class for a block deletion event.
* @param {!Block=} opt_block The deleted block. Undefined for a blank
* event.
* @extends {BlockBase}
* @constructor
*/
const BlockDelete = function(opt_block) {
BlockDelete.superClass_.constructor.call(this, opt_block);
if (!opt_block) {
return; // Blank event to be populated by fromJson.
}
if (opt_block.getParent()) {
throw Error('Connected blocks cannot be deleted.');
}
if (opt_block.isShadow()) {
// Respawning shadow blocks is handled via disconnection.
this.recordUndo = false;
}
if (opt_block.workspace.rendered) {
this.oldXml = Xml.blockToDomWithXY(opt_block);
} else {
this.oldXml = Xml.blockToDom(opt_block);
}
this.ids = Events.getDescendantIds(opt_block);
};
object.inherits(BlockDelete, BlockBase);
/**
* Type of this event.
* @type {string}
*/
BlockDelete.prototype.type = Events.BLOCK_DELETE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
BlockDelete.prototype.toJson = function() {
const json = BlockDelete.superClass_.toJson.call(this);
json['oldXml'] = Xml.domToText(this.oldXml);
json['ids'] = this.ids;
if (!this.recordUndo) {
json['recordUndo'] = this.recordUndo;
}
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
BlockDelete.prototype.fromJson = function(json) {
BlockDelete.superClass_.fromJson.call(this, json);
this.oldXml = Xml.textToDom(json['oldXml']);
this.ids = json['ids'];
if (json['recordUndo'] !== undefined) {
this.recordUndo = json['recordUndo'];
}
};
/**
* Run a deletion event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
BlockDelete.prototype.run = function(forward) {
const workspace = this.getEventWorkspace_();
if (forward) {
for (let i = 0; i < this.ids.length; i++) {
const id = this.ids[i];
const block = workspace.getBlockById(id);
if (block) {
block.dispose(false);
} else if (id == this.blockId) {
// Only complain about root-level block.
console.warn('Can\'t delete non-existent block: ' + id);
}
}
} else {
const xmlEl = xml.createElement('xml');
xmlEl.appendChild(this.oldXml);
Xml.domToWorkspace(xmlEl, workspace);
}
};
registry.register(registry.Type.EVENT, Events.DELETE, BlockDelete);
exports = BlockDelete;

View File

@@ -10,30 +10,31 @@
*/
'use strict';
goog.provide('Blockly.Events.BlockDrag');
goog.module('Blockly.Events.BlockDrag');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a block drag event.
* @param {!Blockly.Block=} opt_block The top block in the stack that is being
* @param {!Block=} opt_block The top block in the stack that is being
* dragged. Undefined for a blank event.
* @param {boolean=} opt_isStart Whether this is the start of a block drag.
* Undefined for a blank event.
* @param {!Array<!Blockly.Block>=} opt_blocks The blocks affected by this
* @param {!Array<!Block>=} opt_blocks The blocks affected by this
* drag. Undefined for a blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.BlockDrag = function(opt_block, opt_isStart, opt_blocks) {
var workspaceId = opt_block ? opt_block.workspace.id : undefined;
Blockly.Events.BlockDrag.superClass_.constructor.call(this, workspaceId);
const BlockDrag = function(opt_block, opt_isStart, opt_blocks) {
const workspaceId = opt_block ? opt_block.workspace.id : undefined;
BlockDrag.superClass_.constructor.call(this, workspaceId);
this.blockId = opt_block ? opt_block.id : null;
/**
@@ -44,24 +45,24 @@ Blockly.Events.BlockDrag = function(opt_block, opt_isStart, opt_blocks) {
/**
* The blocks affected by this drag event.
* @type {!Array<!Blockly.Block>|undefined}
* @type {!Array<!Block>|undefined}
*/
this.blocks = opt_blocks;
};
Blockly.utils.object.inherits(Blockly.Events.BlockDrag, Blockly.Events.UiBase);
object.inherits(BlockDrag, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.BlockDrag.prototype.type = Blockly.Events.BLOCK_DRAG;
BlockDrag.prototype.type = Events.BLOCK_DRAG;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.BlockDrag.prototype.toJson = function() {
var json = Blockly.Events.BlockDrag.superClass_.toJson.call(this);
BlockDrag.prototype.toJson = function() {
const json = BlockDrag.superClass_.toJson.call(this);
json['isStart'] = this.isStart;
json['blockId'] = this.blockId;
json['blocks'] = this.blocks;
@@ -72,12 +73,13 @@ Blockly.Events.BlockDrag.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.BlockDrag.prototype.fromJson = function(json) {
Blockly.Events.BlockDrag.superClass_.fromJson.call(this, json);
BlockDrag.prototype.fromJson = function(json) {
BlockDrag.superClass_.fromJson.call(this, json);
this.isStart = json['isStart'];
this.blockId = json['blockId'];
this.blocks = json['blocks'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.BLOCK_DRAG, Blockly.Events.BlockDrag);
registry.register(registry.Type.EVENT, Events.BLOCK_DRAG, BlockDrag);
exports = BlockDrag;

View File

@@ -0,0 +1,188 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Class for a block move event.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.BlockMove');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const BlockBase = goog.require('Blockly.Events.BlockBase');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
const connectionTypes = goog.require('Blockly.connectionTypes');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a block move event. Created before the move.
* @param {!Block=} opt_block The moved block. Undefined for a blank
* event.
* @extends {BlockBase}
* @constructor
*/
const BlockMove = function(opt_block) {
BlockMove.superClass_.constructor.call(this, opt_block);
if (!opt_block) {
return; // Blank event to be populated by fromJson.
}
if (opt_block.isShadow()) {
// Moving shadow blocks is handled via disconnection.
this.recordUndo = false;
}
const location = this.currentLocation_();
this.oldParentId = location.parentId;
this.oldInputName = location.inputName;
this.oldCoordinate = location.coordinate;
};
object.inherits(BlockMove, BlockBase);
/**
* Type of this event.
* @type {string}
*/
BlockMove.prototype.type = Events.BLOCK_MOVE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
BlockMove.prototype.toJson = function() {
const json = BlockMove.superClass_.toJson.call(this);
if (this.newParentId) {
json['newParentId'] = this.newParentId;
}
if (this.newInputName) {
json['newInputName'] = this.newInputName;
}
if (this.newCoordinate) {
json['newCoordinate'] = Math.round(this.newCoordinate.x) + ',' +
Math.round(this.newCoordinate.y);
}
if (!this.recordUndo) {
json['recordUndo'] = this.recordUndo;
}
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
BlockMove.prototype.fromJson = function(json) {
BlockMove.superClass_.fromJson.call(this, json);
this.newParentId = json['newParentId'];
this.newInputName = json['newInputName'];
if (json['newCoordinate']) {
const xy = json['newCoordinate'].split(',');
this.newCoordinate = new Coordinate(Number(xy[0]), Number(xy[1]));
}
if (json['recordUndo'] !== undefined) {
this.recordUndo = json['recordUndo'];
}
};
/**
* Record the block's new location. Called after the move.
*/
BlockMove.prototype.recordNew = function() {
const location = this.currentLocation_();
this.newParentId = location.parentId;
this.newInputName = location.inputName;
this.newCoordinate = location.coordinate;
};
/**
* Returns the parentId and input if the block is connected,
* or the XY location if disconnected.
* @return {!Object} Collection of location info.
* @private
*/
BlockMove.prototype.currentLocation_ = function() {
const workspace = this.getEventWorkspace_();
const block = workspace.getBlockById(this.blockId);
const location = {};
const parent = block.getParent();
if (parent) {
location.parentId = parent.id;
const input = parent.getInputWithBlock(block);
if (input) {
location.inputName = input.name;
}
} else {
location.coordinate = block.getRelativeToSurfaceXY();
}
return location;
};
/**
* Does this event record any change of state?
* @return {boolean} False if something changed.
*/
BlockMove.prototype.isNull = function() {
return this.oldParentId == this.newParentId &&
this.oldInputName == this.newInputName &&
Coordinate.equals(this.oldCoordinate, this.newCoordinate);
};
/**
* Run a move event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
BlockMove.prototype.run = function(forward) {
const workspace = this.getEventWorkspace_();
const block = workspace.getBlockById(this.blockId);
if (!block) {
console.warn('Can\'t move non-existent block: ' + this.blockId);
return;
}
const parentId = forward ? this.newParentId : this.oldParentId;
const inputName = forward ? this.newInputName : this.oldInputName;
const coordinate = forward ? this.newCoordinate : this.oldCoordinate;
let parentBlock;
if (parentId) {
parentBlock = workspace.getBlockById(parentId);
if (!parentBlock) {
console.warn('Can\'t connect to non-existent block: ' + parentId);
return;
}
}
if (block.getParent()) {
block.unplug();
}
if (coordinate) {
const xy = block.getRelativeToSurfaceXY();
block.moveBy(coordinate.x - xy.x, coordinate.y - xy.y);
} else {
const blockConnection = block.outputConnection || block.previousConnection;
let parentConnection;
const connectionType = blockConnection.type;
if (inputName) {
const input = parentBlock.getInput(inputName);
if (input) {
parentConnection = input.connection;
}
} else if (connectionType == connectionTypes.PREVIOUS_STATEMENT) {
parentConnection = parentBlock.nextConnection;
}
if (parentConnection) {
blockConnection.connect(parentConnection);
} else {
console.warn('Can\'t connect to non-existent input: ' + inputName);
}
}
};
registry.register(registry.Type.EVENT, Events.MOVE, BlockMove);
exports = BlockMove;

View File

@@ -10,30 +10,32 @@
*/
'use strict';
goog.provide('Blockly.Events.BubbleOpen');
goog.module('Blockly.Events.BubbleOpen');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a bubble open event.
* @param {Blockly.BlockSvg} opt_block The associated block. Undefined for a
* @param {BlockSvg} opt_block The associated block. Undefined for a
* blank event.
* @param {boolean=} opt_isOpen Whether the bubble is opening (false if
* closing). Undefined for a blank event.
* @param {string=} opt_bubbleType The type of bubble. One of 'mutator', 'comment'
* @param {string=} opt_bubbleType The type of bubble. One of 'mutator',
* 'comment'
* or 'warning'. Undefined for a blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.BubbleOpen = function(opt_block, opt_isOpen, opt_bubbleType) {
var workspaceId = opt_block ? opt_block.workspace.id : undefined;
Blockly.Events.BubbleOpen.superClass_.constructor.call(this, workspaceId);
const BubbleOpen = function(opt_block, opt_isOpen, opt_bubbleType) {
const workspaceId = opt_block ? opt_block.workspace.id : undefined;
BubbleOpen.superClass_.constructor.call(this, workspaceId);
this.blockId = opt_block ? opt_block.id : null;
/**
@@ -48,20 +50,20 @@ Blockly.Events.BubbleOpen = function(opt_block, opt_isOpen, opt_bubbleType) {
*/
this.bubbleType = opt_bubbleType;
};
Blockly.utils.object.inherits(Blockly.Events.BubbleOpen, Blockly.Events.UiBase);
object.inherits(BubbleOpen, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.BubbleOpen.prototype.type = Blockly.Events.BUBBLE_OPEN;
BubbleOpen.prototype.type = Events.BUBBLE_OPEN;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.BubbleOpen.prototype.toJson = function() {
var json = Blockly.Events.BubbleOpen.superClass_.toJson.call(this);
BubbleOpen.prototype.toJson = function() {
const json = BubbleOpen.superClass_.toJson.call(this);
json['isOpen'] = this.isOpen;
json['bubbleType'] = this.bubbleType;
json['blockId'] = this.blockId;
@@ -72,12 +74,13 @@ Blockly.Events.BubbleOpen.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.BubbleOpen.prototype.fromJson = function(json) {
Blockly.Events.BubbleOpen.superClass_.fromJson.call(this, json);
BubbleOpen.prototype.fromJson = function(json) {
BubbleOpen.superClass_.fromJson.call(this, json);
this.isOpen = json['isOpen'];
this.bubbleType = json['bubbleType'];
this.blockId = json['blockId'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.BUBBLE_OPEN, Blockly.Events.BubbleOpen);
registry.register(registry.Type.EVENT, Events.BUBBLE_OPEN, BubbleOpen);
exports = BubbleOpen;

View File

@@ -10,31 +10,31 @@
*/
'use strict';
goog.provide('Blockly.Events.Click');
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.Block');
goog.module('Blockly.Events.Click');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a click event.
* @param {?Blockly.Block=} opt_block The affected block. Null for click events
* @param {?Block=} opt_block The affected block. Null for click events
* that do not have an associated block (i.e. workspace click). Undefined
* for a blank event.
* @param {?string=} opt_workspaceId The workspace identifier for this event.
* Not used if block is passed. Undefined for a blank event.
* @param {string=} opt_targetType The type of element targeted by this click
* event. Undefined for a blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.Click = function(opt_block, opt_workspaceId, opt_targetType) {
var workspaceId = opt_block ? opt_block.workspace.id : opt_workspaceId;
Blockly.Events.Click.superClass_.constructor.call(this, workspaceId);
const Click = function(opt_block, opt_workspaceId, opt_targetType) {
const workspaceId = opt_block ? opt_block.workspace.id : opt_workspaceId;
Click.superClass_.constructor.call(this, workspaceId);
this.blockId = opt_block ? opt_block.id : null;
/**
@@ -43,20 +43,20 @@ Blockly.Events.Click = function(opt_block, opt_workspaceId, opt_targetType) {
*/
this.targetType = opt_targetType;
};
Blockly.utils.object.inherits(Blockly.Events.Click, Blockly.Events.UiBase);
object.inherits(Click, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.Click.prototype.type = Blockly.Events.CLICK;
Click.prototype.type = Events.CLICK;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.Click.prototype.toJson = function() {
var json = Blockly.Events.Click.superClass_.toJson.call(this);
Click.prototype.toJson = function() {
const json = Click.superClass_.toJson.call(this);
json['targetType'] = this.targetType;
if (this.blockId) {
json['blockId'] = this.blockId;
@@ -68,11 +68,12 @@ Blockly.Events.Click.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.Click.prototype.fromJson = function(json) {
Blockly.Events.Click.superClass_.fromJson.call(this, json);
Click.prototype.fromJson = function(json) {
Click.superClass_.fromJson.call(this, json);
this.targetType = json['targetType'];
this.blockId = json['blockId'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT, Blockly.Events.CLICK,
Blockly.Events.Click);
registry.register(registry.Type.EVENT, Events.CLICK, Click);
exports = Click;

View File

@@ -10,39 +10,39 @@
*/
'use strict';
goog.provide('Blockly.Events.MarkerMove');
goog.module('Blockly.Events.MarkerMove');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.ASTNode');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.Workspace');
const ASTNode = goog.require('Blockly.ASTNode');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a marker move event.
* @param {?Blockly.Block=} opt_block The affected block. Null if current node
* @param {?Block=} opt_block The affected block. Null if current node
* is of type workspace. Undefined for a blank event.
* @param {boolean=} isCursor Whether this is a cursor event. Undefined for a
* blank event.
* @param {?Blockly.ASTNode=} opt_oldNode The old node the marker used to be on.
* @param {?ASTNode=} opt_oldNode The old node the marker used to be on.
* Undefined for a blank event.
* @param {!Blockly.ASTNode=} opt_newNode The new node the marker is now on.
* @param {!ASTNode=} opt_newNode The new node the marker is now on.
* Undefined for a blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.MarkerMove = function(opt_block, isCursor, opt_oldNode,
opt_newNode) {
var workspaceId = opt_block ? opt_block.workspace.id : undefined;
if (opt_newNode && opt_newNode.getType() == Blockly.ASTNode.types.WORKSPACE) {
workspaceId =
(/** @type {!Blockly.Workspace} */ (opt_newNode.getLocation())).id;
const MarkerMove = function(opt_block, isCursor, opt_oldNode, opt_newNode) {
let workspaceId = opt_block ? opt_block.workspace.id : undefined;
if (opt_newNode && opt_newNode.getType() == ASTNode.types.WORKSPACE) {
workspaceId = (/** @type {!Workspace} */ (opt_newNode.getLocation())).id;
}
Blockly.Events.MarkerMove.superClass_.constructor.call(this, workspaceId);
MarkerMove.superClass_.constructor.call(this, workspaceId);
/**
* The workspace identifier for this event.
@@ -52,13 +52,13 @@ Blockly.Events.MarkerMove = function(opt_block, isCursor, opt_oldNode,
/**
* The old node the marker used to be on.
* @type {?Blockly.ASTNode|undefined}
* @type {?ASTNode|undefined}
*/
this.oldNode = opt_oldNode;
/**
* The new node the marker is now on.
* @type {Blockly.ASTNode|undefined}
* @type {ASTNode|undefined}
*/
this.newNode = opt_newNode;
@@ -68,20 +68,20 @@ Blockly.Events.MarkerMove = function(opt_block, isCursor, opt_oldNode,
*/
this.isCursor = isCursor;
};
Blockly.utils.object.inherits(Blockly.Events.MarkerMove, Blockly.Events.UiBase);
object.inherits(MarkerMove, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.MarkerMove.prototype.type = Blockly.Events.MARKER_MOVE;
MarkerMove.prototype.type = Events.MARKER_MOVE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.MarkerMove.prototype.toJson = function() {
var json = Blockly.Events.MarkerMove.superClass_.toJson.call(this);
MarkerMove.prototype.toJson = function() {
const json = MarkerMove.superClass_.toJson.call(this);
json['isCursor'] = this.isCursor;
json['blockId'] = this.blockId;
json['oldNode'] = this.oldNode;
@@ -93,13 +93,14 @@ Blockly.Events.MarkerMove.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.MarkerMove.prototype.fromJson = function(json) {
Blockly.Events.MarkerMove.superClass_.fromJson.call(this, json);
MarkerMove.prototype.fromJson = function(json) {
MarkerMove.superClass_.fromJson.call(this, json);
this.isCursor = json['isCursor'];
this.blockId = json['blockId'];
this.oldNode = json['oldNode'];
this.newNode = json['newNode'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.MARKER_MOVE, Blockly.Events.MarkerMove);
registry.register(registry.Type.EVENT, Events.MARKER_MOVE, MarkerMove);
exports = MarkerMove;

View File

@@ -10,12 +10,13 @@
*/
'use strict';
goog.provide('Blockly.Events.Selected');
goog.module('Blockly.Events.Selected');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
@@ -26,12 +27,11 @@ goog.require('Blockly.utils.object');
* element currently selected (deselect). Undefined for a blank event.
* @param {string=} opt_workspaceId The workspace identifier for this event.
* Null if no element previously selected. Undefined for a blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.Selected = function(opt_oldElementId, opt_newElementId,
opt_workspaceId) {
Blockly.Events.Selected.superClass_.constructor.call(this, opt_workspaceId);
const Selected = function(opt_oldElementId, opt_newElementId, opt_workspaceId) {
Selected.superClass_.constructor.call(this, opt_workspaceId);
/**
* The id of the last selected element.
@@ -45,20 +45,20 @@ Blockly.Events.Selected = function(opt_oldElementId, opt_newElementId,
*/
this.newElementId = opt_newElementId;
};
Blockly.utils.object.inherits(Blockly.Events.Selected, Blockly.Events.UiBase);
object.inherits(Selected, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.Selected.prototype.type = Blockly.Events.SELECTED;
Selected.prototype.type = Events.SELECTED;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.Selected.prototype.toJson = function() {
var json = Blockly.Events.Selected.superClass_.toJson.call(this);
Selected.prototype.toJson = function() {
const json = Selected.superClass_.toJson.call(this);
json['oldElementId'] = this.oldElementId;
json['newElementId'] = this.newElementId;
return json;
@@ -68,11 +68,12 @@ Blockly.Events.Selected.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.Selected.prototype.fromJson = function(json) {
Blockly.Events.Selected.superClass_.fromJson.call(this, json);
Selected.prototype.fromJson = function(json) {
Selected.superClass_.fromJson.call(this, json);
this.oldElementId = json['oldElementId'];
this.newElementId = json['newElementId'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT, Blockly.Events.SELECTED,
Blockly.Events.Selected);
registry.register(registry.Type.EVENT, Events.SELECTED, Selected);
exports = Selected;

View File

@@ -10,12 +10,13 @@
*/
'use strict';
goog.provide('Blockly.Events.ThemeChange');
goog.module('Blockly.Events.ThemeChange');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
@@ -23,11 +24,11 @@ goog.require('Blockly.utils.object');
* @param {string=} opt_themeName The theme name. Undefined for a blank event.
* @param {string=} opt_workspaceId The workspace identifier for this event.
* event. Undefined for a blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.ThemeChange = function(opt_themeName, opt_workspaceId) {
Blockly.Events.ThemeChange.superClass_.constructor.call(this, opt_workspaceId);
const ThemeChange = function(opt_themeName, opt_workspaceId) {
ThemeChange.superClass_.constructor.call(this, opt_workspaceId);
/**
* The theme name.
@@ -35,20 +36,20 @@ Blockly.Events.ThemeChange = function(opt_themeName, opt_workspaceId) {
*/
this.themeName = opt_themeName;
};
Blockly.utils.object.inherits(Blockly.Events.ThemeChange, Blockly.Events.UiBase);
object.inherits(ThemeChange, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.ThemeChange.prototype.type = Blockly.Events.THEME_CHANGE;
ThemeChange.prototype.type = Events.THEME_CHANGE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.ThemeChange.prototype.toJson = function() {
var json = Blockly.Events.ThemeChange.superClass_.toJson.call(this);
ThemeChange.prototype.toJson = function() {
const json = ThemeChange.superClass_.toJson.call(this);
json['themeName'] = this.themeName;
return json;
};
@@ -57,10 +58,11 @@ Blockly.Events.ThemeChange.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.ThemeChange.prototype.fromJson = function(json) {
Blockly.Events.ThemeChange.superClass_.fromJson.call(this, json);
ThemeChange.prototype.fromJson = function(json) {
ThemeChange.superClass_.fromJson.call(this, json);
this.themeName = json['themeName'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.THEME_CHANGE, Blockly.Events.ThemeChange);
registry.register(registry.Type.EVENT, Events.THEME_CHANGE, ThemeChange);
exports = ThemeChange;

View File

@@ -10,12 +10,13 @@
*/
'use strict';
goog.provide('Blockly.Events.ToolboxItemSelect');
goog.module('Blockly.Events.ToolboxItemSelect');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
@@ -26,13 +27,11 @@ goog.require('Blockly.utils.object');
* a blank event.
* @param {string=} opt_workspaceId The workspace identifier for this event.
* Undefined for a blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.ToolboxItemSelect = function(opt_oldItem, opt_newItem,
opt_workspaceId) {
Blockly.Events.ToolboxItemSelect.superClass_.constructor.call(
this, opt_workspaceId);
const ToolboxItemSelect = function(opt_oldItem, opt_newItem, opt_workspaceId) {
ToolboxItemSelect.superClass_.constructor.call(this, opt_workspaceId);
/**
* The previously selected toolbox item.
@@ -46,20 +45,20 @@ Blockly.Events.ToolboxItemSelect = function(opt_oldItem, opt_newItem,
*/
this.newItem = opt_newItem;
};
Blockly.utils.object.inherits(Blockly.Events.ToolboxItemSelect, Blockly.Events.UiBase);
object.inherits(ToolboxItemSelect, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.ToolboxItemSelect.prototype.type = Blockly.Events.TOOLBOX_ITEM_SELECT;
ToolboxItemSelect.prototype.type = Events.TOOLBOX_ITEM_SELECT;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.ToolboxItemSelect.prototype.toJson = function() {
var json = Blockly.Events.ToolboxItemSelect.superClass_.toJson.call(this);
ToolboxItemSelect.prototype.toJson = function() {
const json = ToolboxItemSelect.superClass_.toJson.call(this);
json['oldItem'] = this.oldItem;
json['newItem'] = this.newItem;
return json;
@@ -69,11 +68,13 @@ Blockly.Events.ToolboxItemSelect.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.ToolboxItemSelect.prototype.fromJson = function(json) {
Blockly.Events.ToolboxItemSelect.superClass_.fromJson.call(this, json);
ToolboxItemSelect.prototype.fromJson = function(json) {
ToolboxItemSelect.superClass_.fromJson.call(this, json);
this.oldItem = json['oldItem'];
this.newItem = json['newItem'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.TOOLBOX_ITEM_SELECT, Blockly.Events.ToolboxItemSelect);
registry.register(
registry.Type.EVENT, Events.TOOLBOX_ITEM_SELECT, ToolboxItemSelect);
exports = ToolboxItemSelect;

View File

@@ -10,12 +10,13 @@
*/
'use strict';
goog.provide('Blockly.Events.TrashcanOpen');
goog.module('Blockly.Events.TrashcanOpen');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
@@ -24,11 +25,11 @@ goog.require('Blockly.utils.object');
* opening). Undefined for a blank event.
* @param {string=} opt_workspaceId The workspace identifier for this event.
* Undefined for a blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.TrashcanOpen = function(opt_isOpen, opt_workspaceId) {
Blockly.Events.TrashcanOpen.superClass_.constructor.call(this, opt_workspaceId);
const TrashcanOpen = function(opt_isOpen, opt_workspaceId) {
TrashcanOpen.superClass_.constructor.call(this, opt_workspaceId);
/**
* Whether the trashcan flyout is opening (false if closing).
@@ -36,20 +37,20 @@ Blockly.Events.TrashcanOpen = function(opt_isOpen, opt_workspaceId) {
*/
this.isOpen = opt_isOpen;
};
Blockly.utils.object.inherits(Blockly.Events.TrashcanOpen, Blockly.Events.UiBase);
object.inherits(TrashcanOpen, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.TrashcanOpen.prototype.type = Blockly.Events.TRASHCAN_OPEN;
TrashcanOpen.prototype.type = Events.TRASHCAN_OPEN;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.TrashcanOpen.prototype.toJson = function() {
var json = Blockly.Events.TrashcanOpen.superClass_.toJson.call(this);
TrashcanOpen.prototype.toJson = function() {
const json = TrashcanOpen.superClass_.toJson.call(this);
json['isOpen'] = this.isOpen;
return json;
};
@@ -58,10 +59,11 @@ Blockly.Events.TrashcanOpen.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.TrashcanOpen.prototype.fromJson = function(json) {
Blockly.Events.TrashcanOpen.superClass_.fromJson.call(this, json);
TrashcanOpen.prototype.fromJson = function(json) {
TrashcanOpen.superClass_.fromJson.call(this, json);
this.isOpen = json['isOpen'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.TRASHCAN_OPEN, Blockly.Events.TrashcanOpen);
registry.register(registry.Type.EVENT, Events.TRASHCAN_OPEN, TrashcanOpen);
exports = TrashcanOpen;

83
core/events/events_ui.js Normal file
View File

@@ -0,0 +1,83 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview (Deprecated) Events fired as a result of UI actions in
* Blockly's editor.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
goog.module('Blockly.Events.Ui');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a UI event.
* @param {?Block=} opt_block The affected block. Null for UI events
* that do not have an associated block. Undefined for a blank event.
* @param {string=} opt_element One of 'selected', 'comment', 'mutatorOpen',
* etc.
* @param {*=} opt_oldValue Previous value of element.
* @param {*=} opt_newValue New value of element.
* @extends {UiBase}
* @deprecated December 2020. Instead use a more specific UI event.
* @constructor
*/
const Ui = function(opt_block, opt_element, opt_oldValue, opt_newValue) {
const workspaceId = opt_block ? opt_block.workspace.id : undefined;
Ui.superClass_.constructor.call(this, workspaceId);
this.blockId = opt_block ? opt_block.id : null;
this.element = typeof opt_element == 'undefined' ? '' : opt_element;
this.oldValue = typeof opt_oldValue == 'undefined' ? '' : opt_oldValue;
this.newValue = typeof opt_newValue == 'undefined' ? '' : opt_newValue;
};
object.inherits(Ui, UiBase);
/**
* Type of this event.
* @type {string}
*/
Ui.prototype.type = Events.UI;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Ui.prototype.toJson = function() {
const json = Ui.superClass_.toJson.call(this);
json['element'] = this.element;
if (this.newValue !== undefined) {
json['newValue'] = this.newValue;
}
if (this.blockId) {
json['blockId'] = this.blockId;
}
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Ui.prototype.fromJson = function(json) {
Ui.superClass_.fromJson.call(this, json);
this.element = json['element'];
this.newValue = json['newValue'];
this.blockId = json['blockId'];
};
registry.register(registry.Type.EVENT, Events.UI, Ui);
exports = Ui;

View File

@@ -0,0 +1,58 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Base class for events fired as a result of UI actions in
* Blockly's editor.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
goog.module('Blockly.Events.UiBase');
goog.module.declareLegacyNamespace();
const Abstract = goog.require('Blockly.Events.Abstract');
const object = goog.require('Blockly.utils.object');
/**
* Base class for a UI event.
* UI events are events that don't need to be sent over the wire for multi-user
* editing to work (e.g. scrolling the workspace, zooming, opening toolbox
* categories).
* UI events do not undo or redo.
* @param {string=} opt_workspaceId The workspace identifier for this event.
* Undefined for a blank event.
* @extends {Abstract}
* @constructor
*/
const UiBase = function(opt_workspaceId) {
UiBase.superClass_.constructor.call(this);
/**
* Whether or not the event is blank (to be populated by fromJson).
* @type {boolean}
*/
this.isBlank = typeof opt_workspaceId == 'undefined';
/**
* The workspace identifier for this event.
* @type {string}
*/
this.workspaceId = opt_workspaceId ? opt_workspaceId : '';
// UI events do not undo or redo.
this.recordUndo = false;
};
object.inherits(UiBase, Abstract);
/**
* Whether or not the event is a UI event.
* @type {boolean}
*/
UiBase.prototype.isUiEvent = true;
exports = UiBase;

View File

@@ -0,0 +1,66 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Abstract class for a variable event.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.VarBase');
goog.module.declareLegacyNamespace();
const Abstract = goog.require('Blockly.Events.Abstract');
/* eslint-disable-next-line no-unused-vars */
const VariableModel = goog.requireType('Blockly.VariableModel');
const object = goog.require('Blockly.utils.object');
/**
* Abstract class for a variable event.
* @param {!VariableModel=} opt_variable The variable this event
* corresponds to. Undefined for a blank event.
* @extends {Abstract}
* @constructor
*/
const VarBase = function(opt_variable) {
VarBase.superClass_.constructor.call(this);
this.isBlank = typeof opt_variable == 'undefined';
/**
* The variable id for the variable this event pertains to.
* @type {string}
*/
this.varId = this.isBlank ? '' : opt_variable.getId();
/**
* The workspace identifier for this event.
* @type {string}
*/
this.workspaceId = this.isBlank ? '' : opt_variable.workspace.id;
};
object.inherits(VarBase, Abstract);
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
VarBase.prototype.toJson = function() {
const json = VarBase.superClass_.toJson.call(this);
json['varId'] = this.varId;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
VarBase.prototype.fromJson = function(json) {
VarBase.superClass_.toJson.call(this);
this.varId = json['varId'];
};
exports = VarBase;

View File

@@ -0,0 +1,84 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Class for a variable creation event.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.VarCreate');
goog.module.declareLegacyNamespace();
const Events = goog.require('Blockly.Events');
const VarBase = goog.require('Blockly.Events.VarBase');
/* eslint-disable-next-line no-unused-vars */
const VariableModel = goog.requireType('Blockly.VariableModel');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a variable creation event.
* @param {!VariableModel=} opt_variable The created variable. Undefined
* for a blank event.
* @extends {VarBase}
* @constructor
*/
const VarCreate = function(opt_variable) {
VarCreate.superClass_.constructor.call(this, opt_variable);
if (!opt_variable) {
return; // Blank event to be populated by fromJson.
}
this.varType = opt_variable.type;
this.varName = opt_variable.name;
};
object.inherits(VarCreate, VarBase);
/**
* Type of this event.
* @type {string}
*/
VarCreate.prototype.type = Events.VAR_CREATE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
VarCreate.prototype.toJson = function() {
const json = VarCreate.superClass_.toJson.call(this);
json['varType'] = this.varType;
json['varName'] = this.varName;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
VarCreate.prototype.fromJson = function(json) {
VarCreate.superClass_.fromJson.call(this, json);
this.varType = json['varType'];
this.varName = json['varName'];
};
/**
* Run a variable creation event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
VarCreate.prototype.run = function(forward) {
const workspace = this.getEventWorkspace_();
if (forward) {
workspace.createVariable(this.varName, this.varType, this.varId);
} else {
workspace.deleteVariableById(this.varId);
}
};
registry.register(registry.Type.EVENT, Events.VAR_CREATE, VarCreate);
exports = VarCreate;

View File

@@ -0,0 +1,84 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Classes for all types of variable events.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.VarDelete');
goog.module.declareLegacyNamespace();
const Events = goog.require('Blockly.Events');
const VarBase = goog.require('Blockly.Events.VarBase');
/* eslint-disable-next-line no-unused-vars */
const VariableModel = goog.requireType('Blockly.VariableModel');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a variable deletion event.
* @param {!VariableModel=} opt_variable The deleted variable. Undefined
* for a blank event.
* @extends {VarBase}
* @constructor
*/
const VarDelete = function(opt_variable) {
VarDelete.superClass_.constructor.call(this, opt_variable);
if (!opt_variable) {
return; // Blank event to be populated by fromJson.
}
this.varType = opt_variable.type;
this.varName = opt_variable.name;
};
object.inherits(VarDelete, VarBase);
/**
* Type of this event.
* @type {string}
*/
VarDelete.prototype.type = Events.VAR_DELETE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
VarDelete.prototype.toJson = function() {
const json = VarDelete.superClass_.toJson.call(this);
json['varType'] = this.varType;
json['varName'] = this.varName;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
VarDelete.prototype.fromJson = function(json) {
VarDelete.superClass_.fromJson.call(this, json);
this.varType = json['varType'];
this.varName = json['varName'];
};
/**
* Run a variable deletion event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
VarDelete.prototype.run = function(forward) {
const workspace = this.getEventWorkspace_();
if (forward) {
workspace.deleteVariableById(this.varId);
} else {
workspace.createVariable(this.varName, this.varType, this.varId);
}
};
registry.register(registry.Type.EVENT, Events.VAR_DELETE, VarDelete);
exports = VarDelete;

View File

@@ -0,0 +1,85 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Class for a variable rename event.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.Events.VarRename');
goog.module.declareLegacyNamespace();
const Events = goog.require('Blockly.Events');
const VarBase = goog.require('Blockly.Events.VarBase');
/* eslint-disable-next-line no-unused-vars */
const VariableModel = goog.requireType('Blockly.VariableModel');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
* Class for a variable rename event.
* @param {!VariableModel=} opt_variable The renamed variable. Undefined
* for a blank event.
* @param {string=} newName The new name the variable will be changed to.
* @extends {VarBase}
* @constructor
*/
const VarRename = function(opt_variable, newName) {
VarRename.superClass_.constructor.call(this, opt_variable);
if (!opt_variable) {
return; // Blank event to be populated by fromJson.
}
this.oldName = opt_variable.name;
this.newName = typeof newName == 'undefined' ? '' : newName;
};
object.inherits(VarRename, VarBase);
/**
* Type of this event.
* @type {string}
*/
VarRename.prototype.type = Events.VAR_RENAME;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
VarRename.prototype.toJson = function() {
const json = VarRename.superClass_.toJson.call(this);
json['oldName'] = this.oldName;
json['newName'] = this.newName;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
VarRename.prototype.fromJson = function(json) {
VarRename.superClass_.fromJson.call(this, json);
this.oldName = json['oldName'];
this.newName = json['newName'];
};
/**
* Run a variable rename event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
VarRename.prototype.run = function(forward) {
const workspace = this.getEventWorkspace_();
if (forward) {
workspace.renameVariableById(this.varId, this.newName);
} else {
workspace.renameVariableById(this.varId, this.oldName);
}
};
registry.register(registry.Type.EVENT, Events.VAR_RENAME, VarRename);
exports = VarRename;

View File

@@ -10,12 +10,13 @@
*/
'use strict';
goog.provide('Blockly.Events.ViewportChange');
goog.module('Blockly.Events.ViewportChange');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.UiBase');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
const Events = goog.require('Blockly.Events');
const UiBase = goog.require('Blockly.Events.UiBase');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
@@ -30,12 +31,12 @@ goog.require('Blockly.utils.object');
* Undefined for a blank event.
* @param {number=} opt_oldScale The old scale of the workspace. Undefined for a
* blank event.
* @extends {Blockly.Events.UiBase}
* @extends {UiBase}
* @constructor
*/
Blockly.Events.ViewportChange = function(opt_top, opt_left, opt_scale,
opt_workspaceId, opt_oldScale) {
Blockly.Events.ViewportChange.superClass_.constructor.call(this, opt_workspaceId);
const ViewportChange = function(
opt_top, opt_left, opt_scale, opt_workspaceId, opt_oldScale) {
ViewportChange.superClass_.constructor.call(this, opt_workspaceId);
/**
* Top-edge of the visible portion of the workspace, relative to the workspace
@@ -63,21 +64,20 @@ Blockly.Events.ViewportChange = function(opt_top, opt_left, opt_scale,
*/
this.oldScale = opt_oldScale;
};
Blockly.utils.object.inherits(Blockly.Events.ViewportChange,
Blockly.Events.UiBase);
object.inherits(ViewportChange, UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.ViewportChange.prototype.type = Blockly.Events.VIEWPORT_CHANGE;
ViewportChange.prototype.type = Events.VIEWPORT_CHANGE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.ViewportChange.prototype.toJson = function() {
var json = Blockly.Events.ViewportChange.superClass_.toJson.call(this);
ViewportChange.prototype.toJson = function() {
const json = ViewportChange.superClass_.toJson.call(this);
json['viewTop'] = this.viewTop;
json['viewLeft'] = this.viewLeft;
json['scale'] = this.scale;
@@ -89,13 +89,14 @@ Blockly.Events.ViewportChange.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.ViewportChange.prototype.fromJson = function(json) {
Blockly.Events.ViewportChange.superClass_.fromJson.call(this, json);
ViewportChange.prototype.fromJson = function(json) {
ViewportChange.superClass_.fromJson.call(this, json);
this.viewTop = json['viewTop'];
this.viewLeft = json['viewLeft'];
this.scale = json['scale'];
this.oldScale = json['oldScale'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.VIEWPORT_CHANGE, Blockly.Events.ViewportChange);
registry.register(registry.Type.EVENT, Events.VIEWPORT_CHANGE, ViewportChange);
exports = ViewportChange;

View File

@@ -1,119 +0,0 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Events fired as a result of UI actions in Blockly's editor.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.Events.Ui');
goog.provide('Blockly.Events.UiBase');
goog.require('Blockly.Events');
goog.require('Blockly.Events.Abstract');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.Block');
/**
* Base class for a UI event.
* UI events are events that don't need to be sent over the wire for multi-user
* editing to work (e.g. scrolling the workspace, zooming, opening toolbox
* categories).
* UI events do not undo or redo.
* @param {string=} opt_workspaceId The workspace identifier for this event.
* Undefined for a blank event.
* @extends {Blockly.Events.Abstract}
* @constructor
*/
Blockly.Events.UiBase = function(opt_workspaceId) {
Blockly.Events.UiBase.superClass_.constructor.call(this);
/**
* Whether or not the event is blank (to be populated by fromJson).
* @type {boolean}
*/
this.isBlank = typeof opt_workspaceId == 'undefined';
/**
* The workspace identifier for this event.
* @type {string}
*/
this.workspaceId = opt_workspaceId ? opt_workspaceId : '';
// UI events do not undo or redo.
this.recordUndo = false;
};
Blockly.utils.object.inherits(Blockly.Events.UiBase, Blockly.Events.Abstract);
/**
* Whether or not the event is a UI event.
* @type {boolean}
*/
Blockly.Events.UiBase.prototype.isUiEvent = true;
/**
* Class for a UI event.
* @param {?Blockly.Block=} opt_block The affected block. Null for UI events
* that do not have an associated block. Undefined for a blank event.
* @param {string=} opt_element One of 'selected', 'comment', 'mutatorOpen',
* etc.
* @param {*=} opt_oldValue Previous value of element.
* @param {*=} opt_newValue New value of element.
* @extends {Blockly.Events.UiBase}
* @deprecated December 2020. Instead use a more specific UI event.
* @constructor
*/
Blockly.Events.Ui = function(opt_block, opt_element, opt_oldValue,
opt_newValue) {
var workspaceId = opt_block ? opt_block.workspace.id : undefined;
Blockly.Events.Ui.superClass_.constructor.call(this, workspaceId);
this.blockId = opt_block ? opt_block.id : null;
this.element = typeof opt_element == 'undefined' ? '' : opt_element;
this.oldValue = typeof opt_oldValue == 'undefined' ? '' : opt_oldValue;
this.newValue = typeof opt_newValue == 'undefined' ? '' : opt_newValue;
};
Blockly.utils.object.inherits(Blockly.Events.Ui, Blockly.Events.UiBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.Ui.prototype.type = Blockly.Events.UI;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.Ui.prototype.toJson = function() {
var json = Blockly.Events.Ui.superClass_.toJson.call(this);
json['element'] = this.element;
if (this.newValue !== undefined) {
json['newValue'] = this.newValue;
}
if (this.blockId) {
json['blockId'] = this.blockId;
}
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.Ui.prototype.fromJson = function(json) {
Blockly.Events.Ui.superClass_.fromJson.call(this, json);
this.element = json['element'];
this.newValue = json['newValue'];
this.blockId = json['blockId'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT, Blockly.Events.UI,
Blockly.Events.Ui);

View File

@@ -1,250 +0,0 @@
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Classes for all types of variable events.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.provide('Blockly.Events.VarBase');
goog.provide('Blockly.Events.VarCreate');
goog.provide('Blockly.Events.VarDelete');
goog.provide('Blockly.Events.VarRename');
goog.require('Blockly.Events');
goog.require('Blockly.Events.Abstract');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.VariableModel');
/**
* Abstract class for a variable event.
* @param {!Blockly.VariableModel=} opt_variable The variable this event
* corresponds to. Undefined for a blank event.
* @extends {Blockly.Events.Abstract}
* @constructor
*/
Blockly.Events.VarBase = function(opt_variable) {
Blockly.Events.VarBase.superClass_.constructor.call(this);
this.isBlank = typeof opt_variable == 'undefined';
/**
* The variable id for the variable this event pertains to.
* @type {string}
*/
this.varId = this.isBlank ? '' : opt_variable.getId();
/**
* The workspace identifier for this event.
* @type {string}
*/
this.workspaceId = this.isBlank ? '' : opt_variable.workspace.id;
};
Blockly.utils.object.inherits(Blockly.Events.VarBase, Blockly.Events.Abstract);
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.VarBase.prototype.toJson = function() {
var json = Blockly.Events.VarBase.superClass_.toJson.call(this);
json['varId'] = this.varId;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.VarBase.prototype.fromJson = function(json) {
Blockly.Events.VarBase.superClass_.toJson.call(this);
this.varId = json['varId'];
};
/**
* Class for a variable creation event.
* @param {!Blockly.VariableModel=} opt_variable The created variable. Undefined
* for a blank event.
* @extends {Blockly.Events.VarBase}
* @constructor
*/
Blockly.Events.VarCreate = function(opt_variable) {
Blockly.Events.VarCreate.superClass_.constructor.call(this, opt_variable);
if (!opt_variable) {
return; // Blank event to be populated by fromJson.
}
this.varType = opt_variable.type;
this.varName = opt_variable.name;
};
Blockly.utils.object.inherits(Blockly.Events.VarCreate, Blockly.Events.VarBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.VarCreate.prototype.type = Blockly.Events.VAR_CREATE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.VarCreate.prototype.toJson = function() {
var json = Blockly.Events.VarCreate.superClass_.toJson.call(this);
json['varType'] = this.varType;
json['varName'] = this.varName;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.VarCreate.prototype.fromJson = function(json) {
Blockly.Events.VarCreate.superClass_.fromJson.call(this, json);
this.varType = json['varType'];
this.varName = json['varName'];
};
/**
* Run a variable creation event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
Blockly.Events.VarCreate.prototype.run = function(forward) {
var workspace = this.getEventWorkspace_();
if (forward) {
workspace.createVariable(this.varName, this.varType, this.varId);
} else {
workspace.deleteVariableById(this.varId);
}
};
/**
* Class for a variable deletion event.
* @param {!Blockly.VariableModel=} opt_variable The deleted variable. Undefined
* for a blank event.
* @extends {Blockly.Events.VarBase}
* @constructor
*/
Blockly.Events.VarDelete = function(opt_variable) {
Blockly.Events.VarDelete.superClass_.constructor.call(this, opt_variable);
if (!opt_variable) {
return; // Blank event to be populated by fromJson.
}
this.varType = opt_variable.type;
this.varName = opt_variable.name;
};
Blockly.utils.object.inherits(Blockly.Events.VarDelete, Blockly.Events.VarBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.VarDelete.prototype.type = Blockly.Events.VAR_DELETE;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.VarDelete.prototype.toJson = function() {
var json = Blockly.Events.VarDelete.superClass_.toJson.call(this);
json['varType'] = this.varType;
json['varName'] = this.varName;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.VarDelete.prototype.fromJson = function(json) {
Blockly.Events.VarDelete.superClass_.fromJson.call(this, json);
this.varType = json['varType'];
this.varName = json['varName'];
};
/**
* Run a variable deletion event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
Blockly.Events.VarDelete.prototype.run = function(forward) {
var workspace = this.getEventWorkspace_();
if (forward) {
workspace.deleteVariableById(this.varId);
} else {
workspace.createVariable(this.varName, this.varType, this.varId);
}
};
/**
* Class for a variable rename event.
* @param {!Blockly.VariableModel=} opt_variable The renamed variable. Undefined
* for a blank event.
* @param {string=} newName The new name the variable will be changed to.
* @extends {Blockly.Events.VarBase}
* @constructor
*/
Blockly.Events.VarRename = function(opt_variable, newName) {
Blockly.Events.VarRename.superClass_.constructor.call(this, opt_variable);
if (!opt_variable) {
return; // Blank event to be populated by fromJson.
}
this.oldName = opt_variable.name;
this.newName = typeof newName == 'undefined' ? '' : newName;
};
Blockly.utils.object.inherits(Blockly.Events.VarRename, Blockly.Events.VarBase);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.VarRename.prototype.type = Blockly.Events.VAR_RENAME;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.VarRename.prototype.toJson = function() {
var json = Blockly.Events.VarRename.superClass_.toJson.call(this);
json['oldName'] = this.oldName;
json['newName'] = this.newName;
return json;
};
/**
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.VarRename.prototype.fromJson = function(json) {
Blockly.Events.VarRename.superClass_.fromJson.call(this, json);
this.oldName = json['oldName'];
this.newName = json['newName'];
};
/**
* Run a variable rename event.
* @param {boolean} forward True if run forward, false if run backward (undo).
*/
Blockly.Events.VarRename.prototype.run = function(forward) {
var workspace = this.getEventWorkspace_();
if (forward) {
workspace.renameVariableById(this.varId, this.newName);
} else {
workspace.renameVariableById(this.varId, this.oldName);
}
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.VAR_CREATE, Blockly.Events.VarCreate);
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.VAR_DELETE, Blockly.Events.VarDelete);
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.VAR_RENAME, Blockly.Events.VarRename);

View File

@@ -10,14 +10,15 @@
*/
'use strict';
goog.provide('Blockly.Events.FinishedLoading');
goog.module('Blockly.Events.FinishedLoading');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Events');
goog.require('Blockly.Events.Abstract');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.Workspace');
const Events = goog.require('Blockly.Events');
const EventsAbstract = goog.require('Blockly.Events.Abstract');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
@@ -25,13 +26,12 @@ goog.requireType('Blockly.Workspace');
* Used to notify the developer when the workspace has finished loading (i.e
* domToWorkspace).
* Finished loading events do not record undo or redo.
* @param {!Blockly.Workspace=} opt_workspace The workspace that has finished
* @param {!Workspace=} opt_workspace The workspace that has finished
* loading. Undefined for a blank event.
* @extends {Blockly.Events.Abstract}
* @extends {EventsAbstract}
* @constructor
*/
Blockly.Events.FinishedLoading = function(opt_workspace) {
const FinishedLoading = function(opt_workspace) {
/**
* Whether or not the event is blank (to be populated by fromJson).
* @type {boolean}
@@ -50,26 +50,25 @@ Blockly.Events.FinishedLoading = function(opt_workspace) {
* perspective, and should be undone together.
* @type {string}
*/
this.group = Blockly.Events.getGroup();
this.group = Events.getGroup();
// Workspace events do not undo or redo.
this.recordUndo = false;
};
Blockly.utils.object.inherits(Blockly.Events.FinishedLoading,
Blockly.Events.Abstract);
object.inherits(FinishedLoading, EventsAbstract);
/**
* Type of this event.
* @type {string}
*/
Blockly.Events.FinishedLoading.prototype.type = Blockly.Events.FINISHED_LOADING;
FinishedLoading.prototype.type = Events.FINISHED_LOADING;
/**
* Encode the event as JSON.
* @return {!Object} JSON representation.
*/
Blockly.Events.FinishedLoading.prototype.toJson = function() {
var json = {
FinishedLoading.prototype.toJson = function() {
const json = {
'type': this.type,
};
if (this.group) {
@@ -85,11 +84,13 @@ Blockly.Events.FinishedLoading.prototype.toJson = function() {
* Decode the JSON event.
* @param {!Object} json JSON representation.
*/
Blockly.Events.FinishedLoading.prototype.fromJson = function(json) {
FinishedLoading.prototype.fromJson = function(json) {
this.isBlank = false;
this.workspaceId = json['workspaceId'];
this.group = json['group'];
};
Blockly.registry.register(Blockly.registry.Type.EVENT,
Blockly.Events.FINISHED_LOADING, Blockly.Events.FinishedLoading);
registry.register(
registry.Type.EVENT, Events.FINISHED_LOADING, FinishedLoading);
exports = FinishedLoading;

View File

@@ -24,6 +24,8 @@ goog.require('Blockly.utils.object');
goog.require('Blockly.utils.xml');
goog.require('Blockly.Xml');
goog.requireType('Blockly.WorkspaceComment');
/**
* Abstract class for a comment event.
@@ -232,7 +234,7 @@ Blockly.Events.CommentCreateDeleteHelper = function(event, create) {
} else {
var comment = workspace.getCommentById(event.commentId);
if (comment) {
comment.dispose(false, false);
comment.dispose();
} else {
// Only complain about root-level block.
console.warn("Can't uncreate non-existent comment: " + event.commentId);

View File

@@ -22,7 +22,8 @@ goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const {checkMessageReferences, replaceMessageReferences, runAfterPageLoad} = goog.require('Blockly.utils');
const utils = goog.require('Blockly.utils');
goog.requireType('Blockly.Mutator');
/**
@@ -102,10 +103,11 @@ const registerMutator = function(name, mixinObj, opt_helperFn, opt_blockList) {
// Sanity checks passed.
register(name, function() {
if (hasMutatorDialog) {
if (!Blockly.Mutator) {
const Mutator = goog.module.get('Blockly.Mutator');
if (!Mutator) {
throw Error(errorPrefix + 'Missing require for Blockly.Mutator');
}
this.setMutator(new Blockly.Mutator(opt_blockList || []));
this.setMutator(new Mutator(opt_blockList || []));
}
// Mixin the object.
this.mixin(mixinObj);
@@ -331,13 +333,13 @@ const buildTooltipForDropdown = function(dropdownName, lookupTable) {
// Check the tooltip string messages for invalid references.
// Wait for load, in case Blockly.Msg is not yet populated.
// runAfterPageLoad() does not run in a Node.js environment due to lack of
// document object, in which case skip the validation.
// utils.runAfterPageLoad() does not run in a Node.js environment due to lack
// of document object, in which case skip the validation.
if (typeof document == 'object') { // Relies on document.readyState
runAfterPageLoad(function() {
utils.runAfterPageLoad(function() {
for (let key in lookupTable) {
// Will print warnings if reference is missing.
checkMessageReferences(lookupTable[key]);
utils.checkMessageReferences(lookupTable[key]);
}
});
}
@@ -366,7 +368,7 @@ const buildTooltipForDropdown = function(dropdownName, lookupTable) {
console.warn(warning + '.');
}
} else {
tooltip = replaceMessageReferences(tooltip);
tooltip = utils.replaceMessageReferences(tooltip);
}
return tooltip;
}.bind(this));
@@ -411,12 +413,12 @@ const checkDropdownOptionsInTable = function(block, dropdownName, lookupTable) {
const buildTooltipWithFieldText = function(msgTemplate, fieldName) {
// Check the tooltip string messages for invalid references.
// Wait for load, in case Blockly.Msg is not yet populated.
// runAfterPageLoad() does not run in a Node.js environment due to lack of
// document object, in which case skip the validation.
// utils.runAfterPageLoad() does not run in a Node.js environment due to lack
// of document object, in which case skip the validation.
if (typeof document == 'object') { // Relies on document.readyState
runAfterPageLoad(function() {
utils.runAfterPageLoad(function() {
// Will print warnings if reference is missing.
checkMessageReferences(msgTemplate);
utils.checkMessageReferences(msgTemplate);
});
}
@@ -427,7 +429,7 @@ const buildTooltipWithFieldText = function(msgTemplate, fieldName) {
const extensionFn = function() {
this.setTooltip(function() {
const field = this.getField(fieldName);
return replaceMessageReferences(msgTemplate)
return utils.replaceMessageReferences(msgTemplate)
.replace('%1', field ? field.getText() : '');
}.bind(this));
};

View File

@@ -26,15 +26,15 @@ const Coordinate = goog.requireType('Blockly.utils.Coordinate');
const DropDownDiv = goog.require('Blockly.DropDownDiv');
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const IASTNodeLocationSvg = goog.require('Blockly.IASTNodeLocationSvg');
const IASTNodeLocationSvg = goog.requireType('Blockly.IASTNodeLocationSvg');
/* eslint-disable-next-line no-unused-vars */
const IASTNodeLocationWithBlock = goog.require('Blockly.IASTNodeLocationWithBlock');
const IASTNodeLocationWithBlock = goog.requireType('Blockly.IASTNodeLocationWithBlock');
/* eslint-disable-next-line no-unused-vars */
const IKeyboardAccessible = goog.require('Blockly.IKeyboardAccessible');
/* eslint-disable-next-line no-unused-vars */
const IRegistrable = goog.require('Blockly.IRegistrable');
const IKeyboardAccessible = goog.requireType('Blockly.IKeyboardAccessible');
/* eslint-disable-next-line no-unused-vars */
const Input = goog.requireType('Blockly.Input');
/* eslint-disable-next-line no-unused-vars */
const IRegistrable = goog.requireType('Blockly.IRegistrable');
const MarkerManager = goog.require('Blockly.MarkerManager');
const Rect = goog.require('Blockly.utils.Rect');
/* eslint-disable-next-line no-unused-vars */
@@ -45,12 +45,11 @@ const Tooltip = goog.require('Blockly.Tooltip');
const WidgetDiv = goog.require('Blockly.WidgetDiv');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const dom = goog.require('Blockly.utils.dom');
const browserEvents = goog.require('Blockly.browserEvents');
const style = goog.require('Blockly.utils.style');
const userAgent = goog.require('Blockly.utils.userAgent');
const {addClass, createSvgElement, getFastTextWidth, removeClass, removeNode} = goog.require('Blockly.utils.dom');
/* eslint-disable-next-line no-unused-vars */
const {conditionalBind, unbind, Data} = goog.require('Blockly.browserEvents');
const {getPageOffset} = goog.require('Blockly.utils.style');
const {replaceMessageReferences} = goog.require('Blockly.utils');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
/** @suppress {extraRequire} */
@@ -150,7 +149,7 @@ const Field = function(value, opt_validator, opt_config) {
/**
* Mouse down event listener data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.mouseDownWrapper_ = null;
@@ -281,7 +280,7 @@ Field.prototype.SERIALIZABLE = false;
Field.prototype.configure_ = function(config) {
let tooltip = config['tooltip'];
if (typeof tooltip == 'string') {
tooltip = replaceMessageReferences(config['tooltip']);
tooltip = utils.replaceMessageReferences(config['tooltip']);
}
tooltip && this.setTooltip(tooltip);
@@ -331,7 +330,7 @@ Field.prototype.init = function() {
// Field has already been initialized once.
return;
}
this.fieldGroup_ = createSvgElement(Svg.G, {}, null);
this.fieldGroup_ = dom.createSvgElement(Svg.G, {}, null);
if (!this.isVisible()) {
this.fieldGroup_.style.display = 'none';
}
@@ -367,7 +366,7 @@ Field.prototype.initModel = function() {};
* @protected
*/
Field.prototype.createBorderRect_ = function() {
this.borderRect_ = createSvgElement(
this.borderRect_ = dom.createSvgElement(
Svg.RECT, {
'rx': this.getConstants().FIELD_BORDER_RECT_RADIUS,
'ry': this.getConstants().FIELD_BORDER_RECT_RADIUS,
@@ -387,7 +386,7 @@ Field.prototype.createBorderRect_ = function() {
* @protected
*/
Field.prototype.createTextElement_ = function() {
this.textElement_ = createSvgElement(
this.textElement_ = dom.createSvgElement(
Svg.TEXT, {
'class': 'blocklyText',
},
@@ -406,7 +405,7 @@ Field.prototype.createTextElement_ = function() {
*/
Field.prototype.bindEvents_ = function() {
Tooltip.bindMouseEvents(this.getClickTarget_());
this.mouseDownWrapper_ = conditionalBind(
this.mouseDownWrapper_ = browserEvents.conditionalBind(
this.getClickTarget_(), 'mousedown', this, this.onMouseDown_);
};
@@ -443,10 +442,10 @@ Field.prototype.dispose = function() {
Tooltip.unbindMouseEvents(this.getClickTarget_());
if (this.mouseDownWrapper_) {
unbind(this.mouseDownWrapper_);
browserEvents.unbind(this.mouseDownWrapper_);
}
removeNode(this.fieldGroup_);
dom.removeNode(this.fieldGroup_);
this.disposed = true;
};
@@ -460,12 +459,12 @@ Field.prototype.updateEditable = function() {
return;
}
if (this.enabled_ && this.sourceBlock_.isEditable()) {
addClass(group, 'blocklyEditableText');
removeClass(group, 'blocklyNonEditableText');
dom.addClass(group, 'blocklyEditableText');
dom.removeClass(group, 'blocklyNonEditableText');
group.style.cursor = this.CURSOR;
} else {
addClass(group, 'blocklyNonEditableText');
removeClass(group, 'blocklyEditableText');
dom.addClass(group, 'blocklyNonEditableText');
dom.removeClass(group, 'blocklyEditableText');
group.style.cursor = '';
}
};
@@ -643,7 +642,7 @@ Field.prototype.updateSize_ = function(opt_margin) {
let contentWidth = 0;
if (this.textElement_) {
contentWidth = getFastTextWidth(
contentWidth = dom.getFastTextWidth(
this.textElement_, constants.FIELD_TEXT_FONTSIZE,
constants.FIELD_TEXT_FONTWEIGHT, constants.FIELD_TEXT_FONTFAMILY);
totalWidth += contentWidth;
@@ -760,7 +759,7 @@ Field.prototype.getScaledBBox = function() {
}
} else {
const bBox = this.borderRect_.getBoundingClientRect();
xy = getPageOffset(this.borderRect_);
xy = style.getPageOffset(this.borderRect_);
scaledWidth = bBox.width;
scaledHeight = bBox.height;
}
@@ -1020,7 +1019,7 @@ Field.prototype.getClickTarget_ = function() {
* @protected
*/
Field.prototype.getAbsoluteXY_ = function() {
return getPageOffset(
return style.getPageOffset(
/** @type {!SVGRectElement} */ (this.getClickTarget_()));
};

View File

@@ -16,7 +16,7 @@ goog.module.declareLegacyNamespace();
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 object = goog.require('Blockly.utils.object');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
@@ -48,7 +48,7 @@ const FieldCheckbox = function(opt_value, opt_validator, opt_config) {
FieldCheckbox.superClass_.constructor.call(
this, opt_value, opt_validator, opt_config);
};
inherits(FieldCheckbox, Field);
object.inherits(FieldCheckbox, Field);
/**
* The default value for this field.

View File

@@ -20,12 +20,11 @@ const IdGenerator = goog.require('Blockly.utils.IdGenerator');
const KeyCodes = goog.require('Blockly.utils.KeyCodes');
const Size = goog.require('Blockly.utils.Size');
const aria = goog.require('Blockly.utils.aria');
const browserEvents = goog.require('Blockly.browserEvents');
const colour = goog.require('Blockly.utils.colour');
const dom = goog.require('Blockly.utils.dom');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const {addClass, removeClass} = goog.require('Blockly.utils.dom');
/* eslint-disable-next-line no-unused-vars */
const {conditionalBind, unbind, Data} = goog.require('Blockly.browserEvents');
const {inherits} = goog.require('Blockly.utils.object');
const object = goog.require('Blockly.utils.object');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
@@ -65,40 +64,40 @@ const FieldColour = function(opt_value, opt_validator, opt_config) {
/**
* Mouse click event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onClickWrapper_ = null;
/**
* Mouse move event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onMouseMoveWrapper_ = null;
/**
* Mouse enter event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onMouseEnterWrapper_ = null;
/**
* Mouse leave event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onMouseLeaveWrapper_ = null;
/**
* Key down event data.
* @type {?Data}
* @type {?browserEvents.Data}
* @private
*/
this.onKeyDownWrapper_ = null;
};
inherits(FieldColour, Field);
object.inherits(FieldColour, Field);
/**
* Construct a FieldColour from a JSON arg object.
@@ -472,7 +471,7 @@ FieldColour.prototype.onMouseLeave_ = function() {
this.picker_.blur();
const highlighted = this.getHighlighted_();
if (highlighted) {
removeClass(highlighted, 'blocklyColourHighlighted');
dom.removeClass(highlighted, 'blocklyColourHighlighted');
}
};
@@ -503,10 +502,10 @@ FieldColour.prototype.setHighlightedCell_ = function(cell, index) {
// Unhighlight the current item.
const highlighted = this.getHighlighted_();
if (highlighted) {
removeClass(highlighted, 'blocklyColourHighlighted');
dom.removeClass(highlighted, 'blocklyColourHighlighted');
}
// Highlight new item.
addClass(cell, 'blocklyColourHighlighted');
dom.addClass(cell, 'blocklyColourHighlighted');
// Set new highlighted index.
this.highlightedIndex_ = index;
@@ -560,15 +559,15 @@ FieldColour.prototype.dropdownCreate_ = function() {
// Configure event handler on the table to listen for any event in a cell.
this.onClickWrapper_ =
conditionalBind(table, 'click', this, this.onClick_, true);
this.onMouseMoveWrapper_ =
conditionalBind(table, 'mousemove', this, this.onMouseMove_, true);
this.onMouseEnterWrapper_ =
conditionalBind(table, 'mouseenter', this, this.onMouseEnter_, true);
this.onMouseLeaveWrapper_ =
conditionalBind(table, 'mouseleave', this, this.onMouseLeave_, true);
browserEvents.conditionalBind(table, 'click', this, this.onClick_, true);
this.onMouseMoveWrapper_ = browserEvents.conditionalBind(
table, 'mousemove', this, this.onMouseMove_, true);
this.onMouseEnterWrapper_ = browserEvents.conditionalBind(
table, 'mouseenter', this, this.onMouseEnter_, true);
this.onMouseLeaveWrapper_ = browserEvents.conditionalBind(
table, 'mouseleave', this, this.onMouseLeave_, true);
this.onKeyDownWrapper_ =
conditionalBind(table, 'keydown', this, this.onKeyDown_);
browserEvents.conditionalBind(table, 'keydown', this, this.onKeyDown_);
this.picker_ = table;
};
@@ -579,23 +578,23 @@ FieldColour.prototype.dropdownCreate_ = function() {
*/
FieldColour.prototype.dropdownDispose_ = function() {
if (this.onClickWrapper_) {
unbind(this.onClickWrapper_);
browserEvents.unbind(this.onClickWrapper_);
this.onClickWrapper_ = null;
}
if (this.onMouseMoveWrapper_) {
unbind(this.onMouseMoveWrapper_);
browserEvents.unbind(this.onMouseMoveWrapper_);
this.onMouseMoveWrapper_ = null;
}
if (this.onMouseEnterWrapper_) {
unbind(this.onMouseEnterWrapper_);
browserEvents.unbind(this.onMouseEnterWrapper_);
this.onMouseEnterWrapper_ = null;
}
if (this.onMouseLeaveWrapper_) {
unbind(this.onMouseLeaveWrapper_);
browserEvents.unbind(this.onMouseLeaveWrapper_);
this.onMouseLeaveWrapper_ = null;
}
if (this.onKeyDownWrapper_) {
unbind(this.onKeyDownWrapper_);
browserEvents.unbind(this.onKeyDownWrapper_);
this.onKeyDownWrapper_ = null;
}
this.picker_ = null;

View File

@@ -24,10 +24,10 @@ const Svg = goog.require('Blockly.utils.Svg');
const aria = goog.require('Blockly.utils.aria');
const dom = goog.require('Blockly.utils.dom');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const object = goog.require('Blockly.utils.object');
const userAgent = goog.require('Blockly.utils.userAgent');
const {commonWordPrefix, commonWordSuffix, shortestStringLength} = goog.require('Blockly.utils.string');
const {inherits} = goog.require('Blockly.utils.object');
const {replaceMessageReferences} = goog.require('Blockly.utils');
const utils = goog.require('Blockly.utils');
const utilsString = goog.require('Blockly.utils.string');
/**
@@ -130,7 +130,7 @@ const FieldDropdown = function(menuGenerator, opt_validator, opt_config) {
*/
this.svgArrow_ = null;
};
inherits(FieldDropdown, Field);
object.inherits(FieldDropdown, Field);
/**
* Dropdown image properties.
@@ -405,10 +405,10 @@ FieldDropdown.prototype.trimOptions_ = function() {
for (let i = 0; i < options.length; i++) {
const label = options[i][0];
if (typeof label == 'string') {
options[i][0] = replaceMessageReferences(label);
options[i][0] = utils.replaceMessageReferences(label);
} else {
if (label.alt != null) {
options[i][0].alt = replaceMessageReferences(label.alt);
options[i][0].alt = utils.replaceMessageReferences(label.alt);
}
hasImages = true;
}
@@ -420,9 +420,9 @@ FieldDropdown.prototype.trimOptions_ = function() {
for (let i = 0; i < options.length; i++) {
strings.push(options[i][0]);
}
const shortest = shortestStringLength(strings);
const prefixLength = commonWordPrefix(strings, shortest);
const suffixLength = commonWordSuffix(strings, shortest);
const shortest = utilsString.shortestStringLength(strings);
const prefixLength = utilsString.commonWordPrefix(strings, shortest);
const suffixLength = utilsString.commonWordSuffix(strings, shortest);
if (!prefixLength && !suffixLength) {
return;
}

View File

@@ -16,10 +16,10 @@ goog.module.declareLegacyNamespace();
const Field = goog.require('Blockly.Field');
const Size = goog.require('Blockly.utils.Size');
const Svg = goog.require('Blockly.utils.Svg');
const dom = goog.require('Blockly.utils.dom');
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');
const object = goog.require('Blockly.utils.object');
const utils = goog.require('Blockly.utils');
/**
@@ -45,9 +45,9 @@ const FieldImage = function(
if (!src) {
throw Error('Src value of an image field is required');
}
src = replaceMessageReferences(src);
const imageHeight = Number(replaceMessageReferences(height));
const imageWidth = Number(replaceMessageReferences(width));
src = utils.replaceMessageReferences(src);
const imageHeight = Number(utils.replaceMessageReferences(height));
const imageWidth = Number(utils.replaceMessageReferences(width));
if (isNaN(imageHeight) || isNaN(imageWidth)) {
throw Error(
'Height and width values of an image field must cast to' +
@@ -78,7 +78,7 @@ const FieldImage = function(
if (!opt_config) { // If the config wasn't passed, do old configuration.
this.flipRtl_ = !!opt_flipRtl;
this.altText_ = replaceMessageReferences(opt_alt) || '';
this.altText_ = utils.replaceMessageReferences(opt_alt) || '';
}
// Initialize other properties.
@@ -115,7 +115,7 @@ const FieldImage = function(
*/
this.imageElement_ = null;
};
inherits(FieldImage, Field);
object.inherits(FieldImage, Field);
/**
* The default value for this field.
@@ -174,7 +174,7 @@ FieldImage.prototype.isDirty_ = false;
FieldImage.prototype.configure_ = function(config) {
FieldImage.superClass_.configure_.call(this, config);
this.flipRtl_ = !!config['flipRtl'];
this.altText_ = replaceMessageReferences(config['alt']) || '';
this.altText_ = utils.replaceMessageReferences(config['alt']) || '';
};
/**
@@ -182,7 +182,7 @@ FieldImage.prototype.configure_ = function(config) {
* @package
*/
FieldImage.prototype.initView = function() {
this.imageElement_ = createSvgElement(
this.imageElement_ = dom.createSvgElement(
Svg.IMAGE, {
'height': this.imageHeight_ + 'px',
'width': this.size_.width + 'px',
@@ -190,7 +190,7 @@ FieldImage.prototype.initView = function() {
},
this.fieldGroup_);
this.imageElement_.setAttributeNS(
XLINK_NS, 'xlink:href', /** @type {string} */ (this.value_));
dom.XLINK_NS, 'xlink:href', /** @type {string} */ (this.value_));
if (this.clickHandler_) {
this.imageElement_.style.cursor = 'pointer';
@@ -227,7 +227,7 @@ FieldImage.prototype.doValueUpdate_ = function(newValue) {
this.value_ = newValue;
if (this.imageElement_) {
this.imageElement_.setAttributeNS(
XLINK_NS, 'xlink:href', String(this.value_));
dom.XLINK_NS, 'xlink:href', String(this.value_));
}
};

View File

@@ -17,8 +17,8 @@ goog.module.declareLegacyNamespace();
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');
const object = goog.require('Blockly.utils.object');
const utils = goog.require('Blockly.utils');
/**
@@ -47,7 +47,7 @@ const FieldLabel = function(opt_value, opt_class, opt_config) {
this.class_ = opt_class || null;
}
};
inherits(FieldLabel, Field);
object.inherits(FieldLabel, Field);
/**
* The default value for this field.
@@ -65,7 +65,7 @@ FieldLabel.prototype.DEFAULT_VALUE = '';
* @nocollapse
*/
FieldLabel.fromJson = function(options) {
const text = replaceMessageReferences(options['text']);
const text = utils.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);

View File

@@ -16,8 +16,8 @@ goog.module.declareLegacyNamespace();
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');
const object = goog.require('Blockly.utils.object');
const utils = goog.require('Blockly.utils');
/**
@@ -37,7 +37,7 @@ const FieldLabelSerializable = function(opt_value, opt_class, opt_config) {
FieldLabelSerializable.superClass_.constructor.call(
this, opt_value, opt_class, opt_config);
};
inherits(FieldLabelSerializable, FieldLabel);
object.inherits(FieldLabelSerializable, FieldLabel);
/**
* Construct a FieldLabelSerializable from a JSON arg object,
@@ -48,7 +48,7 @@ inherits(FieldLabelSerializable, FieldLabel);
* @nocollapse
*/
FieldLabelSerializable.fromJson = function(options) {
const text = replaceMessageReferences(options['text']);
const text = utils.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);

View File

@@ -24,9 +24,9 @@ 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 object = goog.require('Blockly.utils.object');
const userAgent = goog.require('Blockly.utils.userAgent');
const {inherits} = goog.require('Blockly.utils.object');
const {replaceMessageReferences} = goog.require('Blockly.utils');
const utils = goog.require('Blockly.utils');
/**
@@ -70,7 +70,7 @@ const FieldMultilineInput = function(opt_value, opt_validator, opt_config) {
*/
this.isOverflowedY_ = false;
};
inherits(FieldMultilineInput, FieldTextInput);
object.inherits(FieldMultilineInput, FieldTextInput);
/**
* @override
@@ -89,7 +89,7 @@ FieldMultilineInput.prototype.configure_ = function(config) {
* @nocollapse
*/
FieldMultilineInput.fromJson = function(options) {
const text = replaceMessageReferences(options['text']);
const text = utils.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);

View File

@@ -15,8 +15,8 @@ goog.module.declareLegacyNamespace();
const FieldTextInput = goog.require('Blockly.FieldTextInput');
const aria = goog.require('Blockly.utils.aria');
const {inherits} = goog.require('Blockly.utils.object');
const {register} = goog.require('Blockly.fieldRegistry');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const object = goog.require('Blockly.utils.object');
/**
@@ -74,7 +74,7 @@ const FieldNumber = function(
this.setConstraints(opt_min, opt_max, opt_precision);
}
};
inherits(FieldNumber, FieldTextInput);
object.inherits(FieldNumber, FieldTextInput);
/**
* The default value for this field.
@@ -313,6 +313,6 @@ FieldNumber.prototype.widgetCreate_ = function() {
return htmlInput;
};
register('field_number', FieldNumber);
fieldRegistry.register('field_number', FieldNumber);
exports = FieldNumber;

View File

@@ -13,6 +13,7 @@
goog.module('Blockly.FieldTextInput');
goog.module.declareLegacyNamespace();
const Blockly = goog.require('Blockly');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Coordinate = goog.require('Blockly.utils.Coordinate');
@@ -28,10 +29,9 @@ 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 object = goog.require('Blockly.utils.object');
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');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
@@ -95,7 +95,7 @@ const FieldTextInput = function(opt_value, opt_validator, opt_config) {
*/
this.workspace_ = null;
};
inherits(FieldTextInput, Field);
object.inherits(FieldTextInput, Field);
/**
* The default value for this field.
@@ -113,7 +113,7 @@ FieldTextInput.prototype.DEFAULT_VALUE = '';
* @nocollapse
*/
FieldTextInput.fromJson = function(options) {
const text = replaceMessageReferences(options['text']);
const text = utils.replaceMessageReferences(options['text']);
// `this` might be a subclass of FieldTextInput if that class doesn't override
// the static fromJson method.
return new this(text, undefined, options);
@@ -312,7 +312,7 @@ FieldTextInput.prototype.showEditor_ = function(_opt_e, opt_quietInput) {
* @private
*/
FieldTextInput.prototype.showPromptEditor_ = function() {
blocklyPrompt(Msg['CHANGE_VALUE_TITLE'], this.getText(), function(text) {
Blockly.prompt(Msg['CHANGE_VALUE_TITLE'], this.getText(), function(text) {
this.setValue(this.getValueFromEditorText_(text));
}.bind(this));
};

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');
@@ -24,8 +27,8 @@ const Variables = goog.require('Blockly.Variables');
const Xml = goog.require('Blockly.Xml');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const internalConstants = goog.require('Blockly.internalConstants');
const {inherits} = goog.require('Blockly.utils.object');
const {replaceMessageReferences} = goog.require('Blockly.utils');
const object = goog.require('Blockly.utils.object');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
@@ -86,7 +89,7 @@ const FieldVariable = function(
this.setTypes_(opt_variableTypes, opt_defaultType);
}
};
inherits(FieldVariable, FieldDropdown);
object.inherits(FieldVariable, FieldDropdown);
/**
* Construct a FieldVariable from a JSON arg object,
@@ -98,7 +101,7 @@ inherits(FieldVariable, FieldDropdown);
* @nocollapse
*/
FieldVariable.fromJson = function(options) {
const varName = replaceMessageReferences(options['variable']);
const varName = utils.replaceMessageReferences(options['variable']);
// `this` might be a subclass of FieldVariable if that class doesn't override
// the static fromJson method.
return new this(varName, undefined, undefined, undefined, options);

View File

@@ -14,17 +14,19 @@ goog.module('Blockly.Flyout');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.require('Blockly.Block');
const Block = goog.requireType('Blockly.Block');
const Blockly = goog.require('Blockly');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const ComponentManager = goog.require('Blockly.ComponentManager');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const DeleteArea = goog.require('Blockly.DeleteArea');
const Events = goog.require('Blockly.Events');
const FlyoutButton = goog.require('Blockly.FlyoutButton');
/* eslint-disable-next-line no-unused-vars */
const FlyoutButton = goog.requireType('Blockly.FlyoutButton');
const FlyoutMetricsManager = goog.require('Blockly.FlyoutMetricsManager');
/* eslint-disable-next-line no-unused-vars */
const IFlyout = goog.require('Blockly.IFlyout');
const IFlyout = goog.requireType('Blockly.IFlyout');
/* eslint-disable-next-line no-unused-vars */
const Options = goog.requireType('Blockly.Options');
const ScrollbarPair = goog.require('Blockly.ScrollbarPair');
@@ -38,7 +40,6 @@ const dom = goog.require('Blockly.utils.dom');
const toolbox = goog.require('Blockly.utils.toolbox');
const utils = goog.require('Blockly.utils');
const utilsXml = goog.require('Blockly.utils.xml');
const {hideChaff} = goog.require('Blockly');
/** @suppress {extraRequire} */
goog.require('Blockly.blockRendering');
/** @suppress {extraRequire} */
@@ -659,6 +660,7 @@ Flyout.prototype.getDynamicCategoryContents_ = function(categoryName) {
* @private
*/
Flyout.prototype.createButton_ = function(btnInfo, isLabel) {
const FlyoutButton = goog.module.get('Blockly.FlyoutButton');
if (!FlyoutButton) {
throw Error('Missing require for Blockly.FlyoutButton');
}
@@ -854,7 +856,7 @@ Flyout.prototype.createBlock = function(originalBlock) {
}
// Close the flyout.
hideChaff();
Blockly.hideChaff();
const newVariables = Variables.getAddedVariables(
this.targetWorkspace, variablesBeforeCreation);

View File

@@ -10,42 +10,44 @@
*/
'use strict';
goog.provide('Blockly.FlyoutButton');
goog.module('Blockly.FlyoutButton');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.Css');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.style');
goog.require('Blockly.utils.Svg');
goog.requireType('Blockly.utils.toolbox');
goog.requireType('Blockly.WorkspaceSvg');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Css = goog.require('Blockly.Css');
const Svg = goog.require('Blockly.utils.Svg');
/* 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 style = goog.require('Blockly.utils.style');
/* eslint-disable-next-line no-unused-vars */
const toolbox = goog.requireType('Blockly.utils.toolbox');
const utils = goog.require('Blockly.utils');
/**
* Class for a button in the flyout.
* @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this
* @param {!WorkspaceSvg} workspace The workspace in which to place this
* button.
* @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace.
* @param {!Blockly.utils.toolbox.ButtonOrLabelInfo} json
* @param {!WorkspaceSvg} targetWorkspace The flyout's target workspace.
* @param {!toolbox.ButtonOrLabelInfo} json
* The JSON specifying the label/button.
* @param {boolean} isLabel Whether this button should be styled as a label.
* @constructor
* @package
*/
Blockly.FlyoutButton = function(workspace, targetWorkspace, json, isLabel) {
const FlyoutButton = function(workspace, targetWorkspace, json, isLabel) {
// Labels behave the same as buttons, but are styled differently.
/**
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @private
*/
this.workspace_ = workspace;
/**
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @private
*/
this.targetWorkspace_ = targetWorkspace;
@@ -57,10 +59,10 @@ Blockly.FlyoutButton = function(workspace, targetWorkspace, json, isLabel) {
this.text_ = json['text'];
/**
* @type {!Blockly.utils.Coordinate}
* @type {!Coordinate}
* @private
*/
this.position_ = new Blockly.utils.Coordinate(0, 0);
this.position_ = new Coordinate(0, 0);
/**
* Whether this button should be styled as a label.
@@ -75,8 +77,8 @@ Blockly.FlyoutButton = function(workspace, targetWorkspace, json, isLabel) {
* @private
*/
this.callbackKey_ = json['callbackKey'] ||
/* Check the lower case version too to satisfy IE */
json['callbackkey'];
/* Check the lower case version too to satisfy IE */
json['callbackkey'];
/**
* If specified, a CSS class to add to this button.
@@ -87,14 +89,14 @@ Blockly.FlyoutButton = function(workspace, targetWorkspace, json, isLabel) {
/**
* Mouse up event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.onMouseUpWrapper_ = null;
/**
* The JSON specifying the label / button.
* @type {!Blockly.utils.toolbox.ButtonOrLabelInfo}
* @type {!toolbox.ButtonOrLabelInfo}
*/
this.info = json;
};
@@ -102,69 +104,70 @@ Blockly.FlyoutButton = function(workspace, targetWorkspace, json, isLabel) {
/**
* The horizontal margin around the text in the button.
*/
Blockly.FlyoutButton.MARGIN_X = 5;
FlyoutButton.MARGIN_X = 5;
/**
* The vertical margin around the text in the button.
*/
Blockly.FlyoutButton.MARGIN_Y = 2;
FlyoutButton.MARGIN_Y = 2;
/**
* The width of the button's rect.
* @type {number}
*/
Blockly.FlyoutButton.prototype.width = 0;
FlyoutButton.prototype.width = 0;
/**
* The height of the button's rect.
* @type {number}
*/
Blockly.FlyoutButton.prototype.height = 0;
FlyoutButton.prototype.height = 0;
/**
* Create the button elements.
* @return {!SVGElement} The button's SVG group.
*/
Blockly.FlyoutButton.prototype.createDom = function() {
var cssClass = this.isLabel_ ? 'blocklyFlyoutLabel' : 'blocklyFlyoutButton';
FlyoutButton.prototype.createDom = function() {
let cssClass = this.isLabel_ ? 'blocklyFlyoutLabel' : 'blocklyFlyoutButton';
if (this.cssClass_) {
cssClass += ' ' + this.cssClass_;
}
this.svgGroup_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.G, {'class': cssClass},
this.workspace_.getCanvas());
this.svgGroup_ = dom.createSvgElement(
Svg.G, {'class': cssClass}, this.workspace_.getCanvas());
let shadow;
if (!this.isLabel_) {
// Shadow rectangle (light source does not mirror in RTL).
var shadow = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
shadow = dom.createSvgElement(
Svg.RECT, {
'class': 'blocklyFlyoutButtonShadow',
'rx': 4, 'ry': 4, 'x': 1, 'y': 1
'rx': 4,
'ry': 4,
'x': 1,
'y': 1
},
this.svgGroup_);
}
// Background rectangle.
var rect = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
'class': this.isLabel_ ?
'blocklyFlyoutLabelBackground' : 'blocklyFlyoutButtonBackground',
'rx': 4, 'ry': 4
const rect = dom.createSvgElement(
Svg.RECT, {
'class': this.isLabel_ ? 'blocklyFlyoutLabelBackground' :
'blocklyFlyoutButtonBackground',
'rx': 4,
'ry': 4
},
this.svgGroup_);
var svgText = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.TEXT,
{
const svgText = dom.createSvgElement(
Svg.TEXT, {
'class': this.isLabel_ ? 'blocklyFlyoutLabelText' : 'blocklyText',
'x': 0,
'y': 0,
'text-anchor': 'middle'
},
this.svgGroup_);
var text = Blockly.utils.replaceMessageReferences(this.text_);
let text = utils.replaceMessageReferences(this.text_);
if (this.workspace_.RTL) {
// Force text to be RTL by adding an RLM.
text += '\u200F';
@@ -172,22 +175,22 @@ Blockly.FlyoutButton.prototype.createDom = function() {
svgText.textContent = text;
if (this.isLabel_) {
this.svgText_ = svgText;
this.workspace_.getThemeManager().subscribe(this.svgText_,
'flyoutForegroundColour', 'fill');
this.workspace_.getThemeManager().subscribe(
this.svgText_, 'flyoutForegroundColour', 'fill');
}
var fontSize = Blockly.utils.style.getComputedStyle(svgText, 'fontSize');
var fontWeight = Blockly.utils.style.getComputedStyle(svgText, 'fontWeight');
var fontFamily = Blockly.utils.style.getComputedStyle(svgText, 'fontFamily');
this.width = Blockly.utils.dom.getFastTextWidthWithSizeString(svgText,
fontSize, fontWeight, fontFamily);
var fontMetrics = Blockly.utils.dom.measureFontMetrics(text, fontSize,
fontWeight, fontFamily);
const fontSize = style.getComputedStyle(svgText, 'fontSize');
const fontWeight = style.getComputedStyle(svgText, 'fontWeight');
const fontFamily = style.getComputedStyle(svgText, 'fontFamily');
this.width = dom.getFastTextWidthWithSizeString(
svgText, fontSize, fontWeight, fontFamily);
const fontMetrics =
dom.measureFontMetrics(text, fontSize, fontWeight, fontFamily);
this.height = fontMetrics.height;
if (!this.isLabel_) {
this.width += 2 * Blockly.FlyoutButton.MARGIN_X;
this.height += 2 * Blockly.FlyoutButton.MARGIN_Y;
this.width += 2 * FlyoutButton.MARGIN_X;
this.height += 2 * FlyoutButton.MARGIN_Y;
shadow.setAttribute('width', this.width);
shadow.setAttribute('height', this.height);
}
@@ -195,12 +198,12 @@ Blockly.FlyoutButton.prototype.createDom = function() {
rect.setAttribute('height', this.height);
svgText.setAttribute('x', this.width / 2);
svgText.setAttribute('y', this.height / 2 - fontMetrics.height / 2 +
fontMetrics.baseline);
svgText.setAttribute(
'y', this.height / 2 - fontMetrics.height / 2 + fontMetrics.baseline);
this.updateTransform_();
this.onMouseUpWrapper_ = Blockly.browserEvents.conditionalBind(
this.onMouseUpWrapper_ = browserEvents.conditionalBind(
this.svgGroup_, 'mouseup', this, this.onMouseUp_);
return this.svgGroup_;
};
@@ -208,7 +211,7 @@ Blockly.FlyoutButton.prototype.createDom = function() {
/**
* Correctly position the flyout button and make it visible.
*/
Blockly.FlyoutButton.prototype.show = function() {
FlyoutButton.prototype.show = function() {
this.updateTransform_();
this.svgGroup_.setAttribute('display', 'block');
};
@@ -217,8 +220,9 @@ Blockly.FlyoutButton.prototype.show = function() {
* Update SVG attributes to match internal state.
* @private
*/
Blockly.FlyoutButton.prototype.updateTransform_ = function() {
this.svgGroup_.setAttribute('transform',
FlyoutButton.prototype.updateTransform_ = function() {
this.svgGroup_.setAttribute(
'transform',
'translate(' + this.position_.x + ',' + this.position_.y + ')');
};
@@ -227,7 +231,7 @@ Blockly.FlyoutButton.prototype.updateTransform_ = function() {
* @param {number} x The new x coordinate.
* @param {number} y The new y coordinate.
*/
Blockly.FlyoutButton.prototype.moveTo = function(x, y) {
FlyoutButton.prototype.moveTo = function(x, y) {
this.position_.x = x;
this.position_.y = y;
this.updateTransform_();
@@ -236,44 +240,44 @@ Blockly.FlyoutButton.prototype.moveTo = function(x, y) {
/**
* @return {boolean} Whether or not the button is a label.
*/
Blockly.FlyoutButton.prototype.isLabel = function() {
FlyoutButton.prototype.isLabel = function() {
return this.isLabel_;
};
/**
* Location of the button.
* @return {!Blockly.utils.Coordinate} x, y coordinates.
* @return {!Coordinate} x, y coordinates.
* @package
*/
Blockly.FlyoutButton.prototype.getPosition = function() {
FlyoutButton.prototype.getPosition = function() {
return this.position_;
};
/**
* @return {string} Text of the button.
*/
Blockly.FlyoutButton.prototype.getButtonText = function() {
FlyoutButton.prototype.getButtonText = function() {
return this.text_;
};
/**
* Get the button's target workspace.
* @return {!Blockly.WorkspaceSvg} The target workspace of the flyout where this
* @return {!WorkspaceSvg} The target workspace of the flyout where this
* button resides.
*/
Blockly.FlyoutButton.prototype.getTargetWorkspace = function() {
FlyoutButton.prototype.getTargetWorkspace = function() {
return this.targetWorkspace_;
};
/**
* Dispose of this button.
*/
Blockly.FlyoutButton.prototype.dispose = function() {
FlyoutButton.prototype.dispose = function() {
if (this.onMouseUpWrapper_) {
Blockly.browserEvents.unbind(this.onMouseUpWrapper_);
browserEvents.unbind(this.onMouseUpWrapper_);
}
if (this.svgGroup_) {
Blockly.utils.dom.removeNode(this.svgGroup_);
dom.removeNode(this.svgGroup_);
}
if (this.svgText_) {
this.workspace_.getThemeManager().unsubscribe(this.svgText_);
@@ -285,16 +289,18 @@ Blockly.FlyoutButton.prototype.dispose = function() {
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.FlyoutButton.prototype.onMouseUp_ = function(e) {
var gesture = this.targetWorkspace_.getGesture(e);
FlyoutButton.prototype.onMouseUp_ = function(e) {
const gesture = this.targetWorkspace_.getGesture(e);
if (gesture) {
gesture.cancel();
}
if (this.isLabel_ && this.callbackKey_) {
console.warn('Labels should not have callbacks. Label text: ' + this.text_);
} else if (!this.isLabel_ && !(this.callbackKey_ &&
this.targetWorkspace_.getButtonCallback(this.callbackKey_))) {
} else if (
!this.isLabel_ &&
!(this.callbackKey_ &&
this.targetWorkspace_.getButtonCallback(this.callbackKey_))) {
console.warn('Buttons should have callbacks. Button text: ' + this.text_);
} else if (!this.isLabel_) {
this.targetWorkspace_.getButtonCallback(this.callbackKey_)(this);
@@ -304,7 +310,7 @@ Blockly.FlyoutButton.prototype.onMouseUp_ = function(e) {
/**
* CSS for buttons and labels. See css.js for use.
*/
Blockly.Css.register([
Css.register([
/* eslint-disable indent */
'.blocklyFlyoutButton {',
'fill: #888;',
@@ -328,3 +334,5 @@ Blockly.Css.register([
'}',
/* eslint-enable indent */
]);
exports = FlyoutButton;

View File

@@ -22,10 +22,10 @@ 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 object = goog.require('Blockly.utils.object');
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');
const toolbox = goog.require('Blockly.utils.toolbox');
const utils = goog.require('Blockly.utils');
/**
@@ -39,7 +39,7 @@ const HorizontalFlyout = function(workspaceOptions) {
HorizontalFlyout.superClass_.constructor.call(this, workspaceOptions);
this.horizontalLayout = true;
};
inherits(HorizontalFlyout, Flyout);
object.inherits(HorizontalFlyout, Flyout);
/**
* Sets the translation of the flyout to match the scrollbars.
@@ -92,7 +92,7 @@ HorizontalFlyout.prototype.getY = function() {
const toolboxMetrics = metricsManager.getToolboxMetrics();
let y = 0;
const atTop = this.toolboxPosition_ == Position.TOP;
const atTop = this.toolboxPosition_ == toolbox.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.
@@ -159,7 +159,7 @@ HorizontalFlyout.prototype.position = function() {
* @private
*/
HorizontalFlyout.prototype.setBackgroundPath_ = function(width, height) {
const atTop = this.toolboxPosition_ == Position.TOP;
const atTop = this.toolboxPosition_ == toolbox.Position.TOP;
// Start at top left.
const path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)];
@@ -210,7 +210,7 @@ HorizontalFlyout.prototype.scrollToStart = function() {
* @protected
*/
HorizontalFlyout.prototype.wheel_ = function(e) {
const scrollDelta = getScrollDeltaPixels(e);
const scrollDelta = utils.getScrollDeltaPixels(e);
const delta = scrollDelta.x || scrollDelta.y;
if (delta) {
@@ -326,7 +326,7 @@ HorizontalFlyout.prototype.getClientRect = function() {
const BIG_NUM = 1000000000;
const top = flyoutRect.top;
if (this.toolboxPosition_ == Position.TOP) {
if (this.toolboxPosition_ == toolbox.Position.TOP) {
const height = flyoutRect.height;
return new Rect(-BIG_NUM, top + height, -BIG_NUM, BIG_NUM);
} else { // Bottom.
@@ -335,7 +335,7 @@ HorizontalFlyout.prototype.getClientRect = function() {
};
/**
* Compute height of flyout. Position mat under each block.
* Compute height of flyout. toolbox.Position mat under each block.
* For RTL: Lay out the blocks right-aligned.
* @protected
*/
@@ -362,7 +362,7 @@ HorizontalFlyout.prototype.reflowInternal_ = function() {
}
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_ &&
this.toolboxPosition_ == Position.TOP &&
this.toolboxPosition_ == toolbox.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

View File

@@ -22,10 +22,10 @@ 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 object = goog.require('Blockly.utils.object');
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');
const toolbox = goog.require('Blockly.utils.toolbox');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.Block');
/** @suppress {extraRequire} */
@@ -42,7 +42,7 @@ goog.require('Blockly.constants');
const VerticalFlyout = function(workspaceOptions) {
VerticalFlyout.superClass_.constructor.call(this, workspaceOptions);
};
inherits(VerticalFlyout, Flyout);
object.inherits(VerticalFlyout, Flyout);
/**
* The name of the vertical flyout in the registry.
@@ -94,14 +94,14 @@ VerticalFlyout.prototype.getX = function() {
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_) {
// If there is a category toolbox.
if (this.targetWorkspace.getToolbox()) {
if (this.toolboxPosition_ == Position.LEFT) {
if (this.toolboxPosition_ == toolbox.Position.LEFT) {
x = toolboxMetrics.width;
} else {
x = viewMetrics.width - this.width_;
}
// Simple (flyout-only) toolbox.
} else {
if (this.toolboxPosition_ == Position.LEFT) {
if (this.toolboxPosition_ == toolbox.Position.LEFT) {
x = 0;
} else {
// The simple flyout does not cover the workspace.
@@ -110,7 +110,7 @@ VerticalFlyout.prototype.getX = function() {
}
// Trashcan flyout is opposite the main flyout.
} else {
if (this.toolboxPosition_ == Position.LEFT) {
if (this.toolboxPosition_ == toolbox.Position.LEFT) {
x = 0;
} else {
// Because the anchor point of the flyout is on the left, but we want
@@ -165,7 +165,7 @@ VerticalFlyout.prototype.position = function() {
* @private
*/
VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) {
const atRight = this.toolboxPosition_ == Position.RIGHT;
const atRight = this.toolboxPosition_ == toolbox.Position.RIGHT;
const totalWidth = width + this.CORNER_RADIUS;
// Decide whether to start on the left or right.
@@ -201,7 +201,7 @@ VerticalFlyout.prototype.scrollToStart = function() {
* @protected
*/
VerticalFlyout.prototype.wheel_ = function(e) {
const scrollDelta = getScrollDeltaPixels(e);
const scrollDelta = utils.getScrollDeltaPixels(e);
if (scrollDelta.y) {
const metricsManager = this.workspace_.getMetricsManager();
@@ -306,7 +306,7 @@ VerticalFlyout.prototype.getClientRect = function() {
const BIG_NUM = 1000000000;
const left = flyoutRect.left;
if (this.toolboxPosition_ == Position.LEFT) {
if (this.toolboxPosition_ == toolbox.Position.LEFT) {
const width = flyoutRect.width;
return new Rect(-BIG_NUM, BIG_NUM, -BIG_NUM, left + width);
} else { // Right
@@ -315,7 +315,7 @@ VerticalFlyout.prototype.getClientRect = function() {
};
/**
* Compute width of flyout. Position mat under each block.
* Compute width of flyout. toolbox.Position mat under each block.
* For RTL: Lay out the blocks and buttons to be right-aligned.
* @protected
*/
@@ -363,7 +363,7 @@ VerticalFlyout.prototype.reflowInternal_ = function() {
}
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_ &&
this.toolboxPosition_ == Position.LEFT &&
this.toolboxPosition_ == toolbox.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

View File

@@ -20,9 +20,9 @@ const Block = goog.requireType('Blockly.Block');
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 common = goog.require('Blockly.common');
const deprecation = goog.require('Blockly.utils.deprecation');
const {getMainWorkspace} = goog.require('Blockly');
const internalConstants = goog.require('Blockly.internalConstants');
/**
@@ -98,7 +98,7 @@ 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 = getMainWorkspace();
workspace = common.getMainWorkspace();
}
let code = [];
this.init(workspace);

View File

@@ -20,8 +20,8 @@ goog.module.declareLegacyNamespace();
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');
@@ -31,8 +31,8 @@ const IBubble = goog.requireType('Blockly.IBubble');
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');
@@ -113,7 +113,7 @@ const 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().
* Blockly.common.getMainWorkspace().
* @type {WorkspaceSvg}
* @protected
*/

View File

@@ -22,7 +22,7 @@ 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');
const utils = goog.require('Blockly.utils');
/**
@@ -135,7 +135,7 @@ Icon.prototype.iconClick_ = function(e) {
// Drag operation is concluding. Don't open the editor.
return;
}
if (!this.block_.isInFlyout && !isRightButton(e)) {
if (!this.block_.isInFlyout && !utils.isRightButton(e)) {
this.setVisible(!this.isVisible());
}
};
@@ -167,7 +167,7 @@ Icon.prototype.setIconLocation = function(xy) {
Icon.prototype.computeIconLocation = function() {
// Find coordinates for the centre of the icon and update the arrow.
const blockXY = this.block_.getRelativeToSurfaceXY();
const iconXY = getRelativeXY(
const iconXY = utils.getRelativeXY(
/** @type {!SVGElement} */ (this.iconGroup_));
const newXY = new Coordinate(
blockXY.x + iconXY.x + this.SIZE / 2,

View File

@@ -14,6 +14,7 @@ goog.provide('Blockly.inject');
goog.require('Blockly.BlockDragSurfaceSvg');
goog.require('Blockly.browserEvents');
goog.require('Blockly.common');
goog.require('Blockly.Css');
goog.require('Blockly.DropDownDiv');
goog.require('Blockly.Events');
@@ -33,7 +34,9 @@ goog.require('Blockly.WorkspaceDragSurfaceSvg');
goog.require('Blockly.WorkspaceSvg');
goog.require('Blockly.WidgetDiv');
goog.requireType('Blockly.BlocklyOptions');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.WorkspaceCommentSvg');
/**
@@ -44,8 +47,6 @@ goog.requireType('Blockly.BlockSvg');
* @return {!Blockly.WorkspaceSvg} Newly created main workspace.
*/
Blockly.inject = function(container, opt_options) {
Blockly.checkBlockColourConstants();
if (typeof container == 'string') {
container = document.getElementById(container) ||
document.querySelector(container);
@@ -77,12 +78,12 @@ Blockly.inject = function(container, opt_options) {
Blockly.init_(workspace);
// Keep focus on the first workspace so entering keyboard navigation looks correct.
Blockly.mainWorkspace = workspace;
Blockly.common.setMainWorkspace(workspace);
Blockly.svgResize(workspace);
subContainer.addEventListener('focusin', function() {
Blockly.mainWorkspace = workspace;
Blockly.common.setMainWorkspace(workspace);
});
return workspace;
@@ -215,7 +216,9 @@ Blockly.extractObjectFromEvent_ = function(workspace, e) {
break;
case Blockly.Events.COMMENT_CREATE:
case Blockly.Events.COMMENT_MOVE:
object = workspace.getCommentById(e.commentId);
object = (
/** @type {?Blockly.WorkspaceCommentSvg} */
(workspace.getCommentById(e.commentId)));
break;
}
return object;
@@ -439,7 +442,7 @@ Blockly.inject.bindDocumentEvents_ = function() {
window, 'orientationchange', document, function() {
// TODO (#397): Fix for multiple Blockly workspaces.
Blockly.svgResize(/** @type {!Blockly.WorkspaceSvg} */
(Blockly.getMainWorkspace()));
(Blockly.common.getMainWorkspace()));
});
}
}

View File

@@ -18,7 +18,7 @@ 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');
const Connection = goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
/* eslint-disable-next-line no-unused-vars */

View File

@@ -11,19 +11,22 @@
'use strict';
goog.provide('Blockly.inputTypes');
goog.module('Blockly.inputTypes');
goog.module.declareLegacyNamespace();
goog.require('Blockly.connectionTypes');
const connectionTypes = goog.require('Blockly.connectionTypes');
/**
* Enum for the type of a connection or input.
* @enum {number}
*/
Blockly.inputTypes = {
const inputTypes = {
// A right-facing value input. E.g. 'set item to' or 'return'.
VALUE: Blockly.connectionTypes.INPUT_VALUE,
VALUE: connectionTypes.INPUT_VALUE,
// A down-facing block stack. E.g. 'if-do' or 'else'.
STATEMENT: Blockly.connectionTypes.NEXT_STATEMENT,
STATEMENT: connectionTypes.NEXT_STATEMENT,
// A dummy input. Used to add field(s) with no input.
DUMMY: 5
};
exports = inputTypes;

View File

@@ -10,34 +10,45 @@
*/
'use strict';
goog.provide('Blockly.InsertionMarkerManager');
goog.module('Blockly.InsertionMarkerManager');
goog.module.declareLegacyNamespace();
goog.require('Blockly.blockAnimations');
goog.require('Blockly.ComponentManager');
goog.require('Blockly.connectionTypes');
goog.require('Blockly.Events');
goog.require('Blockly.internalConstants');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.RenderedConnection');
goog.requireType('Blockly.utils.Coordinate');
goog.requireType('Blockly.WorkspaceSvg');
// 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 ComponentManager = goog.require('Blockly.ComponentManager');
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const IDeleteArea = goog.requireType('Blockly.IDeleteArea');
/* eslint-disable-next-line no-unused-vars */
const IDragTarget = goog.requireType('Blockly.IDragTarget');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const blockAnimations = goog.require('Blockly.blockAnimations');
const connectionTypes = goog.require('Blockly.connectionTypes');
const constants = goog.require('Blockly.constants');
const internalConstants = goog.require('Blockly.internalConstants');
/**
* Class that controls updates to connections during drags. It is primarily
* responsible for finding the closest eligible connection and highlighting or
* unhiglighting it as needed during a drag.
* @param {!Blockly.BlockSvg} block The top block in the stack being dragged.
* @param {!BlockSvg} block The top block in the stack being dragged.
* @constructor
*/
Blockly.InsertionMarkerManager = function(block) {
const InsertionMarkerManager = function(block) {
Blockly.selected = block;
/**
* The top block in the stack being dragged.
* Does not change during a drag.
* @type {!Blockly.BlockSvg}
* @type {!BlockSvg}
* @private
*/
this.topBlock_ = block;
@@ -45,7 +56,7 @@ Blockly.InsertionMarkerManager = function(block) {
/**
* The workspace on which these connections are being dragged.
* Does not change during a drag.
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @private
*/
this.workspace_ = block.workspace;
@@ -54,7 +65,7 @@ Blockly.InsertionMarkerManager = function(block) {
* The last connection on the stack, if it's not the last connection on the
* first block.
* Set in initAvailableConnections, if at all.
* @type {Blockly.RenderedConnection}
* @type {RenderedConnection}
* @private
*/
this.lastOnStack_ = null;
@@ -63,7 +74,7 @@ Blockly.InsertionMarkerManager = function(block) {
* The insertion marker corresponding to the last block in the stack, if
* that's not the same as the first block in the stack.
* Set in initAvailableConnections, if at all
* @type {Blockly.BlockSvg}
* @type {BlockSvg}
* @private
*/
this.lastMarker_ = null;
@@ -71,7 +82,7 @@ Blockly.InsertionMarkerManager = function(block) {
/**
* The insertion marker that shows up between blocks to show where a block
* would go if dropped immediately.
* @type {Blockly.BlockSvg}
* @type {BlockSvg}
* @private
*/
this.firstMarker_ = this.createMarkerBlock_(this.topBlock_);
@@ -80,7 +91,7 @@ Blockly.InsertionMarkerManager = function(block) {
* The connection that this block would connect to if released immediately.
* Updated on every mouse move.
* This is not on any of the blocks that are being dragged.
* @type {Blockly.RenderedConnection}
* @type {RenderedConnection}
* @private
*/
this.closestConnection_ = null;
@@ -91,7 +102,7 @@ Blockly.InsertionMarkerManager = function(block) {
* Updated on every mouse move.
* This is on the top block that is being dragged or the last block in the
* dragging stack.
* @type {Blockly.RenderedConnection}
* @type {RenderedConnection}
* @private
*/
this.localConnection_ = null;
@@ -107,21 +118,21 @@ Blockly.InsertionMarkerManager = function(block) {
/**
* Connection on the insertion marker block that corresponds to
* this.localConnection_ on the currently dragged block.
* @type {Blockly.RenderedConnection}
* @type {RenderedConnection}
* @private
*/
this.markerConnection_ = null;
/**
* The block that currently has an input being highlighted, or null.
* @type {Blockly.BlockSvg}
* @type {BlockSvg}
* @private
*/
this.highlightedBlock_ = null;
/**
* The block being faded to indicate replacement, or null.
* @type {Blockly.BlockSvg}
* @type {BlockSvg}
* @private
*/
this.fadedBlock_ = null;
@@ -131,7 +142,7 @@ Blockly.InsertionMarkerManager = function(block) {
* other blocks. This includes all open connections on the top block, as well
* as the last connection on the block stack.
* Does not change during a drag.
* @type {!Array<!Blockly.RenderedConnection>}
* @type {!Array<!RenderedConnection>}
* @private
*/
this.availableConnections_ = this.initAvailableConnections_();
@@ -142,7 +153,7 @@ Blockly.InsertionMarkerManager = function(block) {
* could display.
* @enum {number}
*/
Blockly.InsertionMarkerManager.PREVIEW_TYPE = {
InsertionMarkerManager.PREVIEW_TYPE = {
INSERTION_MARKER: 0,
INPUT_OUTLINE: 1,
REPLACEMENT_FADE: 2,
@@ -154,7 +165,7 @@ Blockly.InsertionMarkerManager.PREVIEW_TYPE = {
* @type {string}
* @const
*/
Blockly.InsertionMarkerManager.DUPLICATE_BLOCK_ERROR = 'The insertion marker ' +
InsertionMarkerManager.DUPLICATE_BLOCK_ERROR = 'The insertion marker ' +
'manager tried to create a marker but the result is missing %1. If ' +
'you are using a mutator, make sure your domToMutation method is ' +
'properly defined.';
@@ -163,10 +174,10 @@ Blockly.InsertionMarkerManager.DUPLICATE_BLOCK_ERROR = 'The insertion marker ' +
* Sever all links from this object.
* @package
*/
Blockly.InsertionMarkerManager.prototype.dispose = function() {
InsertionMarkerManager.prototype.dispose = function() {
this.availableConnections_.length = 0;
Blockly.Events.disable();
Events.disable();
try {
if (this.firstMarker_) {
this.firstMarker_.dispose();
@@ -175,7 +186,7 @@ Blockly.InsertionMarkerManager.prototype.dispose = function() {
this.lastMarker_.dispose();
}
} finally {
Blockly.Events.enable();
Events.enable();
}
};
@@ -184,7 +195,7 @@ Blockly.InsertionMarkerManager.prototype.dispose = function() {
* change if a block is unplugged and the stack is healed.
* @package
*/
Blockly.InsertionMarkerManager.prototype.updateAvailableConnections = function() {
InsertionMarkerManager.prototype.updateAvailableConnections = function() {
this.availableConnections_ = this.initAvailableConnections_();
};
@@ -194,7 +205,7 @@ Blockly.InsertionMarkerManager.prototype.updateAvailableConnections = function()
* @return {boolean} True if the block would be deleted if dropped immediately.
* @package
*/
Blockly.InsertionMarkerManager.prototype.wouldDeleteBlock = function() {
InsertionMarkerManager.prototype.wouldDeleteBlock = function() {
return this.wouldDeleteBlock_;
};
@@ -205,7 +216,7 @@ Blockly.InsertionMarkerManager.prototype.wouldDeleteBlock = function() {
* immediately.
* @package
*/
Blockly.InsertionMarkerManager.prototype.wouldConnectBlock = function() {
InsertionMarkerManager.prototype.wouldConnectBlock = function() {
return !!this.closestConnection_;
};
@@ -214,23 +225,23 @@ Blockly.InsertionMarkerManager.prototype.wouldConnectBlock = function() {
* This should be called at the end of a drag.
* @package
*/
Blockly.InsertionMarkerManager.prototype.applyConnections = function() {
InsertionMarkerManager.prototype.applyConnections = function() {
if (this.closestConnection_) {
// Don't fire events for insertion markers.
Blockly.Events.disable();
Events.disable();
this.hidePreview_();
Blockly.Events.enable();
Events.enable();
// Connect two blocks together.
this.localConnection_.connect(this.closestConnection_);
if (this.topBlock_.rendered) {
// Trigger a connection animation.
// Determine which connection is inferior (lower in the source stack).
var inferiorConnection = this.localConnection_.isSuperior() ?
this.closestConnection_ : this.localConnection_;
Blockly.blockAnimations.connectionUiEffect(
inferiorConnection.getSourceBlock());
const inferiorConnection = this.localConnection_.isSuperior() ?
this.closestConnection_ :
this.localConnection_;
blockAnimations.connectionUiEffect(inferiorConnection.getSourceBlock());
// Bring the just-edited stack to the front.
var rootBlock = this.topBlock_.getRootBlock();
const rootBlock = this.topBlock_.getRootBlock();
rootBlock.bringToFront();
}
}
@@ -238,46 +249,47 @@ Blockly.InsertionMarkerManager.prototype.applyConnections = function() {
/**
* Update connections based on the most recent move location.
* @param {!Blockly.utils.Coordinate} dxy Position relative to drag start,
* @param {!Coordinate} dxy Position relative to drag start,
* in workspace units.
* @param {?Blockly.IDragTarget} dragTarget The drag target that the block is
* @param {?IDragTarget} dragTarget The drag target that the block is
* currently over.
* @package
*/
Blockly.InsertionMarkerManager.prototype.update = function(dxy, dragTarget) {
var candidate = this.getCandidate_(dxy);
InsertionMarkerManager.prototype.update = function(dxy, dragTarget) {
const candidate = this.getCandidate_(dxy);
this.wouldDeleteBlock_ = this.shouldDelete_(candidate, dragTarget);
var shouldUpdate = this.wouldDeleteBlock_ ||
this.shouldUpdatePreviews_(candidate, dxy);
const shouldUpdate =
this.wouldDeleteBlock_ || this.shouldUpdatePreviews_(candidate, dxy);
if (shouldUpdate) {
// Don't fire events for insertion marker creation or movement.
Blockly.Events.disable();
Events.disable();
this.maybeHidePreview_(candidate);
this.maybeShowPreview_(candidate);
Blockly.Events.enable();
Events.enable();
}
};
/**
* Create an insertion marker that represents the given block.
* @param {!Blockly.BlockSvg} sourceBlock The block that the insertion marker
* @param {!BlockSvg} sourceBlock The block that the insertion marker
* will represent.
* @return {!Blockly.BlockSvg} The insertion marker that represents the given
* @return {!BlockSvg} The insertion marker that represents the given
* block.
* @private
*/
Blockly.InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlock) {
var imType = sourceBlock.type;
InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlock) {
const imType = sourceBlock.type;
Blockly.Events.disable();
Events.disable();
let result;
try {
var result = this.workspace_.newBlock(imType);
result = this.workspace_.newBlock(imType);
result.setInsertionMarker(true);
if (sourceBlock.mutationToDom) {
var oldMutationDom = sourceBlock.mutationToDom();
const oldMutationDom = sourceBlock.mutationToDom();
if (oldMutationDom) {
result.domToMutation(oldMutationDom);
}
@@ -285,22 +297,22 @@ Blockly.InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlo
// Copy field values from the other block. These values may impact the
// rendered size of the insertion marker. Note that we do not care about
// child blocks here.
for (var i = 0; i < sourceBlock.inputList.length; i++) {
var sourceInput = sourceBlock.inputList[i];
if (sourceInput.name == Blockly.constants.COLLAPSED_INPUT_NAME) {
for (let i = 0; i < sourceBlock.inputList.length; i++) {
const sourceInput = sourceBlock.inputList[i];
if (sourceInput.name == constants.COLLAPSED_INPUT_NAME) {
continue; // Ignore the collapsed input.
}
var resultInput = result.inputList[i];
const resultInput = result.inputList[i];
if (!resultInput) {
throw new Error(Blockly.InsertionMarkerManager.DUPLICATE_BLOCK_ERROR
.replace('%1', 'an input'));
throw new Error(InsertionMarkerManager.DUPLICATE_BLOCK_ERROR.replace(
'%1', 'an input'));
}
for (var j = 0; j < sourceInput.fieldRow.length; j++) {
var sourceField = sourceInput.fieldRow[j];
var resultField = resultInput.fieldRow[j];
for (let j = 0; j < sourceInput.fieldRow.length; j++) {
const sourceField = sourceInput.fieldRow[j];
const resultField = resultInput.fieldRow[j];
if (!resultField) {
throw new Error(Blockly.InsertionMarkerManager.DUPLICATE_BLOCK_ERROR
.replace('%1', 'a field'));
throw new Error(InsertionMarkerManager.DUPLICATE_BLOCK_ERROR.replace(
'%1', 'a field'));
}
resultField.setValue(sourceField.getValue());
}
@@ -312,7 +324,7 @@ Blockly.InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlo
result.initSvg();
result.getSvgRoot().setAttribute('visibility', 'hidden');
} finally {
Blockly.Events.enable();
Events.enable();
}
return result;
@@ -323,23 +335,23 @@ Blockly.InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlo
* only be called once, at the beginning of a drag.
* If the stack has more than one block, this function will populate
* lastOnStack_ and create the corresponding insertion marker.
* @return {!Array<!Blockly.RenderedConnection>} A list of available
* @return {!Array<!RenderedConnection>} A list of available
* connections.
* @private
*/
Blockly.InsertionMarkerManager.prototype.initAvailableConnections_ = function() {
var available = this.topBlock_.getConnections_(false);
InsertionMarkerManager.prototype.initAvailableConnections_ = function() {
const available = this.topBlock_.getConnections_(false);
// Also check the last connection on this stack
var lastOnStack = this.topBlock_.lastConnectionInStack(true);
const lastOnStack = this.topBlock_.lastConnectionInStack(true);
if (lastOnStack && lastOnStack != this.topBlock_.nextConnection) {
available.push(lastOnStack);
this.lastOnStack_ = lastOnStack;
if (this.lastMarker_) {
Blockly.Events.disable();
Events.disable();
try {
this.lastMarker_.dispose();
} finally {
Blockly.Events.enable();
Events.enable();
}
}
this.lastMarker_ = this.createMarkerBlock_(lastOnStack.getSourceBlock());
@@ -352,16 +364,16 @@ Blockly.InsertionMarkerManager.prototype.initAvailableConnections_ = function()
* updated based on the closest candidate and the current drag distance.
* @param {!Object} candidate An object containing a local connection, a closest
* connection, and a radius. Returned by getCandidate_.
* @param {!Blockly.utils.Coordinate} dxy Position relative to drag start,
* @param {!Coordinate} dxy Position relative to drag start,
* in workspace units.
* @return {boolean} Whether the preview should be updated.
* @private
*/
Blockly.InsertionMarkerManager.prototype.shouldUpdatePreviews_ = function(
InsertionMarkerManager.prototype.shouldUpdatePreviews_ = function(
candidate, dxy) {
var candidateLocal = candidate.local;
var candidateClosest = candidate.closest;
var radius = candidate.radius;
const candidateLocal = candidate.local;
const candidateClosest = candidate.closest;
const radius = candidate.radius;
// Found a connection!
if (candidateLocal && candidateClosest) {
@@ -373,57 +385,55 @@ Blockly.InsertionMarkerManager.prototype.shouldUpdatePreviews_ = function(
this.localConnection_ == candidateLocal) {
return false;
}
var xDiff = this.localConnection_.x + dxy.x - this.closestConnection_.x;
var yDiff = this.localConnection_.y + dxy.y - this.closestConnection_.y;
var curDistance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
const xDiff = this.localConnection_.x + dxy.x - this.closestConnection_.x;
const yDiff = this.localConnection_.y + dxy.y - this.closestConnection_.y;
const curDistance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
// Slightly prefer the existing preview over a new preview.
return !(
candidateClosest &&
radius > curDistance -
Blockly.internalConstants.CURRENT_CONNECTION_PREFERENCE);
radius >
curDistance - internalConstants.CURRENT_CONNECTION_PREFERENCE);
} else if (!this.localConnection_ && !this.closestConnection_) {
// We weren't showing a preview before, but we should now.
// We weren't showing a preview before, but we should now.
return true;
} else {
console.error('Only one of localConnection_ and closestConnection_ was set.');
console.error(
'Only one of localConnection_ and closestConnection_ was set.');
}
} else { // No connection found.
// Only need to update if we were showing a preview before.
return !!(this.localConnection_ && this.closestConnection_);
}
console.error('Returning true from shouldUpdatePreviews, but it\'s not clear why.');
console.error(
'Returning true from shouldUpdatePreviews, but it\'s not clear why.');
return true;
};
/**
* Find the nearest valid connection, which may be the same as the current
* closest connection.
* @param {!Blockly.utils.Coordinate} dxy Position relative to drag start,
* @param {!Coordinate} dxy Position relative to drag start,
* in workspace units.
* @return {!Object} An object containing a local connection, a closest
* connection, and a radius.
* @private
*/
Blockly.InsertionMarkerManager.prototype.getCandidate_ = function(dxy) {
var radius = this.getStartRadius_();
var candidateClosest = null;
var candidateLocal = null;
InsertionMarkerManager.prototype.getCandidate_ = function(dxy) {
let radius = this.getStartRadius_();
let candidateClosest = null;
let candidateLocal = null;
for (var i = 0; i < this.availableConnections_.length; i++) {
var myConnection = this.availableConnections_[i];
var neighbour = myConnection.closest(radius, dxy);
for (let i = 0; i < this.availableConnections_.length; i++) {
const myConnection = this.availableConnections_[i];
const neighbour = myConnection.closest(radius, dxy);
if (neighbour.connection) {
candidateClosest = neighbour.connection;
candidateLocal = myConnection;
radius = neighbour.radius;
}
}
return {
closest: candidateClosest,
local: candidateLocal,
radius: radius
};
return {closest: candidateClosest, local: candidateLocal, radius: radius};
};
/**
@@ -432,38 +442,38 @@ Blockly.InsertionMarkerManager.prototype.getCandidate_ = function(dxy) {
* connection.
* @private
*/
Blockly.InsertionMarkerManager.prototype.getStartRadius_ = function() {
InsertionMarkerManager.prototype.getStartRadius_ = function() {
// If there is already a connection highlighted,
// increase the radius we check for making new connections.
// Why? When a connection is highlighted, blocks move around when the insertion
// marker is created, which could cause the connection became out of range.
// By increasing radiusConnection when a connection already exists,
// we never "lose" the connection from the offset.
// Why? When a connection is highlighted, blocks move around when the
// insertion marker is created, which could cause the connection became out of
// range. By increasing radiusConnection when a connection already exists, we
// never "lose" the connection from the offset.
if (this.closestConnection_ && this.localConnection_) {
return Blockly.internalConstants.CONNECTING_SNAP_RADIUS;
return internalConstants.CONNECTING_SNAP_RADIUS;
}
return Blockly.internalConstants.SNAP_RADIUS;
return internalConstants.SNAP_RADIUS;
};
/**
* Whether ending the drag would delete the block.
* @param {!Object} candidate An object containing a local connection, a closest
* connection, and a radius.
* @param {?Blockly.IDragTarget} dragTarget The drag target that the block is
* @param {?IDragTarget} dragTarget The drag target that the block is
* currently over.
* @return {boolean} Whether dropping the block immediately would delete the
* block.
* @private
*/
Blockly.InsertionMarkerManager.prototype.shouldDelete_ = function(
InsertionMarkerManager.prototype.shouldDelete_ = function(
candidate, dragTarget) {
if (dragTarget) {
var componentManager = this.workspace_.getComponentManager();
var isDeleteArea = componentManager.hasCapability(dragTarget.id,
Blockly.ComponentManager.Capability.DELETE_AREA);
const componentManager = this.workspace_.getComponentManager();
const isDeleteArea = componentManager.hasCapability(
dragTarget.id, ComponentManager.Capability.DELETE_AREA);
if (isDeleteArea) {
return (
/** @type {!Blockly.IDeleteArea} */ (dragTarget))
/** @type {!IDeleteArea} */ (dragTarget))
.wouldDelete(this.topBlock_, candidate && !!candidate.closest);
}
}
@@ -479,13 +489,13 @@ Blockly.InsertionMarkerManager.prototype.shouldDelete_ = function(
* connection, and a radius.
* @private
*/
Blockly.InsertionMarkerManager.prototype.maybeShowPreview_ = function(candidate) {
InsertionMarkerManager.prototype.maybeShowPreview_ = function(candidate) {
// Nope, don't add a marker.
if (this.wouldDeleteBlock_) {
return;
}
var closest = candidate.closest;
var local = candidate.local;
const closest = candidate.closest;
const local = candidate.local;
// Nothing to connect to.
if (!closest) {
@@ -509,22 +519,22 @@ Blockly.InsertionMarkerManager.prototype.maybeShowPreview_ = function(candidate)
* highlight or an insertion marker, and shows the appropriate one.
* @private
*/
Blockly.InsertionMarkerManager.prototype.showPreview_ = function() {
var closest = this.closestConnection_;
var renderer = this.workspace_.getRenderer();
var method = renderer.getConnectionPreviewMethod(
/** @type {!Blockly.RenderedConnection} */ (closest),
/** @type {!Blockly.RenderedConnection} */ (this.localConnection_),
InsertionMarkerManager.prototype.showPreview_ = function() {
const closest = this.closestConnection_;
const renderer = this.workspace_.getRenderer();
const method = renderer.getConnectionPreviewMethod(
/** @type {!RenderedConnection} */ (closest),
/** @type {!RenderedConnection} */ (this.localConnection_),
this.topBlock_);
switch (method) {
case Blockly.InsertionMarkerManager.PREVIEW_TYPE.INPUT_OUTLINE:
case InsertionMarkerManager.PREVIEW_TYPE.INPUT_OUTLINE:
this.showInsertionInputOutline_();
break;
case Blockly.InsertionMarkerManager.PREVIEW_TYPE.INSERTION_MARKER:
case InsertionMarkerManager.PREVIEW_TYPE.INSERTION_MARKER:
this.showInsertionMarker_();
break;
case Blockly.InsertionMarkerManager.PREVIEW_TYPE.REPLACEMENT_FADE:
case InsertionMarkerManager.PREVIEW_TYPE.REPLACEMENT_FADE:
this.showReplacementFade_();
break;
}
@@ -544,7 +554,7 @@ Blockly.InsertionMarkerManager.prototype.showPreview_ = function() {
* connection, and a radius.
* @private
*/
Blockly.InsertionMarkerManager.prototype.maybeHidePreview_ = function(candidate) {
InsertionMarkerManager.prototype.maybeHidePreview_ = function(candidate) {
// If there's no new preview, remove the old one but don't bother deleting it.
// We might need it later, and this saves disposing of it and recreating it.
if (!candidate.closest) {
@@ -552,12 +562,14 @@ Blockly.InsertionMarkerManager.prototype.maybeHidePreview_ = function(candidate)
} else {
// If there's a new preview and there was an preview before, and either
// connection has changed, remove the old preview.
var hadPreview = this.closestConnection_ && this.localConnection_;
var closestChanged = this.closestConnection_ != candidate.closest;
var localChanged = this.localConnection_ != candidate.local;
const hadPreview = this.closestConnection_ && this.localConnection_;
const closestChanged = this.closestConnection_ != candidate.closest;
const localChanged = this.localConnection_ != candidate.local;
// Also hide if we had a preview before but now we're going to delete instead.
if (hadPreview && (closestChanged || localChanged || this.wouldDeleteBlock_)) {
// Also hide if we had a preview before but now we're going to delete
// instead.
if (hadPreview &&
(closestChanged || localChanged || this.wouldDeleteBlock_)) {
this.hidePreview_();
}
}
@@ -573,10 +585,10 @@ Blockly.InsertionMarkerManager.prototype.maybeHidePreview_ = function(candidate)
* highlight or an insertion marker, and hides the appropriate one.
* @private
*/
Blockly.InsertionMarkerManager.prototype.hidePreview_ = function() {
InsertionMarkerManager.prototype.hidePreview_ = function() {
if (this.closestConnection_ && this.closestConnection_.targetBlock() &&
this.workspace_.getRenderer()
.shouldHighlightConnection(this.closestConnection_)) {
this.workspace_.getRenderer().shouldHighlightConnection(
this.closestConnection_)) {
this.closestConnection_.unhighlight();
}
if (this.fadedBlock_) {
@@ -593,16 +605,17 @@ Blockly.InsertionMarkerManager.prototype.hidePreview_ = function() {
* manager state).
* @private
*/
Blockly.InsertionMarkerManager.prototype.showInsertionMarker_ = function() {
var local = this.localConnection_;
var closest = this.closestConnection_;
InsertionMarkerManager.prototype.showInsertionMarker_ = function() {
const local = this.localConnection_;
const closest = this.closestConnection_;
var isLastInStack = this.lastOnStack_ && local == this.lastOnStack_;
var imBlock = isLastInStack ? this.lastMarker_ : this.firstMarker_;
var imConn = imBlock.getMatchingConnection(local.getSourceBlock(), local);
const isLastInStack = this.lastOnStack_ && local == this.lastOnStack_;
const imBlock = isLastInStack ? this.lastMarker_ : this.firstMarker_;
const imConn = imBlock.getMatchingConnection(local.getSourceBlock(), local);
if (imConn == this.markerConnection_) {
throw Error('Made it to showInsertionMarker_ even though the marker isn\'t ' +
throw Error(
'Made it to showInsertionMarker_ even though the marker isn\'t ' +
'changing');
}
@@ -629,23 +642,22 @@ Blockly.InsertionMarkerManager.prototype.showInsertionMarker_ = function() {
* to their original state.
* @private
*/
Blockly.InsertionMarkerManager.prototype.hideInsertionMarker_ = function() {
InsertionMarkerManager.prototype.hideInsertionMarker_ = function() {
if (!this.markerConnection_) {
console.log('No insertion marker connection to disconnect');
return;
}
var imConn = this.markerConnection_;
var imBlock = imConn.getSourceBlock();
var markerNext = imBlock.nextConnection;
var markerPrev = imBlock.previousConnection;
var markerOutput = imBlock.outputConnection;
const imConn = this.markerConnection_;
const imBlock = imConn.getSourceBlock();
const markerNext = imBlock.nextConnection;
const markerPrev = imBlock.previousConnection;
const markerOutput = imBlock.outputConnection;
var isFirstInStatementStack =
const isFirstInStatementStack =
(imConn == markerNext && !(markerPrev && markerPrev.targetConnection));
var isFirstInOutputStack =
imConn.type == Blockly.connectionTypes.INPUT_VALUE &&
const isFirstInOutputStack = imConn.type == connectionTypes.INPUT_VALUE &&
!(markerOutput && markerOutput.targetConnection);
// The insertion marker is the first block in a stack. Unplug won't do
// anything in that case. Instead, unplug the following block.
@@ -653,12 +665,12 @@ Blockly.InsertionMarkerManager.prototype.hideInsertionMarker_ = function() {
imConn.targetBlock().unplug(false);
}
// Inside of a C-block, first statement connection.
else if (imConn.type == Blockly.connectionTypes.NEXT_STATEMENT &&
imConn != markerNext) {
var innerConnection = imConn.targetConnection;
else if (
imConn.type == connectionTypes.NEXT_STATEMENT && imConn != markerNext) {
const innerConnection = imConn.targetConnection;
innerConnection.getSourceBlock().unplug(false);
var previousBlockNextConnection =
const previousBlockNextConnection =
markerPrev ? markerPrev.targetConnection : null;
imBlock.unplug(true);
@@ -670,12 +682,13 @@ Blockly.InsertionMarkerManager.prototype.hideInsertionMarker_ = function() {
}
if (imConn.targetConnection) {
throw Error('markerConnection_ still connected at the end of ' +
throw Error(
'markerConnection_ still connected at the end of ' +
'disconnectInsertionMarker');
}
this.markerConnection_ = null;
var svg = imBlock.getSvgRoot();
const svg = imBlock.getSvgRoot();
if (svg) {
svg.setAttribute('visibility', 'hidden');
}
@@ -685,8 +698,8 @@ Blockly.InsertionMarkerManager.prototype.hideInsertionMarker_ = function() {
* Shows an outline around the input the closest connection belongs to.
* @private
*/
Blockly.InsertionMarkerManager.prototype.showInsertionInputOutline_ = function() {
var closest = this.closestConnection_;
InsertionMarkerManager.prototype.showInsertionInputOutline_ = function() {
const closest = this.closestConnection_;
this.highlightedBlock_ = closest.getSourceBlock();
this.highlightedBlock_.highlightShapeForInput(closest, true);
};
@@ -695,7 +708,7 @@ Blockly.InsertionMarkerManager.prototype.showInsertionInputOutline_ = function()
* Hides any visible input outlines.
* @private
*/
Blockly.InsertionMarkerManager.prototype.hideInsertionInputOutline_ = function() {
InsertionMarkerManager.prototype.hideInsertionInputOutline_ = function() {
this.highlightedBlock_.highlightShapeForInput(this.closestConnection_, false);
this.highlightedBlock_ = null;
};
@@ -705,7 +718,7 @@ Blockly.InsertionMarkerManager.prototype.hideInsertionInputOutline_ = function()
* (the block that is currently connected to it).
* @private
*/
Blockly.InsertionMarkerManager.prototype.showReplacementFade_ = function() {
InsertionMarkerManager.prototype.showReplacementFade_ = function() {
this.fadedBlock_ = this.closestConnection_.targetBlock();
this.fadedBlock_.fadeForReplacement(true);
};
@@ -714,7 +727,7 @@ Blockly.InsertionMarkerManager.prototype.showReplacementFade_ = function() {
* Hides/Removes any visible fade affects.
* @private
*/
Blockly.InsertionMarkerManager.prototype.hideReplacementFade_ = function() {
InsertionMarkerManager.prototype.hideReplacementFade_ = function() {
this.fadedBlock_.fadeForReplacement(false);
this.fadedBlock_ = null;
};
@@ -722,12 +735,12 @@ Blockly.InsertionMarkerManager.prototype.hideReplacementFade_ = function() {
/**
* 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.
* @package
*/
Blockly.InsertionMarkerManager.prototype.getInsertionMarkers = function() {
var result = [];
InsertionMarkerManager.prototype.getInsertionMarkers = function() {
const result = [];
if (this.firstMarker_) {
result.push(this.firstMarker_);
}
@@ -736,3 +749,5 @@ Blockly.InsertionMarkerManager.prototype.getInsertionMarkers = function() {
}
return result;
};
exports = InsertionMarkerManager;

View File

@@ -15,7 +15,7 @@ goog.module('Blockly.IASTNodeLocationSvg');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IASTNodeLocation = goog.require('Blockly.IASTNodeLocation');
const IASTNodeLocation = goog.requireType('Blockly.IASTNodeLocation');
/**

View File

@@ -18,7 +18,7 @@ goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const IASTNodeLocation = goog.require('Blockly.IASTNodeLocation');
const IASTNodeLocation = goog.requireType('Blockly.IASTNodeLocation');
/**

View File

@@ -16,7 +16,7 @@ goog.module('Blockly.IAutoHideable');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IComponent = goog.require('Blockly.IComponent');
const IComponent = goog.requireType('Blockly.IComponent');
/**

View File

@@ -19,9 +19,9 @@ const BlockDragSurfaceSvg = goog.requireType('Blockly.BlockDragSurfaceSvg');
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const IContextMenu = goog.require('Blockly.IContextMenu');
const IContextMenu = goog.requireType('Blockly.IContextMenu');
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.require('Blockly.IDraggable');
const IDraggable = goog.requireType('Blockly.IDraggable');
/**

View File

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

View File

@@ -18,7 +18,7 @@ goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.requireType('Blockly.IDraggable');
/* eslint-disable-next-line no-unused-vars */
const IDragTarget = goog.require('Blockly.IDragTarget');
const IDragTarget = goog.requireType('Blockly.IDragTarget');
/**

View File

@@ -16,7 +16,7 @@ goog.module('Blockly.IDragTarget');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IComponent = goog.require('Blockly.IComponent');
const IComponent = goog.requireType('Blockly.IComponent');
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.requireType('Blockly.IDraggable');
/* eslint-disable-next-line no-unused-vars */

View File

@@ -15,7 +15,7 @@ goog.module('Blockly.IDraggable');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IDeletable = goog.require('Blockly.IDeletable');
const IDeletable = goog.requireType('Blockly.IDeletable');
/**

View File

@@ -19,13 +19,13 @@ const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const IRegistrable = goog.require('Blockly.IRegistrable');
const IRegistrable = goog.requireType('Blockly.IRegistrable');
/* eslint-disable-next-line no-unused-vars */
const Svg = goog.requireType('Blockly.utils.Svg');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const {FlyoutDefinition} = goog.requireType('Blockly.utils.toolbox');
const toolbox = goog.requireType('Blockly.utils.toolbox');
/**
@@ -142,7 +142,7 @@ IFlyout.prototype.hide;
/**
* Show and populate the flyout.
* @param {!FlyoutDefinition|string} flyoutDef Contents to
* @param {!toolbox.FlyoutDefinition|string} flyoutDef Contents to
* display in the flyout. This is either an array of Nodes, a NodeList, a
* toolbox definition, or a string with the name of the dynamic category.
*/

View File

@@ -17,9 +17,9 @@ goog.module.declareLegacyNamespace();
/* 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');
const MetricsManager = goog.requireType('Blockly.MetricsManager');
/* eslint-disable-next-line no-unused-vars */
const {AbsoluteMetrics, ContainerRegion, ToolboxMetrics} = goog.requireType('Blockly.MetricsManager');
const Size = goog.requireType('Blockly.utils.Size');
/**
@@ -39,13 +39,13 @@ 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 {!ContainerRegion=} opt_viewMetrics The view
* @param {!MetricsManager.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 {!ContainerRegion=} opt_contentMetrics The
* @param {!MetricsManager.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 {!ContainerRegion} The metrics for the scroll
* @return {!MetricsManager.ContainerRegion} The metrics for the scroll
* container
*/
IMetricsManager.prototype.getScrollMetrics;
@@ -55,7 +55,7 @@ IMetricsManager.prototype.getScrollMetrics;
* 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 {!ToolboxMetrics} The width and height of the
* @return {!MetricsManager.ToolboxMetrics} The width and height of the
* flyout.
* @public
*/
@@ -66,7 +66,7 @@ IMetricsManager.prototype.getFlyoutMetrics;
* 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 {!ToolboxMetrics} The object with the width,
* @return {!MetricsManager.ToolboxMetrics} The object with the width,
* height and position of the toolbox.
* @public
*/
@@ -84,7 +84,7 @@ 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 {!AbsoluteMetrics} The absolute metrics for
* @return {!MetricsManager.AbsoluteMetrics} The absolute metrics for
* the workspace.
* @public
*/
@@ -95,7 +95,7 @@ IMetricsManager.prototype.getAbsoluteMetrics;
* 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 {!ContainerRegion} The width, height, top and
* @return {!MetricsManager.ContainerRegion} The width, height, top and
* left of the viewport in either workspace coordinates or pixel
* coordinates.
* @public
@@ -108,7 +108,7 @@ 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 {!ContainerRegion} The
* @return {!MetricsManager.ContainerRegion} The
* metrics for the content container.
* @public
*/

View File

@@ -15,11 +15,11 @@ goog.module('Blockly.IPositionable');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IComponent = goog.require('Blockly.IComponent');
const IComponent = goog.requireType('Blockly.IComponent');
/* eslint-disable-next-line no-unused-vars */
const MetricsManager = goog.requireType('Blockly.MetricsManager');
/* eslint-disable-next-line no-unused-vars */
const Rect = goog.requireType('Blockly.utils.Rect');
/* eslint-disable-next-line no-unused-vars */
const {UiMetrics} = goog.requireType('Blockly.MetricsManager');
/**
@@ -31,7 +31,7 @@ const IPositionable = function() {};
/**
* Positions the element. Called when the window is resized.
* @param {!UiMetrics} metrics The workspace metrics.
* @param {!MetricsManager.UiMetrics} metrics The workspace metrics.
* @param {!Array<!Rect>} savedPositions List of rectangles that
* are already on the workspace.
*/

View File

@@ -15,9 +15,9 @@ goog.module('Blockly.ISelectableToolboxItem');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IToolboxItem = goog.require('Blockly.IToolboxItem');
const IToolboxItem = goog.requireType('Blockly.IToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const {FlyoutItemInfoArray} = goog.requireType('Blockly.utils.toolbox');
const toolbox = goog.requireType('Blockly.utils.toolbox');
/**
@@ -37,7 +37,7 @@ ISelectableToolboxItem.prototype.getName;
/**
* Gets the contents of the toolbox item. These are items that are meant to be
* displayed in the flyout.
* @return {!FlyoutItemInfoArray|string} The definition
* @return {!toolbox.FlyoutItemInfoArray|string} The definition
* of items to be displayed in the flyout.
* @public
*/

View File

@@ -17,13 +17,13 @@ goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IFlyout = goog.requireType('Blockly.IFlyout');
/* eslint-disable-next-line no-unused-vars */
const IRegistrable = goog.require('Blockly.IRegistrable');
const IRegistrable = goog.requireType('Blockly.IRegistrable');
/* eslint-disable-next-line no-unused-vars */
const IToolboxItem = goog.requireType('Blockly.IToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const {ToolboxInfo} = goog.requireType('Blockly.utils.toolbox');
const toolbox = goog.requireType('Blockly.utils.toolbox');
/**
@@ -41,7 +41,7 @@ IToolbox.prototype.init;
/**
* Fills the toolbox with new toolbox items and removes any old contents.
* @param {!ToolboxInfo} toolboxDef Object holding information
* @param {!toolbox.ToolboxInfo} toolboxDef Object holding information
* for creating a toolbox.
*/
IToolbox.prototype.render;

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

@@ -16,8 +16,8 @@ goog.module.declareLegacyNamespace();
const ASTNode = goog.require('Blockly.ASTNode');
const Cursor = goog.require('Blockly.Cursor');
const {register, Type} = goog.require('Blockly.registry');
const {inherits} = goog.require('Blockly.utils.object');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
@@ -30,7 +30,7 @@ const {inherits} = goog.require('Blockly.utils.object');
const BasicCursor = function() {
BasicCursor.superClass_.constructor.call(this);
};
inherits(BasicCursor, Cursor);
object.inherits(BasicCursor, Cursor);
/**
* Name used for registering a basic cursor.
@@ -214,6 +214,7 @@ BasicCursor.prototype.getRightMostChild_ = function(node) {
return this.getRightMostChild_(newNode);
};
register(Type.CURSOR, BasicCursor.registrationName, BasicCursor);
registry.register(
registry.Type.CURSOR, BasicCursor.registrationName, BasicCursor);
exports = BasicCursor;

View File

@@ -16,8 +16,8 @@ goog.module.declareLegacyNamespace();
const ASTNode = goog.require('Blockly.ASTNode');
const Marker = goog.require('Blockly.Marker');
const {DEFAULT, register, Type} = goog.require('Blockly.registry');
const {inherits} = goog.require('Blockly.utils.object');
const object = goog.require('Blockly.utils.object');
const registry = goog.require('Blockly.registry');
/**
@@ -34,7 +34,7 @@ const Cursor = function() {
*/
this.type = 'cursor';
};
inherits(Cursor, Marker);
object.inherits(Cursor, Marker);
/**
* Find the next connection, field, or block.
@@ -134,6 +134,6 @@ Cursor.prototype.out = function() {
return newNode;
};
register(Type.CURSOR, DEFAULT, Cursor);
registry.register(registry.Type.CURSOR, registry.DEFAULT, Cursor);
exports = Cursor;

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

@@ -18,7 +18,7 @@ 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');
const object = goog.require('Blockly.utils.object');
/**
@@ -29,7 +29,7 @@ const {inherits} = goog.require('Blockly.utils.object');
const TabNavigateCursor = function() {
TabNavigateCursor.superClass_.constructor.call(this);
};
inherits(TabNavigateCursor, BasicCursor);
object.inherits(TabNavigateCursor, BasicCursor);
/**
* Skip all nodes except for tab navigable fields.

View File

@@ -10,11 +10,12 @@
*/
'use strict';
goog.provide('Blockly.MenuItem');
goog.module('Blockly.MenuItem');
goog.module.declareLegacyNamespace();
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.IdGenerator');
const aria = goog.require('Blockly.utils.aria');
const dom = goog.require('Blockly.utils.dom');
const idGenerator = goog.require('Blockly.utils.IdGenerator');
/**
@@ -25,7 +26,7 @@ goog.require('Blockly.utils.IdGenerator');
* @param {string=} opt_value Data/model associated with the menu item.
* @constructor
*/
Blockly.MenuItem = function(content, opt_value) {
const MenuItem = function(content, opt_value) {
/**
* Human-readable text of this menu item, or the HTML element to display.
* @type {string|!HTMLElement}
@@ -63,7 +64,7 @@ Blockly.MenuItem = function(content, opt_value) {
/**
* ARIA name for this menu.
* @type {?Blockly.utils.aria.Role}
* @type {?aria.Role}
* @private
*/
this.roleName_ = null;
@@ -102,9 +103,9 @@ Blockly.MenuItem = function(content, opt_value) {
* Creates the menuitem's DOM.
* @return {!Element} Completed DOM.
*/
Blockly.MenuItem.prototype.createDom = function() {
var element = document.createElement('div');
element.id = Blockly.utils.IdGenerator.getNextUniqueId();
MenuItem.prototype.createDom = function() {
const element = document.createElement('div');
element.id = idGenerator.getNextUniqueId();
this.element_ = element;
// Set class and style
@@ -112,20 +113,20 @@ Blockly.MenuItem.prototype.createDom = function() {
element.className = 'blocklyMenuItem goog-menuitem ' +
(this.enabled_ ? '' : 'blocklyMenuItemDisabled goog-menuitem-disabled ') +
(this.checked_ ? 'blocklyMenuItemSelected goog-option-selected ' : '') +
(this.highlight_ ?
'blocklyMenuItemHighlight goog-menuitem-highlight ' : '') +
(this.highlight_ ? 'blocklyMenuItemHighlight goog-menuitem-highlight ' :
'') +
(this.rightToLeft_ ? 'blocklyMenuItemRtl goog-menuitem-rtl ' : '');
var content = document.createElement('div');
const content = document.createElement('div');
content.className = 'blocklyMenuItemContent goog-menuitem-content';
// Add a checkbox for checkable menu items.
if (this.checkable_) {
var checkbox = document.createElement('div');
const checkbox = document.createElement('div');
checkbox.className = 'blocklyMenuItemCheckbox goog-menuitem-checkbox';
content.appendChild(checkbox);
}
var contentDom = /** @type {!HTMLElement} */ (this.content_);
let contentDom = /** @type {!HTMLElement} */ (this.content_);
if (typeof this.content_ == 'string') {
contentDom = document.createTextNode(this.content_);
}
@@ -134,12 +135,12 @@ Blockly.MenuItem.prototype.createDom = function() {
// Initialize ARIA role and state.
if (this.roleName_) {
Blockly.utils.aria.setRole(element, this.roleName_);
aria.setRole(element, this.roleName_);
}
Blockly.utils.aria.setState(element, Blockly.utils.aria.State.SELECTED,
aria.setState(
element, aria.State.SELECTED,
(this.checkable_ && this.checked_) || false);
Blockly.utils.aria.setState(element, Blockly.utils.aria.State.DISABLED,
!this.enabled_);
aria.setState(element, aria.State.DISABLED, !this.enabled_);
return element;
};
@@ -147,7 +148,7 @@ Blockly.MenuItem.prototype.createDom = function() {
/**
* Dispose of this menu item.
*/
Blockly.MenuItem.prototype.dispose = function() {
MenuItem.prototype.dispose = function() {
this.element_ = null;
};
@@ -156,7 +157,7 @@ Blockly.MenuItem.prototype.dispose = function() {
* @return {?Element} The DOM element.
* @package
*/
Blockly.MenuItem.prototype.getElement = function() {
MenuItem.prototype.getElement = function() {
return this.element_;
};
@@ -165,7 +166,7 @@ Blockly.MenuItem.prototype.getElement = function() {
* @return {string} Unique component ID.
* @package
*/
Blockly.MenuItem.prototype.getId = function() {
MenuItem.prototype.getId = function() {
return this.element_.id;
};
@@ -174,7 +175,7 @@ Blockly.MenuItem.prototype.getId = function() {
* @return {*} value Value associated with the menu item.
* @package
*/
Blockly.MenuItem.prototype.getValue = function() {
MenuItem.prototype.getValue = function() {
return this.value_;
};
@@ -183,16 +184,16 @@ Blockly.MenuItem.prototype.getValue = function() {
* @param {boolean} rtl True if RTL, false if LTR.
* @package
*/
Blockly.MenuItem.prototype.setRightToLeft = function(rtl) {
MenuItem.prototype.setRightToLeft = function(rtl) {
this.rightToLeft_ = rtl;
};
/**
* Set the menu item's accessibility role.
* @param {!Blockly.utils.aria.Role} roleName Role name.
* @param {!aria.Role} roleName Role name.
* @package
*/
Blockly.MenuItem.prototype.setRole = function(roleName) {
MenuItem.prototype.setRole = function(roleName) {
this.roleName_ = roleName;
};
@@ -202,7 +203,7 @@ Blockly.MenuItem.prototype.setRole = function(roleName) {
* @param {boolean} checkable Whether the menu item is checkable.
* @package
*/
Blockly.MenuItem.prototype.setCheckable = function(checkable) {
MenuItem.prototype.setCheckable = function(checkable) {
this.checkable_ = checkable;
};
@@ -211,7 +212,7 @@ Blockly.MenuItem.prototype.setCheckable = function(checkable) {
* @param {boolean} checked Whether to check or uncheck the component.
* @package
*/
Blockly.MenuItem.prototype.setChecked = function(checked) {
MenuItem.prototype.setChecked = function(checked) {
this.checked_ = checked;
};
@@ -220,21 +221,21 @@ Blockly.MenuItem.prototype.setChecked = function(checked) {
* @param {boolean} highlight Whether to highlight or unhighlight the component.
* @package
*/
Blockly.MenuItem.prototype.setHighlighted = function(highlight) {
MenuItem.prototype.setHighlighted = function(highlight) {
this.highlight_ = highlight;
var el = this.getElement();
const el = this.getElement();
if (el && this.isEnabled()) {
// goog-menuitem-highlight is deprecated, use blocklyMenuItemHighlight.
// May 2020.
var name = 'blocklyMenuItemHighlight';
var nameDep = 'goog-menuitem-highlight';
const name = 'blocklyMenuItemHighlight';
const nameDep = 'goog-menuitem-highlight';
if (highlight) {
Blockly.utils.dom.addClass(el, name);
Blockly.utils.dom.addClass(el, nameDep);
dom.addClass(el, name);
dom.addClass(el, nameDep);
} else {
Blockly.utils.dom.removeClass(el, name);
Blockly.utils.dom.removeClass(el, nameDep);
dom.removeClass(el, name);
dom.removeClass(el, nameDep);
}
}
};
@@ -244,7 +245,7 @@ Blockly.MenuItem.prototype.setHighlighted = function(highlight) {
* @return {boolean} Whether the menu item is enabled.
* @package
*/
Blockly.MenuItem.prototype.isEnabled = function() {
MenuItem.prototype.isEnabled = function() {
return this.enabled_;
};
@@ -253,7 +254,7 @@ Blockly.MenuItem.prototype.isEnabled = function() {
* @param {boolean} enabled Whether to enable or disable the menu item.
* @package
*/
Blockly.MenuItem.prototype.setEnabled = function(enabled) {
MenuItem.prototype.setEnabled = function(enabled) {
this.enabled_ = enabled;
};
@@ -262,7 +263,7 @@ Blockly.MenuItem.prototype.setEnabled = function(enabled) {
* by the user.
* @package
*/
Blockly.MenuItem.prototype.performAction = function() {
MenuItem.prototype.performAction = function() {
if (this.isEnabled() && this.actionHandler_) {
this.actionHandler_(this);
}
@@ -271,10 +272,12 @@ Blockly.MenuItem.prototype.performAction = function() {
/**
* Set the handler that's called when the menu item is activated by the user.
* `obj` will be used as the 'this' object in the function when called.
* @param {function(!Blockly.MenuItem)} fn The handler.
* @param {function(!MenuItem)} fn The handler.
* @param {!Object} obj Used as the 'this' object in fn when called.
* @package
*/
Blockly.MenuItem.prototype.onAction = function(fn, obj) {
MenuItem.prototype.onAction = function(fn, obj) {
this.actionHandler_ = fn.bind(obj);
};
exports = MenuItem;

View File

@@ -13,7 +13,7 @@
goog.provide('Blockly.FlyoutMetricsManager');
goog.provide('Blockly.MetricsManager');
goog.require('Blockly.IMetricsManager');
goog.requireType('Blockly.IMetricsManager');
goog.require('Blockly.registry');
goog.require('Blockly.utils.Size');
goog.require('Blockly.utils.toolbox');

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 BlocklyOptions = goog.requireType('Blockly.BlocklyOptions');
/* 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 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,29 +149,28 @@ 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(
/** @type {!Blockly.BlocklyOptions} */
const workspaceOptions = new Options(
/** @type {!BlocklyOptions} */
({
// If you want to enable disabling, also remove the
// event filter from workspaceChanged_ .
@@ -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

@@ -10,12 +10,18 @@
*/
'use strict';
goog.provide('Blockly.Names');
goog.module('Blockly.Names');
goog.module.declareLegacyNamespace();
goog.require('Blockly.internalConstants');
goog.require('Blockly.Msg');
goog.requireType('Blockly.VariableMap');
const Msg = goog.require('Blockly.Msg');
/* eslint-disable-next-line no-unused-vars */
const VariableMap = goog.requireType('Blockly.VariableMap');
const Variables = goog.require('Blockly.Variables');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const internalConstants = goog.require('Blockly.internalConstants');
/** @suppress {extraRequire} */
goog.requireType('Blockly.Procedures');
/**
@@ -26,12 +32,12 @@ goog.requireType('Blockly.VariableMap');
* before all variable names (but not procedure names).
* @constructor
*/
Blockly.Names = function(reservedWords, opt_variablePrefix) {
const Names = function(reservedWords, opt_variablePrefix) {
this.variablePrefix_ = opt_variablePrefix || '';
this.reservedDict_ = Object.create(null);
if (reservedWords) {
var splitWords = reservedWords.split(',');
for (var i = 0; i < splitWords.length; i++) {
const splitWords = reservedWords.split(',');
for (let i = 0; i < splitWords.length; i++) {
this.reservedDict_[splitWords[i]] = true;
}
}
@@ -45,7 +51,7 @@ Blockly.Names = function(reservedWords, opt_variablePrefix) {
* will never be shown to the user in the workspace or stored in the variable
* map.
*/
Blockly.Names.DEVELOPER_VARIABLE_TYPE = 'DEVELOPER_VARIABLE';
Names.DEVELOPER_VARIABLE_TYPE = 'DEVELOPER_VARIABLE';
/**
* When JavaScript (or most other languages) is generated, variable 'foo' and
@@ -59,7 +65,7 @@ Blockly.Names.DEVELOPER_VARIABLE_TYPE = 'DEVELOPER_VARIABLE';
/**
* Empty the database and start from scratch. The reserved words are kept.
*/
Blockly.Names.prototype.reset = function() {
Names.prototype.reset = function() {
this.db_ = Object.create(null);
this.dbReverse_ = Object.create(null);
this.variableMap_ = null;
@@ -67,31 +73,32 @@ Blockly.Names.prototype.reset = function() {
/**
* Set the variable map that maps from variable name to variable object.
* @param {!Blockly.VariableMap} map The map to track.
* @param {!VariableMap} map The map to track.
*/
Blockly.Names.prototype.setVariableMap = function(map) {
Names.prototype.setVariableMap = function(map) {
this.variableMap_ = map;
};
/**
* Get the name for a user-defined variable, based on its ID.
* This should only be used for variables of realm
* Blockly.internalConstants.VARIABLE_CATEGORY_NAME.
* internalConstants.VARIABLE_CATEGORY_NAME.
* @param {string} id The ID to look up in the variable map.
* @return {?string} The name of the referenced variable, or null if there was
* no variable map or the variable was not found in the map.
* @private
*/
Blockly.Names.prototype.getNameForUserVariable_ = function(id) {
Names.prototype.getNameForUserVariable_ = function(id) {
if (!this.variableMap_) {
console.warn('Deprecated call to Blockly.Names.prototype.getName without ' +
console.warn(
'Deprecated call to Names.prototype.getName without ' +
'defining a variable map. To fix, add the following code in your ' +
'generator\'s init() function:\n' +
'Blockly.YourGeneratorName.nameDB_.setVariableMap(' +
'workspace.getVariableMap());');
return null;
}
var variable = this.variableMap_.getVariableById(id);
const variable = this.variableMap_.getVariableById(id);
if (variable) {
return variable.name;
}
@@ -100,27 +107,27 @@ Blockly.Names.prototype.getNameForUserVariable_ = function(id) {
/**
* Generate names for user variables, but only ones that are being used.
* @param {!Blockly.Workspace} workspace Workspace to generate variables from.
* @param {!Workspace} workspace Workspace to generate variables from.
*/
Blockly.Names.prototype.populateVariables = function(workspace) {
var variables = Blockly.Variables.allUsedVarModels(workspace);
for (var i = 0; i < variables.length; i++) {
Names.prototype.populateVariables = function(workspace) {
const variables = Variables.allUsedVarModels(workspace);
for (let i = 0; i < variables.length; i++) {
this.getName(
variables[i].getId(), Blockly.internalConstants.VARIABLE_CATEGORY_NAME);
variables[i].getId(), internalConstants.VARIABLE_CATEGORY_NAME);
}
};
/**
* Generate names for procedures.
* @param {!Blockly.Workspace} workspace Workspace to generate procedures from.
* @param {!Workspace} workspace Workspace to generate procedures from.
*/
Blockly.Names.prototype.populateProcedures = function(workspace) {
var procedures = Blockly.Procedures.allProcedures(workspace);
Names.prototype.populateProcedures = function(workspace) {
let procedures =
goog.module.get('Blockly.Procedures').allProcedures(workspace);
// Flatten the return vs no-return procedure lists.
procedures = procedures[0].concat(procedures[1]);
for (var i = 0; i < procedures.length; i++) {
this.getName(
procedures[i][0], Blockly.internalConstants.PROCEDURE_CATEGORY_NAME);
for (let i = 0; i < procedures.length; i++) {
this.getName(procedures[i][0], internalConstants.PROCEDURE_CATEGORY_NAME);
}
};
@@ -132,29 +139,29 @@ Blockly.Names.prototype.populateProcedures = function(workspace) {
* ('VARIABLE', 'PROCEDURE', 'DEVELOPER_VARIABLE', etc...).
* @return {string} An entity name that is legal in the exported language.
*/
Blockly.Names.prototype.getName = function(nameOrId, realm) {
var name = nameOrId;
if (realm == Blockly.internalConstants.VARIABLE_CATEGORY_NAME) {
var varName = this.getNameForUserVariable_(nameOrId);
Names.prototype.getName = function(nameOrId, realm) {
let name = nameOrId;
if (realm == internalConstants.VARIABLE_CATEGORY_NAME) {
const varName = this.getNameForUserVariable_(nameOrId);
if (varName) {
// Successful ID lookup.
name = varName;
}
}
var normalizedName = name.toLowerCase();
const normalizedName = name.toLowerCase();
var isVar = realm == Blockly.internalConstants.VARIABLE_CATEGORY_NAME ||
realm == Blockly.Names.DEVELOPER_VARIABLE_TYPE;
const isVar = realm == internalConstants.VARIABLE_CATEGORY_NAME ||
realm == Names.DEVELOPER_VARIABLE_TYPE;
var prefix = isVar ? this.variablePrefix_ : '';
const prefix = isVar ? this.variablePrefix_ : '';
if (!(realm in this.db_)) {
this.db_[realm] = Object.create(null);
}
var realmDb = this.db_[realm];
const realmDb = this.db_[realm];
if (normalizedName in realmDb) {
return prefix + realmDb[normalizedName];
}
var safeName = this.getDistinctName(name, realm);
const safeName = this.getDistinctName(name, realm);
realmDb[normalizedName] = safeName.substr(prefix.length);
return safeName;
};
@@ -165,8 +172,8 @@ Blockly.Names.prototype.getName = function(nameOrId, realm) {
* ('VARIABLE', 'PROCEDURE', 'DEVELOPER_VARIABLE', etc...).
* @return {!Array<string>} A list of Blockly entity names (no constraints).
*/
Blockly.Names.prototype.getUserNames = function(realm) {
var realmDb = this.db_[realm] || {};
Names.prototype.getUserNames = function(realm) {
const realmDb = this.db_[realm] || {};
return Object.keys(realmDb);
};
@@ -180,9 +187,9 @@ Blockly.Names.prototype.getUserNames = function(realm) {
* ('VARIABLE', 'PROCEDURE', 'DEVELOPER_VARIABLE', etc...).
* @return {string} An entity name that is legal in the exported language.
*/
Blockly.Names.prototype.getDistinctName = function(name, realm) {
var safeName = this.safeName_(name);
var i = '';
Names.prototype.getDistinctName = function(name, realm) {
let safeName = this.safeName_(name);
let i = '';
while (this.dbReverse_[safeName + i] ||
(safeName + i) in this.reservedDict_) {
// Collision with existing name. Create a unique name.
@@ -190,9 +197,9 @@ Blockly.Names.prototype.getDistinctName = function(name, realm) {
}
safeName += i;
this.dbReverse_[safeName] = true;
var isVar = realm == Blockly.internalConstants.VARIABLE_CATEGORY_NAME ||
realm == Blockly.Names.DEVELOPER_VARIABLE_TYPE;
var prefix = isVar ? this.variablePrefix_ : '';
const isVar = realm == internalConstants.VARIABLE_CATEGORY_NAME ||
realm == Names.DEVELOPER_VARIABLE_TYPE;
const prefix = isVar ? this.variablePrefix_ : '';
return prefix + safeName;
};
@@ -204,9 +211,9 @@ Blockly.Names.prototype.getDistinctName = function(name, realm) {
* @return {string} Safe entity name.
* @private
*/
Blockly.Names.prototype.safeName_ = function(name) {
Names.prototype.safeName_ = function(name) {
if (!name) {
name = Blockly.Msg['UNNAMED_KEY'] || 'unnamed';
name = Msg['UNNAMED_KEY'] || 'unnamed';
} else {
// Unfortunately names in non-latin characters will look like
// _E9_9F_B3_E4_B9_90 which is pretty meaningless.
@@ -227,7 +234,9 @@ Blockly.Names.prototype.safeName_ = function(name) {
* @param {string} name2 Second name.
* @return {boolean} True if names are the same.
*/
Blockly.Names.equals = function(name1, name2) {
Names.equals = function(name1, name2) {
// name1.localeCompare(name2) is slower.
return name1.toLowerCase() == name2.toLowerCase();
};
exports = Names;

View File

@@ -10,104 +10,111 @@
*/
'use strict';
goog.provide('Blockly.Options');
goog.module('Blockly.Options');
goog.module.declareLegacyNamespace();
goog.require('Blockly.registry');
goog.require('Blockly.Theme');
goog.require('Blockly.Themes.Classic');
goog.require('Blockly.utils.IdGenerator');
goog.require('Blockly.utils.Metrics');
goog.require('Blockly.utils.toolbox');
goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const BlocklyOptions = goog.requireType('Blockly.BlocklyOptions');
const Classic = goog.require('Blockly.Themes.Classic');
const IdGenerator = goog.require('Blockly.utils.IdGenerator');
/* eslint-disable-next-line no-unused-vars */
const Metrics = goog.requireType('Blockly.utils.Metrics');
const Theme = goog.require('Blockly.Theme');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const deprecation = goog.require('Blockly.utils.deprecation');
const registry = goog.require('Blockly.registry');
const toolbox = goog.require('Blockly.utils.toolbox');
/**
* Parse the user-specified options, using reasonable defaults where behaviour
* is unspecified.
* @param {!Blockly.BlocklyOptions} options Dictionary of options.
* Specification: https://developers.google.com/blockly/guides/get-started/web#configuration
* @param {!BlocklyOptions} options Dictionary of options.
* Specification:
* https://developers.google.com/blockly/guides/get-started/web#configuration
* @constructor
*/
Blockly.Options = function(options) {
var readOnly = !!options['readOnly'];
if (readOnly) {
var toolboxJsonDef = null;
var hasCategories = false;
var hasTrashcan = false;
var hasCollapse = false;
var hasComments = false;
var hasDisable = false;
var hasSounds = false;
} else {
var toolboxJsonDef = Blockly.utils.toolbox.convertToolboxDefToJson(options['toolbox']);
var hasCategories = Blockly.utils.toolbox.hasCategories(toolboxJsonDef);
var hasTrashcan = options['trashcan'];
const Options = function(options) {
let toolboxJsonDef = null;
let hasCategories = false;
let hasTrashcan = false;
let hasCollapse = false;
let hasComments = false;
let hasDisable = false;
let hasSounds = false;
const readOnly = !!options['readOnly'];
if (!readOnly) {
toolboxJsonDef = toolbox.convertToolboxDefToJson(options['toolbox']);
hasCategories = toolbox.hasCategories(toolboxJsonDef);
hasTrashcan = options['trashcan'];
if (hasTrashcan === undefined) {
hasTrashcan = hasCategories;
}
var maxTrashcanContents = options['maxTrashcanContents'];
if (hasTrashcan) {
if (maxTrashcanContents === undefined) {
maxTrashcanContents = 32;
}
} else {
maxTrashcanContents = 0;
}
var hasCollapse = options['collapse'];
hasCollapse = options['collapse'];
if (hasCollapse === undefined) {
hasCollapse = hasCategories;
}
var hasComments = options['comments'];
hasComments = options['comments'];
if (hasComments === undefined) {
hasComments = hasCategories;
}
var hasDisable = options['disable'];
hasDisable = options['disable'];
if (hasDisable === undefined) {
hasDisable = hasCategories;
}
var hasSounds = options['sounds'];
hasSounds = options['sounds'];
if (hasSounds === undefined) {
hasSounds = true;
}
}
var rtl = !!options['rtl'];
var horizontalLayout = options['horizontalLayout'];
let maxTrashcanContents = options['maxTrashcanContents'];
if (hasTrashcan) {
if (maxTrashcanContents === undefined) {
maxTrashcanContents = 32;
}
} else {
maxTrashcanContents = 0;
}
const rtl = !!options['rtl'];
let horizontalLayout = options['horizontalLayout'];
if (horizontalLayout === undefined) {
horizontalLayout = false;
}
var toolboxAtStart = options['toolboxPosition'];
let toolboxAtStart = options['toolboxPosition'];
toolboxAtStart = toolboxAtStart !== 'end';
/** @type {!Blockly.utils.toolbox.Position} */
var toolboxPosition;
/** @type {!toolbox.Position} */
let toolboxPosition;
if (horizontalLayout) {
toolboxPosition = toolboxAtStart ?
Blockly.utils.toolbox.Position.TOP : Blockly.utils.toolbox.Position.BOTTOM;
toolboxPosition =
toolboxAtStart ? toolbox.Position.TOP : toolbox.Position.BOTTOM;
} else {
toolboxPosition = (toolboxAtStart == rtl) ?
Blockly.utils.toolbox.Position.RIGHT : Blockly.utils.toolbox.Position.LEFT;
toolboxPosition = (toolboxAtStart == rtl) ? toolbox.Position.RIGHT :
toolbox.Position.LEFT;
}
var hasCss = options['css'];
let hasCss = options['css'];
if (hasCss === undefined) {
hasCss = true;
}
var pathToMedia = 'https://blockly-demo.appspot.com/static/media/';
let pathToMedia = 'https://blockly-demo.appspot.com/static/media/';
if (options['media']) {
pathToMedia = options['media'];
} else if (options['path']) {
// 'path' is a deprecated option which has been replaced by 'media'.
pathToMedia = options['path'] + 'media/';
}
let oneBasedIndex;
if (options['oneBasedIndex'] === undefined) {
var oneBasedIndex = true;
oneBasedIndex = true;
} else {
var oneBasedIndex = !!options['oneBasedIndex'];
oneBasedIndex = !!options['oneBasedIndex'];
}
var renderer = options['renderer'] || 'geras';
const renderer = options['renderer'] || 'geras';
var plugins = options['plugins'] || {};
const plugins = options['plugins'] || {};
/** @type {boolean} */
this.RTL = rtl;
@@ -129,8 +136,8 @@ Blockly.Options = function(options) {
this.pathToMedia = pathToMedia;
/** @type {boolean} */
this.hasCategories = hasCategories;
/** @type {!Blockly.Options.MoveOptions} */
this.moveOptions = Blockly.Options.parseMoveOptions_(options, hasCategories);
/** @type {!Options.MoveOptions} */
this.moveOptions = Options.parseMoveOptions_(options, hasCategories);
/** @deprecated January 2019 */
this.hasScrollbars = !!this.moveOptions.scrollbars;
/** @type {boolean} */
@@ -143,16 +150,16 @@ Blockly.Options = function(options) {
this.hasCss = hasCss;
/** @type {boolean} */
this.horizontalLayout = horizontalLayout;
/** @type {?Blockly.utils.toolbox.ToolboxInfo} */
/** @type {?toolbox.ToolboxInfo} */
this.languageTree = toolboxJsonDef;
/** @type {!Blockly.Options.GridOptions} */
this.gridOptions = Blockly.Options.parseGridOptions_(options);
/** @type {!Blockly.Options.ZoomOptions} */
this.zoomOptions = Blockly.Options.parseZoomOptions_(options);
/** @type {!Blockly.utils.toolbox.Position} */
/** @type {!Options.GridOptions} */
this.gridOptions = Options.parseGridOptions_(options);
/** @type {!Options.ZoomOptions} */
this.zoomOptions = Options.parseZoomOptions_(options);
/** @type {!toolbox.Position} */
this.toolboxPosition = toolboxPosition;
/** @type {!Blockly.Theme} */
this.theme = Blockly.Options.parseThemeOptions_(options);
/** @type {!Theme} */
this.theme = Options.parseThemeOptions_(options);
/** @type {string} */
this.renderer = renderer;
/** @type {?Object} */
@@ -169,7 +176,7 @@ Blockly.Options = function(options) {
* The parent of the current workspace, or null if there is no parent
* workspace. We can assert that this is of type WorkspaceSvg as opposed to
* Workspace as this is only used in a rendered workspace.
* @type {Blockly.WorkspaceSvg}
* @type {WorkspaceSvg}
*/
this.parentWorkspace = options['parentWorkspace'];
@@ -180,14 +187,6 @@ Blockly.Options = function(options) {
this.plugins = plugins;
};
/**
* Blockly options.
* This interface is further described in
* `typings/parts/blockly-interfaces.d.ts`.
* @interface
*/
Blockly.BlocklyOptions = function() {};
/**
* Grid Options.
* @typedef {{
@@ -197,17 +196,17 @@ Blockly.BlocklyOptions = function() {};
* spacing: number
* }}
*/
Blockly.Options.GridOptions;
Options.GridOptions;
/**
* Move Options.
* @typedef {{
* drag: boolean,
* scrollbars: (boolean | !Blockly.Options.ScrollbarOptions),
* scrollbars: (boolean | !Options.ScrollbarOptions),
* wheel: boolean
* }}
*/
Blockly.Options.MoveOptions;
Options.MoveOptions;
/**
* Scrollbar Options.
@@ -216,7 +215,7 @@ Blockly.Options.MoveOptions;
* vertical: boolean
* }}
*/
Blockly.Options.ScrollbarOptions;
Options.ScrollbarOptions;
/**
* Zoom Options.
@@ -230,7 +229,7 @@ Blockly.Options.ScrollbarOptions;
* wheel: boolean
* }}
*/
Blockly.Options.ZoomOptions;
Options.ZoomOptions;
/**
* If set, sets the translation of the workspace to match the scrollbars.
@@ -238,25 +237,25 @@ Blockly.Options.ZoomOptions;
* is a float between 0 and 1 specifying the degree of scrolling.
* @return {void}
*/
Blockly.Options.prototype.setMetrics;
Options.prototype.setMetrics;
/**
* Return an object with the metrics required to size the workspace.
* @return {!Blockly.utils.Metrics} Contains size and position metrics.
* @return {!Metrics} Contains size and position metrics.
*/
Blockly.Options.prototype.getMetrics;
Options.prototype.getMetrics;
/**
* Parse the user-specified move options, using reasonable defaults where
* behaviour is unspecified.
* @param {!Object} options Dictionary of options.
* @param {boolean} hasCategories Whether the workspace has categories or not.
* @return {!Blockly.Options.MoveOptions} Normalized move options.
* @return {!Options.MoveOptions} Normalized move options.
* @private
*/
Blockly.Options.parseMoveOptions_ = function(options, hasCategories) {
var move = options['move'] || {};
var moveOptions = {};
Options.parseMoveOptions_ = function(options, hasCategories) {
const move = options['move'] || {};
const moveOptions = {};
if (move['scrollbars'] === undefined && options['scrollbars'] === undefined) {
moveOptions.scrollbars = hasCategories;
} else if (typeof move['scrollbars'] == 'object') {
@@ -268,7 +267,8 @@ Blockly.Options.parseMoveOptions_ = function(options, hasCategories) {
// !!moveOptions.scrollbars.
if (moveOptions.scrollbars.horizontal && moveOptions.scrollbars.vertical) {
moveOptions.scrollbars = true;
} else if (!moveOptions.scrollbars.horizontal &&
} else if (
!moveOptions.scrollbars.horizontal &&
!moveOptions.scrollbars.vertical) {
moveOptions.scrollbars = false;
}
@@ -298,12 +298,12 @@ Blockly.Options.parseMoveOptions_ = function(options, hasCategories) {
* behaviour is unspecified. See zoom documentation:
* https://developers.google.com/blockly/guides/configure/web/zoom
* @param {!Object} options Dictionary of options.
* @return {!Blockly.Options.ZoomOptions} Normalized zoom options.
* @return {!Options.ZoomOptions} Normalized zoom options.
* @private
*/
Blockly.Options.parseZoomOptions_ = function(options) {
var zoom = options['zoom'] || {};
var zoomOptions = {};
Options.parseZoomOptions_ = function(options) {
const zoom = options['zoom'] || {};
const zoomOptions = {};
if (zoom['controls'] === undefined) {
zoomOptions.controls = false;
} else {
@@ -347,12 +347,12 @@ Blockly.Options.parseZoomOptions_ = function(options) {
* behaviour is unspecified. See grid documentation:
* https://developers.google.com/blockly/guides/configure/web/grid
* @param {!Object} options Dictionary of options.
* @return {!Blockly.Options.GridOptions} Normalized grid options.
* @return {!Options.GridOptions} Normalized grid options.
* @private
*/
Blockly.Options.parseGridOptions_ = function(options) {
var grid = options['grid'] || {};
var gridOptions = {};
Options.parseGridOptions_ = function(options) {
const grid = options['grid'] || {};
const gridOptions = {};
gridOptions.spacing = Number(grid['spacing']) || 0;
gridOptions.colour = grid['colour'] || '#888';
gridOptions.length =
@@ -365,19 +365,19 @@ Blockly.Options.parseGridOptions_ = function(options) {
* Parse the user-specified theme options, using the classic theme as a default.
* https://developers.google.com/blockly/guides/configure/web/themes
* @param {!Object} options Dictionary of options.
* @return {!Blockly.Theme} A Blockly Theme.
* @return {!Theme} A Blockly Theme.
* @private
*/
Blockly.Options.parseThemeOptions_ = function(options) {
var theme = options['theme'] || Blockly.Themes.Classic;
Options.parseThemeOptions_ = function(options) {
const theme = options['theme'] || Classic;
if (typeof theme == 'string') {
return /** @type {!Blockly.Theme} */ (
Blockly.registry.getObject(Blockly.registry.Type.THEME, theme));
} else if (theme instanceof Blockly.Theme) {
return /** @type {!Blockly.Theme} */ (theme);
return /** @type {!Theme} */ (
registry.getObject(registry.Type.THEME, theme));
} else if (theme instanceof Theme) {
return /** @type {!Theme} */ (theme);
}
return Blockly.Theme.defineTheme(theme.name ||
('builtin' + Blockly.utils.IdGenerator.getNextUniqueId()), theme);
return Theme.defineTheme(
theme.name || ('builtin' + IdGenerator.getNextUniqueId()), theme);
};
/**
@@ -385,13 +385,13 @@ Blockly.Options.parseThemeOptions_ = function(options) {
* @param {?Node|?string} toolboxDef DOM tree of blocks, or text representation
* of same.
* @return {?Node} DOM tree of blocks, or null.
* @deprecated Use Blockly.utils.toolbox.parseToolboxTree. (2020 September 28)
* @deprecated Use toolbox.parseToolboxTree. (2020 September 28)
*/
Blockly.Options.parseToolboxTree = function(toolboxDef) {
Blockly.utils.deprecation.warn(
'Blockly.Options.parseToolboxTree',
'September 2020',
'September 2021',
'Blockly.utils.toolbox.parseToolboxTree');
return Blockly.utils.toolbox.parseToolboxTree(toolboxDef);
Options.parseToolboxTree = function(toolboxDef) {
deprecation.warn(
'Options.parseToolboxTree', 'September 2020', 'September 2021',
'toolbox.parseToolboxTree');
return toolbox.parseToolboxTree(toolboxDef);
};
exports = Options;

View File

@@ -10,171 +10,170 @@
*/
'use strict';
goog.provide('Blockly.uiPosition');
goog.module('Blockly.uiPosition');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Scrollbar');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.toolbox');
goog.requireType('Blockly.MetricsManager');
goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const MetricsManager = goog.requireType('Blockly.MetricsManager');
const Rect = goog.require('Blockly.utils.Rect');
const Scrollbar = goog.require('Blockly.Scrollbar');
/* eslint-disable-next-line no-unused-vars */
const Size = goog.requireType('Blockly.utils.Size');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const toolbox = goog.require('Blockly.utils.toolbox');
/**
* Enum for vertical positioning.
* @enum {number}
* @package
*/
Blockly.uiPosition.verticalPosition = {
const verticalPosition = {
TOP: 0,
BOTTOM: 1
};
/** @package */
exports.verticalPosition = verticalPosition;
/**
* Enum for horizontal positioning.
* @enum {number}
* @package
*/
Blockly.uiPosition.horizontalPosition = {
const horizontalPosition = {
LEFT: 0,
RIGHT: 1
};
/** @package */
exports.horizontalPosition = horizontalPosition;
/**
* An object defining a horizontal and vertical positioning.
* @typedef {{
* horizontal: !Blockly.uiPosition.horizontalPosition,
* vertical: !Blockly.uiPosition.verticalPosition
* horizontal: !horizontalPosition,
* vertical: !verticalPosition
* }}
* @package
*/
Blockly.uiPosition.Position;
let Position;
/** @package */
exports.Position = Position;
/**
* Enum for bump rules to use for dealing with collisions.
* @enum {number}
* @package
*/
Blockly.uiPosition.bumpDirection = {
const bumpDirection = {
UP: 0,
DOWN: 1
};
/** @package */
exports.bumpDirection = bumpDirection;
/**
* Returns a rectangle representing reasonable position for where to place a UI
* element of the specified size given the restraints and locations of the
* scrollbars. This method does not take into account any already placed UI
* elements.
* @param {!Blockly.uiPosition.Position} position The starting
* @param {!Position} position The starting
* horizontal and vertical position.
* @param {!Blockly.utils.Size} size the size of the UI element to get a start
* @param {!Size} size the size of the UI element to get a start
* position for.
* @param {number} horizontalPadding The horizontal padding to use.
* @param {number} verticalPadding The vertical padding to use.
* @param {!Blockly.MetricsManager.UiMetrics} metrics The workspace UI metrics.
* @param {!Blockly.WorkspaceSvg} workspace The workspace.
* @return {!Blockly.utils.Rect} The suggested start position.
* @package
* @param {!MetricsManager.UiMetrics} metrics The workspace UI metrics.
* @param {!WorkspaceSvg} workspace The workspace.
* @return {!Rect} The suggested start position.
*/
Blockly.uiPosition.getStartPositionRect = function(
position, size, horizontalPadding,
verticalPadding, metrics, workspace) {
const getStartPositionRect = function(
position, size, horizontalPadding, verticalPadding, metrics, workspace) {
// Horizontal positioning.
var left = 0;
var hasVerticalScrollbar =
let left = 0;
const hasVerticalScrollbar =
workspace.scrollbar && workspace.scrollbar.canScrollVertically();
if (position.horizontal ===
Blockly.uiPosition.horizontalPosition.LEFT) {
if (position.horizontal === horizontalPosition.LEFT) {
left = metrics.absoluteMetrics.left + horizontalPadding;
if (hasVerticalScrollbar && workspace.RTL) {
left += Blockly.Scrollbar.scrollbarThickness;
left += Scrollbar.scrollbarThickness;
}
} else { // position.horizontal == horizontalPosition.RIGHT
left = metrics.absoluteMetrics.left + metrics.viewMetrics.width -
size.width - horizontalPadding;
if (hasVerticalScrollbar && !workspace.RTL) {
left -= Blockly.Scrollbar.scrollbarThickness;
left -= Scrollbar.scrollbarThickness;
}
}
// Vertical positioning.
var top = 0;
if (position.vertical ===
Blockly.uiPosition.verticalPosition.TOP) {
let top = 0;
if (position.vertical === verticalPosition.TOP) {
top = metrics.absoluteMetrics.top + verticalPadding;
} else { // position.vertical == verticalPosition.BOTTOM
top = metrics.absoluteMetrics.top + metrics.viewMetrics.height -
size.height - verticalPadding;
if (workspace.scrollbar && workspace.scrollbar.canScrollHorizontally()) {
// The scrollbars are always positioned on the bottom if they exist.
top -= Blockly.Scrollbar.scrollbarThickness;
top -= Scrollbar.scrollbarThickness;
}
}
return new Blockly.utils.Rect(
top, top + size.height, left, left + size.width);
return new Rect(top, top + size.height, left, left + size.width);
};
/** @package */
exports.getStartPositionRect = getStartPositionRect;
/**
* Returns a corner position that is on the opposite side of the workspace from
* the toolbox.
* If in horizontal orientation, defaults to the bottom corner. If in vertical
* orientation, defaults to the right corner.
* @param {!Blockly.WorkspaceSvg} workspace The workspace.
* @param {!Blockly.MetricsManager.UiMetrics} metrics The workspace metrics.
* @return {!Blockly.uiPosition.Position} The suggested corner position.
* @package
* @param {!WorkspaceSvg} workspace The workspace.
* @param {!MetricsManager.UiMetrics} metrics The workspace metrics.
* @return {!Position} The suggested corner position.
*/
Blockly.uiPosition.getCornerOppositeToolbox = function(workspace, metrics) {
var leftCorner =
metrics.toolboxMetrics.position !== Blockly.utils.toolbox.Position.LEFT &&
const getCornerOppositeToolbox = function(workspace, metrics) {
const leftCorner =
metrics.toolboxMetrics.position !== toolbox.Position.LEFT &&
(!workspace.horizontalLayout || workspace.RTL);
var topCorner =
metrics.toolboxMetrics.position === Blockly.utils.toolbox.Position.BOTTOM;
var horizontalPosition = leftCorner ?
Blockly.uiPosition.horizontalPosition.LEFT :
Blockly.uiPosition.horizontalPosition.RIGHT;
var verticalPosition = topCorner ?
Blockly.uiPosition.verticalPosition.TOP :
Blockly.uiPosition.verticalPosition.BOTTOM;
return {
horizontal: horizontalPosition,
vertical: verticalPosition
};
const topCorner = metrics.toolboxMetrics.position === toolbox.Position.BOTTOM;
const hPosition =
leftCorner ? horizontalPosition.LEFT : horizontalPosition.RIGHT;
const vPosition = topCorner ? verticalPosition.TOP : verticalPosition.BOTTOM;
return {horizontal: hPosition, vertical: vPosition};
};
/** @package */
exports.getCornerOppositeToolbox = getCornerOppositeToolbox;
/**
* Returns a position Rect based on a starting position that is bumped
* so that it doesn't intersect with any of the provided savedPositions. This
* method does not check that the bumped position is still within bounds.
* @param {!Blockly.utils.Rect} startRect The starting position to use.
* @param {!Rect} startRect The starting position to use.
* @param {number} margin The margin to use between elements when bumping.
* @param {!Blockly.uiPosition.bumpDirection} bumpDirection The direction
* to bump if there is a collision with an existing UI element.
* @param {!Array<!Blockly.utils.Rect>} savedPositions List of rectangles that
* @param {!bumpDirection} bumpDir The direction to bump if there is a collision
* with an existing UI element.
* @param {!Array<!Rect>} savedPositions List of rectangles that
* represent the positions of UI elements already placed.
* @return {!Blockly.utils.Rect} The suggested position rectangle.
* @package
* @return {!Rect} The suggested position rectangle.
*/
Blockly.uiPosition.bumpPositionRect = function(
startRect, margin, bumpDirection, savedPositions) {
var top = startRect.top;
var left = startRect.left;
var width = startRect.right - startRect.left;
var height = startRect.bottom - startRect.top;
const bumpPositionRect = function(startRect, margin, bumpDir, savedPositions) {
let top = startRect.top;
const left = startRect.left;
const width = startRect.right - startRect.left;
const height = startRect.bottom - startRect.top;
// Check for collision and bump if needed.
var boundingRect = startRect;
for (var i = 0, otherEl; (otherEl = savedPositions[i]); i++) {
let boundingRect = startRect;
for (let i = 0; i < savedPositions.length; i++) {
const otherEl = savedPositions[i];
if (boundingRect.intersects(otherEl)) {
if (bumpDirection === Blockly.uiPosition.bumpDirection.UP) {
if (bumpDir === bumpDirection.UP) {
top = otherEl.top - height - margin;
} else { // bumpDirection == bumpDirection.DOWN
} else { // bumpDir == bumpDirection.DOWN
top = otherEl.bottom + margin;
}
// Recheck other savedPositions
boundingRect = new Blockly.utils.Rect(
top, top + height, left, left + width);
boundingRect = new Rect(top, top + height, left, left + width);
i = -1;
}
}
return boundingRect;
};
/** @package */
exports.bumpPositionRect = bumpPositionRect;

View File

@@ -14,38 +14,35 @@
* @name Blockly.Procedures
* @namespace
*/
goog.provide('Blockly.Procedures');
goog.module('Blockly.Procedures');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Blocks');
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');
const Blocks = goog.require('Blockly.Blocks');
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
const Msg = goog.require('Blockly.Msg');
const Names = goog.require('Blockly.Names');
const Variables = goog.require('Blockly.Variables');
const Workspace = goog.require('Blockly.Workspace');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const Xml = goog.require('Blockly.Xml');
const utilsXml = goog.require('Blockly.utils.xml');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Field');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Msg');
goog.require('Blockly.Names');
goog.require('Blockly.utils.xml');
goog.require('Blockly.Workspace');
goog.require('Blockly.Xml');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.Events.Abstract');
goog.requireType('Blockly.WorkspaceSvg');
/**
* Constant to separate procedure names from variables and generated functions
* when running generators.
* @deprecated Use Blockly.internalConstants.PROCEDURE_CATEGORY_NAME
*/
Blockly.Procedures.NAME_TYPE =
Blockly.internalConstants.PROCEDURE_CATEGORY_NAME;
/**
* The default argument for a procedures_mutatorarg block.
* @type {string}
*/
Blockly.Procedures.DEFAULT_ARG = 'x';
const DEFAULT_ARG = 'x';
exports.DEFAULT_ARG = DEFAULT_ARG;
/**
* Procedure block type.
@@ -55,28 +52,32 @@ Blockly.Procedures.DEFAULT_ARG = 'x';
* getProcedureDef: function():!Array
* }}
*/
Blockly.Procedures.ProcedureBlock;
let ProcedureBlock;
exports.ProcedureBlock = ProcedureBlock;
/**
* Find all user-created procedure definitions in a workspace.
* @param {!Blockly.Workspace} root Root workspace.
* @param {!Workspace} root Root workspace.
* @return {!Array<!Array<!Array>>} Pair of arrays, the
* first contains procedures without return variables, the second with.
* Each procedure is defined by a three-element list of name, parameter
* list, and return value boolean.
*/
Blockly.Procedures.allProcedures = function(root) {
var proceduresNoReturn = root.getBlocksByType('procedures_defnoreturn', false)
.map(function(block) {
return /** @type {!Blockly.Procedures.ProcedureBlock} */ (block).getProcedureDef();
const allProcedures = function(root) {
const proceduresNoReturn =
root.getBlocksByType('procedures_defnoreturn', false)
.map(function(block) {
return /** @type {!ProcedureBlock} */ (block).getProcedureDef();
});
const proceduresReturn =
root.getBlocksByType('procedures_defreturn', false).map(function(block) {
return /** @type {!ProcedureBlock} */ (block).getProcedureDef();
});
var proceduresReturn = root.getBlocksByType('procedures_defreturn', false).map(function(block) {
return /** @type {!Blockly.Procedures.ProcedureBlock} */ (block).getProcedureDef();
});
proceduresNoReturn.sort(Blockly.Procedures.procTupleComparator_);
proceduresReturn.sort(Blockly.Procedures.procTupleComparator_);
proceduresNoReturn.sort(procTupleComparator);
proceduresReturn.sort(procTupleComparator);
return [proceduresNoReturn, proceduresReturn];
};
exports.allProcedures = allProcedures;
/**
* Comparison function for case-insensitive sorting of the first element of
@@ -84,9 +85,8 @@ Blockly.Procedures.allProcedures = function(root) {
* @param {!Array} ta First tuple.
* @param {!Array} tb Second tuple.
* @return {number} -1, 0, or 1 to signify greater than, equality, or less than.
* @private
*/
Blockly.Procedures.procTupleComparator_ = function(ta, tb) {
const procTupleComparator = function(ta, tb) {
return ta[0].localeCompare(tb[0], undefined, {sensitivity: 'base'});
};
@@ -95,18 +95,18 @@ Blockly.Procedures.procTupleComparator_ = function(ta, tb) {
* Take the proposed procedure name, and return a legal name i.e. one that
* is not empty and doesn't collide with other procedures.
* @param {string} name Proposed procedure name.
* @param {!Blockly.Block} block Block to disambiguate.
* @param {!Block} block Block to disambiguate.
* @return {string} Non-colliding name.
*/
Blockly.Procedures.findLegalName = function(name, block) {
const findLegalName = function(name, block) {
if (block.isInFlyout) {
// Flyouts can have multiple procedures called 'do something'.
return name;
}
name = name || Blockly.Msg['UNNAMED_KEY'] || 'unnamed';
while (!Blockly.Procedures.isLegalName_(name, block.workspace, block)) {
name = name || Msg['UNNAMED_KEY'] || 'unnamed';
while (!isLegalName(name, block.workspace, block)) {
// Collision with another procedure.
var r = name.match(/^(.*?)(\d+)$/);
const r = name.match(/^(.*?)(\d+)$/);
if (!r) {
name += '2';
} else {
@@ -115,68 +115,68 @@ Blockly.Procedures.findLegalName = function(name, block) {
}
return name;
};
exports.findLegalName = findLegalName;
/**
* Does this procedure have a legal name? Illegal names include names of
* procedures already defined.
* @param {string} name The questionable name.
* @param {!Blockly.Workspace} workspace The workspace to scan for collisions.
* @param {Blockly.Block=} opt_exclude Optional block to exclude from
* @param {!Workspace} workspace The workspace to scan for collisions.
* @param {Block=} opt_exclude Optional block to exclude from
* comparisons (one doesn't want to collide with oneself).
* @return {boolean} True if the name is legal.
* @private
*/
Blockly.Procedures.isLegalName_ = function(name, workspace, opt_exclude) {
return !Blockly.Procedures.isNameUsed(name, workspace, opt_exclude);
const isLegalName = function(name, workspace, opt_exclude) {
return !isNameUsed(name, workspace, opt_exclude);
};
/**
* Return if the given name is already a procedure name.
* @param {string} name The questionable name.
* @param {!Blockly.Workspace} workspace The workspace to scan for collisions.
* @param {Blockly.Block=} opt_exclude Optional block to exclude from
* @param {!Workspace} workspace The workspace to scan for collisions.
* @param {Block=} opt_exclude Optional block to exclude from
* comparisons (one doesn't want to collide with oneself).
* @return {boolean} True if the name is used, otherwise return false.
*/
Blockly.Procedures.isNameUsed = function(name, workspace, opt_exclude) {
var blocks = workspace.getAllBlocks(false);
const isNameUsed = function(name, workspace, opt_exclude) {
const blocks = workspace.getAllBlocks(false);
// Iterate through every block and check the name.
for (var i = 0; i < blocks.length; i++) {
for (let i = 0; i < blocks.length; i++) {
if (blocks[i] == opt_exclude) {
continue;
}
if (blocks[i].getProcedureDef) {
var procedureBlock = /** @type {!Blockly.Procedures.ProcedureBlock} */ (
blocks[i]);
var procName = procedureBlock.getProcedureDef();
if (Blockly.Names.equals(procName[0], name)) {
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
const procName = procedureBlock.getProcedureDef();
if (Names.equals(procName[0], name)) {
return true;
}
}
}
return false;
};
exports.isNameUsed = isNameUsed;
/**
* Rename a procedure. Called by the editable field.
* @param {string} name The proposed new name.
* @return {string} The accepted name.
* @this {Blockly.Field}
* @this {Field}
*/
Blockly.Procedures.rename = function(name) {
const rename = function(name) {
// Strip leading and trailing whitespace. Beyond this, all names are legal.
name = name.trim();
var legalName = Blockly.Procedures.findLegalName(name,
/** @type {!Blockly.Block} */ (this.getSourceBlock()));
var oldName = this.getValue();
const legalName = findLegalName(
name,
/** @type {!Block} */ (this.getSourceBlock()));
const oldName = this.getValue();
if (oldName != name && oldName != legalName) {
// Rename any callers.
var blocks = this.getSourceBlock().workspace.getAllBlocks(false);
for (var i = 0; i < blocks.length; i++) {
const blocks = this.getSourceBlock().workspace.getAllBlocks(false);
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].renameProcedure) {
var procedureBlock = /** @type {!Blockly.Procedures.ProcedureBlock} */ (
blocks[i]);
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
procedureBlock.renameProcedure(
/** @type {string} */ (oldName), legalName);
}
@@ -184,45 +184,46 @@ Blockly.Procedures.rename = function(name) {
}
return legalName;
};
exports.rename = rename;
/**
* Construct the blocks required by the flyout for the procedure category.
* @param {!Blockly.Workspace} workspace The workspace containing procedures.
* @param {!Workspace} workspace The workspace containing procedures.
* @return {!Array<!Element>} Array of XML block elements.
*/
Blockly.Procedures.flyoutCategory = function(workspace) {
var xmlList = [];
if (Blockly.Blocks['procedures_defnoreturn']) {
const flyoutCategory = function(workspace) {
const xmlList = [];
if (Blocks['procedures_defnoreturn']) {
// <block type="procedures_defnoreturn" gap="16">
// <field name="NAME">do something</field>
// </block>
var block = Blockly.utils.xml.createElement('block');
const block = utilsXml.createElement('block');
block.setAttribute('type', 'procedures_defnoreturn');
block.setAttribute('gap', 16);
var nameField = Blockly.utils.xml.createElement('field');
const nameField = utilsXml.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(Blockly.utils.xml.createTextNode(
Blockly.Msg['PROCEDURES_DEFNORETURN_PROCEDURE']));
nameField.appendChild(
utilsXml.createTextNode(Msg['PROCEDURES_DEFNORETURN_PROCEDURE']));
block.appendChild(nameField);
xmlList.push(block);
}
if (Blockly.Blocks['procedures_defreturn']) {
if (Blocks['procedures_defreturn']) {
// <block type="procedures_defreturn" gap="16">
// <field name="NAME">do something</field>
// </block>
var block = Blockly.utils.xml.createElement('block');
const block = utilsXml.createElement('block');
block.setAttribute('type', 'procedures_defreturn');
block.setAttribute('gap', 16);
var nameField = Blockly.utils.xml.createElement('field');
const nameField = utilsXml.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(Blockly.utils.xml.createTextNode(
Blockly.Msg['PROCEDURES_DEFRETURN_PROCEDURE']));
nameField.appendChild(
utilsXml.createTextNode(Msg['PROCEDURES_DEFRETURN_PROCEDURE']));
block.appendChild(nameField);
xmlList.push(block);
}
if (Blockly.Blocks['procedures_ifreturn']) {
if (Blocks['procedures_ifreturn']) {
// <block type="procedures_ifreturn" gap="16"></block>
var block = Blockly.utils.xml.createElement('block');
const block = utilsXml.createElement('block');
block.setAttribute('type', 'procedures_ifreturn');
block.setAttribute('gap', 16);
xmlList.push(block);
@@ -233,22 +234,22 @@ Blockly.Procedures.flyoutCategory = function(workspace) {
}
function populateProcedures(procedureList, templateName) {
for (var i = 0; i < procedureList.length; i++) {
var name = procedureList[i][0];
var args = procedureList[i][1];
for (let i = 0; i < procedureList.length; i++) {
const name = procedureList[i][0];
const args = procedureList[i][1];
// <block type="procedures_callnoreturn" gap="16">
// <mutation name="do something">
// <arg name="x"></arg>
// </mutation>
// </block>
var block = Blockly.utils.xml.createElement('block');
const block = utilsXml.createElement('block');
block.setAttribute('type', templateName);
block.setAttribute('gap', 16);
var mutation = Blockly.utils.xml.createElement('mutation');
const mutation = utilsXml.createElement('mutation');
mutation.setAttribute('name', name);
block.appendChild(mutation);
for (var j = 0; j < args.length; j++) {
var arg = Blockly.utils.xml.createElement('arg');
for (let j = 0; j < args.length; j++) {
const arg = utilsXml.createElement('arg');
arg.setAttribute('name', args[j]);
mutation.appendChild(arg);
}
@@ -256,157 +257,155 @@ Blockly.Procedures.flyoutCategory = function(workspace) {
}
}
var tuple = Blockly.Procedures.allProcedures(workspace);
const tuple = allProcedures(workspace);
populateProcedures(tuple[0], 'procedures_callnoreturn');
populateProcedures(tuple[1], 'procedures_callreturn');
return xmlList;
};
exports.flyoutCategory = flyoutCategory;
/**
* Updates the procedure mutator's flyout so that the arg block is not a
* duplicate of another arg.
* @param {!Blockly.Workspace} workspace The procedure mutator's workspace. This
* @param {!Workspace} workspace The procedure mutator's workspace. This
* workspace's flyout is what is being updated.
* @private
*/
Blockly.Procedures.updateMutatorFlyout_ = function(workspace) {
var usedNames = [];
var blocks = workspace.getBlocksByType('procedures_mutatorarg', false);
for (var i = 0, block; (block = blocks[i]); i++) {
const updateMutatorFlyout = function(workspace) {
const usedNames = [];
const blocks = workspace.getBlocksByType('procedures_mutatorarg', false);
for (let i = 0, block; (block = blocks[i]); i++) {
usedNames.push(block.getFieldValue('NAME'));
}
var xml = Blockly.utils.xml.createElement('xml');
var argBlock = Blockly.utils.xml.createElement('block');
const xmlElement = utilsXml.createElement('xml');
const argBlock = utilsXml.createElement('block');
argBlock.setAttribute('type', 'procedures_mutatorarg');
var nameField = Blockly.utils.xml.createElement('field');
const nameField = utilsXml.createElement('field');
nameField.setAttribute('name', 'NAME');
var argValue = Blockly.Variables.generateUniqueNameFromOptions(
Blockly.Procedures.DEFAULT_ARG, usedNames);
var fieldContent = Blockly.utils.xml.createTextNode(argValue);
const argValue =
Variables.generateUniqueNameFromOptions(DEFAULT_ARG, usedNames);
const fieldContent = utilsXml.createTextNode(argValue);
nameField.appendChild(fieldContent);
argBlock.appendChild(nameField);
xml.appendChild(argBlock);
xmlElement.appendChild(argBlock);
workspace.updateToolbox(xml);
workspace.updateToolbox(xmlElement);
};
/**
* Listens for when a procedure mutator is opened. Then it triggers a flyout
* update and adds a mutator change listener to the mutator workspace.
* @param {!Blockly.Events.Abstract} e The event that triggered this listener.
* @package
* @param {!Abstract} e The event that triggered this listener.
*/
Blockly.Procedures.mutatorOpenListener = function(e) {
if (!(e.type == Blockly.Events.BUBBLE_OPEN && e.bubbleType === 'mutator' &&
e.isOpen)) {
const mutatorOpenListener = function(e) {
if (!(e.type == Events.BUBBLE_OPEN && e.bubbleType === 'mutator' &&
e.isOpen)) {
return;
}
var workspaceId = /** @type {string} */ (e.workspaceId);
var block = Blockly.Workspace.getById(workspaceId)
.getBlockById(e.blockId);
var type = block.type;
const workspaceId = /** @type {string} */ (e.workspaceId);
const block = Workspace.getById(workspaceId).getBlockById(e.blockId);
const type = block.type;
if (type != 'procedures_defnoreturn' && type != 'procedures_defreturn') {
return;
}
var workspace = block.mutator.getWorkspace();
Blockly.Procedures.updateMutatorFlyout_(workspace);
workspace.addChangeListener(Blockly.Procedures.mutatorChangeListener_);
const workspace = block.mutator.getWorkspace();
updateMutatorFlyout(workspace);
workspace.addChangeListener(mutatorChangeListener);
};
/** @package */
exports.mutatorOpenListener = mutatorOpenListener;
/**
* Listens for changes in a procedure mutator and triggers flyout updates when
* necessary.
* @param {!Blockly.Events.Abstract} e The event that triggered this listener.
* @private
* @param {!Abstract} e The event that triggered this listener.
*/
Blockly.Procedures.mutatorChangeListener_ = function(e) {
if (e.type != Blockly.Events.BLOCK_CREATE &&
e.type != Blockly.Events.BLOCK_DELETE &&
e.type != Blockly.Events.BLOCK_CHANGE) {
const mutatorChangeListener = function(e) {
if (e.type != Events.BLOCK_CREATE && e.type != Events.BLOCK_DELETE &&
e.type != Events.BLOCK_CHANGE) {
return;
}
var workspaceId = /** @type {string} */ (e.workspaceId);
var workspace = /** @type {!Blockly.WorkspaceSvg} */
(Blockly.Workspace.getById(workspaceId));
Blockly.Procedures.updateMutatorFlyout_(workspace);
const workspaceId = /** @type {string} */ (e.workspaceId);
const workspace = /** @type {!WorkspaceSvg} */
(Workspace.getById(workspaceId));
updateMutatorFlyout(workspace);
};
/**
* Find all the callers of a named procedure.
* @param {string} name Name of procedure.
* @param {!Blockly.Workspace} workspace The workspace to find callers in.
* @return {!Array<!Blockly.Block>} Array of caller blocks.
* @param {!Workspace} workspace The workspace to find callers in.
* @return {!Array<!Block>} Array of caller blocks.
*/
Blockly.Procedures.getCallers = function(name, workspace) {
var callers = [];
var blocks = workspace.getAllBlocks(false);
const getCallers = function(name, workspace) {
const callers = [];
const blocks = workspace.getAllBlocks(false);
// Iterate through every block and check the name.
for (var i = 0; i < blocks.length; i++) {
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].getProcedureCall) {
var procedureBlock = /** @type {!Blockly.Procedures.ProcedureBlock} */ (
blocks[i]);
var procName = procedureBlock.getProcedureCall();
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
const procName = procedureBlock.getProcedureCall();
// Procedure name may be null if the block is only half-built.
if (procName && Blockly.Names.equals(procName, name)) {
if (procName && Names.equals(procName, name)) {
callers.push(blocks[i]);
}
}
}
return callers;
};
exports.getCallers = getCallers;
/**
* When a procedure definition changes its parameters, find and edit all its
* callers.
* @param {!Blockly.Block} defBlock Procedure definition block.
* @param {!Block} defBlock Procedure definition block.
*/
Blockly.Procedures.mutateCallers = function(defBlock) {
var oldRecordUndo = Blockly.Events.recordUndo;
var procedureBlock = /** @type {!Blockly.Procedures.ProcedureBlock} */ (
defBlock);
var name = procedureBlock.getProcedureDef()[0];
var xmlElement = defBlock.mutationToDom(true);
var callers = Blockly.Procedures.getCallers(name, defBlock.workspace);
for (var i = 0, caller; (caller = callers[i]); i++) {
var oldMutationDom = caller.mutationToDom();
var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
const mutateCallers = function(defBlock) {
const oldRecordUndo = Events.recordUndo;
const procedureBlock = /** @type {!ProcedureBlock} */ (defBlock);
const name = procedureBlock.getProcedureDef()[0];
const xmlElement = defBlock.mutationToDom(true);
const callers = getCallers(name, defBlock.workspace);
for (let i = 0, caller; (caller = callers[i]); i++) {
const oldMutationDom = caller.mutationToDom();
const oldMutation = oldMutationDom && Xml.domToText(oldMutationDom);
caller.domToMutation(xmlElement);
var newMutationDom = caller.mutationToDom();
var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom);
const newMutationDom = caller.mutationToDom();
const newMutation = newMutationDom && Xml.domToText(newMutationDom);
if (oldMutation != newMutation) {
// Fire a mutation on every caller block. But don't record this as an
// undo action since it is deterministically tied to the procedure's
// definition mutation.
Blockly.Events.recordUndo = false;
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))(
Events.recordUndo = false;
Events.fire(new (Events.get(Events.BLOCK_CHANGE))(
caller, 'mutation', null, oldMutation, newMutation));
Blockly.Events.recordUndo = oldRecordUndo;
Events.recordUndo = oldRecordUndo;
}
}
};
exports.mutateCallers = mutateCallers;
/**
* Find the definition block for the named procedure.
* @param {string} name Name of procedure.
* @param {!Blockly.Workspace} workspace The workspace to search.
* @return {?Blockly.Block} The procedure definition block, or null not found.
* @param {!Workspace} workspace The workspace to search.
* @return {?Block} The procedure definition block, or null not found.
*/
Blockly.Procedures.getDefinition = function(name, workspace) {
const getDefinition = function(name, workspace) {
// Do not assume procedure is a top block. Some languages allow nested
// procedures. Also do not assume it is one of the built-in blocks. Only
// rely on getProcedureDef.
var blocks = workspace.getAllBlocks(false);
for (var i = 0; i < blocks.length; i++) {
const blocks = workspace.getAllBlocks(false);
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].getProcedureDef) {
var procedureBlock = /** @type {!Blockly.Procedures.ProcedureBlock} */ (
blocks[i]);
var tuple = procedureBlock.getProcedureDef();
if (tuple && Blockly.Names.equals(tuple[0], name)) {
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
const tuple = procedureBlock.getProcedureDef();
if (tuple && Names.equals(tuple[0], name)) {
return blocks[i]; // Can't use procedureBlock var due to type check.
}
}
}
return null;
};
exports.getDefinition = getDefinition;

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,24 +10,30 @@
*/
'use strict';
/**
* The top level namespace for block rendering.
* @namespace Blockly.blockRendering
*/
goog.provide('Blockly.blockRendering');
goog.module('Blockly.blockRendering');
goog.module.declareLegacyNamespace();
goog.require('Blockly.registry');
goog.requireType('Blockly.blockRendering.Renderer');
goog.requireType('Blockly.Theme');
/* 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');
const registry = goog.require('Blockly.registry');
/**
* Whether or not the debugger is turned on.
* @type {boolean}
* @package
*/
Blockly.blockRendering.useDebugger = false;
let useDebugger = false;
/**
* Returns whether the debugger is turned on.
* @return {boolean} Whether the debugger is turned on.
*/
const isDebuggerEnabled = function() {
return useDebugger;
};
/** @package */
exports.isDebuggerEnabled = isDebuggerEnabled;
/**
* Registers a new renderer.
@@ -36,48 +42,54 @@ Blockly.blockRendering.useDebugger = false;
* to register.
* @throws {Error} if a renderer with the same name has already been registered.
*/
Blockly.blockRendering.register = function(name, rendererClass) {
Blockly.registry.register(Blockly.registry.Type.RENDERER, name,
rendererClass);
const register = function(name, rendererClass) {
registry.register(registry.Type.RENDERER, name, rendererClass);
};
exports.register = register;
/**
* Unregisters the renderer registered with the given name.
* @param {string} name The name of the renderer.
*/
Blockly.blockRendering.unregister = function(name) {
Blockly.registry.unregister(Blockly.registry.Type.RENDERER, name);
const unregister = function(name) {
registry.unregister(registry.Type.RENDERER, name);
};
exports.unregister = unregister;
/**
* Turn on the blocks debugger.
* @package
*/
Blockly.blockRendering.startDebugger = function() {
Blockly.blockRendering.useDebugger = true;
const startDebugger = function() {
useDebugger = true;
};
/** @package */
exports.startDebugger = startDebugger;
/**
* Turn off the blocks debugger.
* @package
*/
Blockly.blockRendering.stopDebugger = function() {
Blockly.blockRendering.useDebugger = false;
const stopDebugger = function() {
useDebugger = false;
};
/** @package */
exports.stopDebugger = stopDebugger;
/**
* Initialize anything needed for rendering (constants, etc).
* @param {!string} name Name of the renderer to initialize.
* @param {!Blockly.Theme} theme The workspace theme object.
* @param {!Theme} theme The workspace theme object.
* @param {Object=} opt_rendererOverrides Rendering constant overrides.
* @return {!Blockly.blockRendering.Renderer} The new instance of a renderer.
* @return {!Renderer} The new instance of a renderer.
* Already initialized.
* @package
*/
Blockly.blockRendering.init = function(name, theme, opt_rendererOverrides) {
var rendererClass = Blockly.registry.getClass(
Blockly.registry.Type.RENDERER, name);
var renderer = new rendererClass(name);
const init = function(name, theme, opt_rendererOverrides) {
const rendererClass = registry.getClass(registry.Type.RENDERER, name);
const renderer = new rendererClass(name);
renderer.init(theme, opt_rendererOverrides);
return renderer;
};
/** @package */
exports.init = init;

View File

@@ -26,7 +26,7 @@ const InlineInput = goog.requireType('Blockly.blockRendering.InlineInput');
/* eslint-disable-next-line no-unused-vars */
const RenderInfo = goog.requireType('Blockly.blockRendering.RenderInfo');
/* eslint-disable-next-line no-unused-vars */
const Row = goog.require('Blockly.blockRendering.Row');
const Row = goog.requireType('Blockly.blockRendering.Row');
const Types = goog.require('Blockly.blockRendering.Types');
const svgPaths = goog.require('Blockly.utils.svgPaths');
@@ -73,7 +73,7 @@ Drawer.prototype.draw = function() {
if (this.info_.RTL) {
this.block_.pathObject.flipRTL();
}
if (Blockly.blockRendering.useDebugger) {
if (Blockly.blockRendering.isDebuggerEnabled()) {
this.block_.renderingDebugger.drawDebug(this.block_, this.info_);
}
this.recordSizeOnBlock_();

Some files were not shown because too many files have changed in this diff Show More