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

This commit is contained in:
Maribeth Bottorff
2021-07-27 14:04:46 -07:00
124 changed files with 6471 additions and 5630 deletions

View File

@@ -2,29 +2,6 @@
"rules": {
"curly": ["error"],
"eol-last": ["error"],
// Blockly/Google use 2-space indents.
// Blockly/Google uses +4 space indents for line continuations.
// Ignore default rules for ternary expressions.
"indent": [
"error", 2,
{
"SwitchCase": 1,
"MemberExpression": 2,
"ObjectExpression": 1,
"FunctionDeclaration": {
"body": 1,
"parameters": 2
},
"FunctionExpression": {
"body": 1,
"parameters": 2
},
"CallExpression": {
"arguments": 2
},
"ignoredNodes": ["ConditionalExpression"]
}
],
"keyword-spacing": ["error"],
"linebreak-style": ["error", "unix"],
"max-len": [
@@ -39,7 +16,7 @@
],
"no-trailing-spaces": ["error", { "skipBlankLines": true }],
"no-unused-vars": [
"error",
"warn",
{
"args": "after-used",
// Ignore vars starting with an underscore.
@@ -48,7 +25,6 @@
"argsIgnorePattern": "^_"
}
],
"no-use-before-define": ["error"],
// Blockly uses for exporting symbols. no-self-assign added in eslint 5.
"no-self-assign": ["off"],
// Blockly uses single quotes except for JSON blobs, which must use double quotes.

View File

@@ -41,3 +41,19 @@ jobs:
env:
CI: true
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 16.x
uses: actions/setup-node@v1
with:
node-version: 16.x
- name: Npm Install
run: npm install
- name: Lint
run: npm run lint

View File

@@ -0,0 +1,35 @@
# For new pull requests against the goog_module branch, adds the 'type: cleanup'
# label and sets the milestone to q3 2021 release.
name: Tag module cleanup
# Trigger on pull requests against goog_module branch only
# Uses pull_request_target to get write permissions so that it can write labels.
on:
pull_request_target:
branches:
- goog_module
jobs:
tag-module-cleanup:
# Add the type: cleanup label
runs-on: ubuntu-latest
steps:
- 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,
issue_number: context.issue.number,
// Sets the labels
labels: ['type: cleanup']
}
await github.issues.update(issuesUpdateParams)

40
.github/workflows/update_metadata.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
# This workflow updates the check_metadata.sh script, which compares the current
# size of build artifacts against their size in the previous version of Blockly.
name: Update Metadata
on: [workflow_dispatch]
jobs:
update-metadata:
runs-on: ubuntu-latest
steps:
- name: Check Out Blockly
uses: actions/checkout@v2
with:
ref: 'develop'
- name: Use Node.js 16.x
uses: actions/setup-node@v1
with:
node-version: 16.x
- name: Build Blockly
run: npm run build:compressed
- name: Build Blockly blocks
run: npm run build:blocks
- name: Update Metadata
run: source ./tests/scripts/update_metadata.sh
- name: Create Pull Request
uses: peter-evans/create-pull-request@9825ae65b1cb54b543b938503728b432a0176d29
with:
commit-message: Update build artifact sizes in check_metadata.sh
delete-branch: true
title: Update build artifact sizes in check_metadata.sh
- name: View Pull Request
run: echo "View Pull Request - ${{ steps.cpr.outputs.pull-request-url }}"

View File

@@ -10,50 +10,49 @@
*/
'use strict';
goog.provide('Blockly.blockAnimations');
goog.module('Blockly.blockAnimations');
goog.module.declareLegacyNamespace();
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Svg');
goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Svg = goog.require('Blockly.utils.Svg');
const dom = goog.require('Blockly.utils.dom');
/**
* PID of disconnect UI animation. There can only be one at a time.
* @type {number}
* @private
*/
Blockly.blockAnimations.disconnectPid_ = 0;
let disconnectPid = 0;
/**
* SVG group of wobbling block. There can only be one at a time.
* @type {Element}
* @private
*/
Blockly.blockAnimations.disconnectGroup_ = null;
let disconnectGroup = null;
/**
* Play some UI effects (sound, animation) when disposing of a block.
* @param {!Blockly.BlockSvg} block The block being disposed of.
* @package
* @param {!BlockSvg} block The block being disposed of.
*/
Blockly.blockAnimations.disposeUiEffect = function(block) {
var workspace = block.workspace;
var svgGroup = block.getSvgRoot();
const disposeUiEffect = function(block) {
const workspace = block.workspace;
const svgGroup = block.getSvgRoot();
workspace.getAudioManager().play('delete');
var xy = workspace.getSvgXY(svgGroup);
const xy = workspace.getSvgXY(svgGroup);
// Deeply clone the current block.
var clone = svgGroup.cloneNode(true);
const clone = svgGroup.cloneNode(true);
clone.translateX_ = xy.x;
clone.translateY_ = xy.y;
clone.setAttribute('transform', 'translate(' + xy.x + ',' + xy.y + ')');
workspace.getParentSvg().appendChild(clone);
clone.bBox_ = clone.getBBox();
// Start the animation.
Blockly.blockAnimations.disposeUiStep_(clone, workspace.RTL, new Date,
workspace.scale);
disposeUiStep(clone, workspace.RTL, new Date, workspace.scale);
};
/** @package */
exports.disposeUiEffect = disposeUiEffect;
/**
* Animate a cloned block and eventually dispose of it.
@@ -63,40 +62,38 @@ Blockly.blockAnimations.disposeUiEffect = function(block) {
* @param {boolean} rtl True if RTL, false if LTR.
* @param {!Date} start Date of animation's start.
* @param {number} workspaceScale Scale of workspace.
* @private
*/
Blockly.blockAnimations.disposeUiStep_ = function(clone, rtl, start,
workspaceScale) {
var ms = new Date - start;
var percent = ms / 150;
const disposeUiStep = function(clone, rtl, start, workspaceScale) {
const ms = new Date - start;
const percent = ms / 150;
if (percent > 1) {
Blockly.utils.dom.removeNode(clone);
dom.removeNode(clone);
} else {
var x = clone.translateX_ +
const x = clone.translateX_ +
(rtl ? -1 : 1) * clone.bBox_.width * workspaceScale / 2 * percent;
var y = clone.translateY_ + clone.bBox_.height * workspaceScale * percent;
var scale = (1 - percent) * workspaceScale;
clone.setAttribute('transform', 'translate(' + x + ',' + y + ')' +
' scale(' + scale + ')');
setTimeout(Blockly.blockAnimations.disposeUiStep_, 10, clone, rtl, start,
workspaceScale);
const y = clone.translateY_ + clone.bBox_.height * workspaceScale * percent;
const scale = (1 - percent) * workspaceScale;
clone.setAttribute(
'transform',
'translate(' + x + ',' + y + ')' +
' scale(' + scale + ')');
setTimeout(disposeUiStep, 10, clone, rtl, start, workspaceScale);
}
};
/**
* Play some UI effects (sound, ripple) after a connection has been established.
* @param {!Blockly.BlockSvg} block The block being connected.
* @package
* @param {!BlockSvg} block The block being connected.
*/
Blockly.blockAnimations.connectionUiEffect = function(block) {
var workspace = block.workspace;
var scale = workspace.scale;
const connectionUiEffect = function(block) {
const workspace = block.workspace;
const scale = workspace.scale;
workspace.getAudioManager().play('click');
if (scale < 1) {
return; // Too small to care about visual effects.
}
// Determine the absolute coordinates of the inferior block.
var xy = workspace.getSvgXY(block.getSvgRoot());
const xy = workspace.getSvgXY(block.getSvgRoot());
// Offset the coordinates based on the two connection types, fix scale.
if (block.outputConnection) {
xy.x += (block.RTL ? 3 : -3) * scale;
@@ -105,9 +102,8 @@ Blockly.blockAnimations.connectionUiEffect = function(block) {
xy.x += (block.RTL ? -23 : 23) * scale;
xy.y += 3 * scale;
}
var ripple = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.CIRCLE,
{
const ripple = dom.createSvgElement(
Svg.CIRCLE, {
'cx': xy.x,
'cy': xy.y,
'r': 0,
@@ -117,89 +113,88 @@ Blockly.blockAnimations.connectionUiEffect = function(block) {
},
workspace.getParentSvg());
// Start the animation.
Blockly.blockAnimations.connectionUiStep_(ripple, new Date, scale);
connectionUiStep(ripple, new Date, scale);
};
/** @package */
exports.connectionUiEffect = connectionUiEffect;
/**
* Expand a ripple around a connection.
* @param {!SVGElement} ripple Element to animate.
* @param {!Date} start Date of animation's start.
* @param {number} scale Scale of workspace.
* @private
*/
Blockly.blockAnimations.connectionUiStep_ = function(ripple, start, scale) {
var ms = new Date - start;
var percent = ms / 150;
const connectionUiStep = function(ripple, start, scale) {
const ms = new Date - start;
const percent = ms / 150;
if (percent > 1) {
Blockly.utils.dom.removeNode(ripple);
dom.removeNode(ripple);
} else {
ripple.setAttribute('r', percent * 25 * scale);
ripple.style.opacity = 1 - percent;
Blockly.blockAnimations.disconnectPid_ = setTimeout(
Blockly.blockAnimations.connectionUiStep_, 10, ripple, start, scale);
disconnectPid = setTimeout(connectionUiStep, 10, ripple, start, scale);
}
};
/**
* Play some UI effects (sound, animation) when disconnecting a block.
* @param {!Blockly.BlockSvg} block The block being disconnected.
* @package
* @param {!BlockSvg} block The block being disconnected.
*/
Blockly.blockAnimations.disconnectUiEffect = function(block) {
const disconnectUiEffect = function(block) {
block.workspace.getAudioManager().play('disconnect');
if (block.workspace.scale < 1) {
return; // Too small to care about visual effects.
}
// Horizontal distance for bottom of block to wiggle.
var DISPLACEMENT = 10;
const DISPLACEMENT = 10;
// Scale magnitude of skew to height of block.
var height = block.getHeightWidth().height;
var magnitude = Math.atan(DISPLACEMENT / height) / Math.PI * 180;
const height = block.getHeightWidth().height;
let magnitude = Math.atan(DISPLACEMENT / height) / Math.PI * 180;
if (!block.RTL) {
magnitude *= -1;
}
// Start the animation.
Blockly.blockAnimations.disconnectUiStep_(
block.getSvgRoot(), magnitude, new Date);
disconnectUiStep(block.getSvgRoot(), magnitude, new Date);
};
/** @package */
exports.disconnectUiEffect = disconnectUiEffect;
/**
* Animate a brief wiggle of a disconnected block.
* @param {!SVGElement} group SVG element to animate.
* @param {number} magnitude Maximum degrees skew (reversed for RTL).
* @param {!Date} start Date of animation's start.
* @private
*/
Blockly.blockAnimations.disconnectUiStep_ = function(group, magnitude, start) {
var DURATION = 200; // Milliseconds.
var WIGGLES = 3; // Half oscillations.
const disconnectUiStep = function(group, magnitude, start) {
const DURATION = 200; // Milliseconds.
const WIGGLES = 3; // Half oscillations.
var ms = new Date - start;
var percent = ms / DURATION;
const ms = new Date - start;
const percent = ms / DURATION;
if (percent > 1) {
group.skew_ = '';
} else {
var skew = Math.round(
const skew = Math.round(
Math.sin(percent * Math.PI * WIGGLES) * (1 - percent) * magnitude);
group.skew_ = 'skewX(' + skew + ')';
Blockly.blockAnimations.disconnectGroup_ = group;
Blockly.blockAnimations.disconnectPid_ =
setTimeout(Blockly.blockAnimations.disconnectUiStep_, 10, group,
magnitude, start);
disconnectGroup = group;
disconnectPid = setTimeout(disconnectUiStep, 10, group, magnitude, start);
}
group.setAttribute('transform', group.translate_ + group.skew_);
};
/**
* Stop the disconnect UI animation immediately.
* @package
*/
Blockly.blockAnimations.disconnectUiStop = function() {
if (Blockly.blockAnimations.disconnectGroup_) {
clearTimeout(Blockly.blockAnimations.disconnectPid_);
var group = Blockly.blockAnimations.disconnectGroup_;
const disconnectUiStop = function() {
if (disconnectGroup) {
clearTimeout(disconnectPid);
const group = disconnectGroup;
group.skew_ = '';
group.setAttribute('transform', group.translate_);
Blockly.blockAnimations.disconnectGroup_ = null;
disconnectGroup = null;
}
};
/** @package */
exports.disconnectUiStop = disconnectUiStop;

View File

@@ -15,11 +15,13 @@
'use strict';
goog.provide('Blockly.BlockDragSurfaceSvg');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Svg');
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');
/**
@@ -28,7 +30,7 @@ goog.require('Blockly.utils.Svg');
* @param {!Element} container Containing element.
* @constructor
*/
Blockly.BlockDragSurfaceSvg = function(container) {
const BlockDragSurfaceSvg = function(container) {
/**
* @type {!Element}
* @private
@@ -38,11 +40,11 @@ Blockly.BlockDragSurfaceSvg = function(container) {
};
/**
* The SVG drag surface. Set once by Blockly.BlockDragSurfaceSvg.createDom.
* The SVG drag surface. Set once by BlockDragSurfaceSvg.createDom.
* @type {?SVGElement}
* @private
*/
Blockly.BlockDragSurfaceSvg.prototype.SVG_ = null;
BlockDragSurfaceSvg.prototype.SVG_ = null;
/**
* This is where blocks live while they are being dragged if the drag surface
@@ -50,14 +52,14 @@ Blockly.BlockDragSurfaceSvg.prototype.SVG_ = null;
* @type {?SVGElement}
* @private
*/
Blockly.BlockDragSurfaceSvg.prototype.dragGroup_ = null;
BlockDragSurfaceSvg.prototype.dragGroup_ = null;
/**
* Containing HTML element; parent of the workspace and the drag surface.
* @type {?Element}
* @private
*/
Blockly.BlockDragSurfaceSvg.prototype.container_ = null;
BlockDragSurfaceSvg.prototype.container_ = null;
/**
* Cached value for the scale of the drag surface.
@@ -65,45 +67,43 @@ Blockly.BlockDragSurfaceSvg.prototype.container_ = null;
* @type {number}
* @private
*/
Blockly.BlockDragSurfaceSvg.prototype.scale_ = 1;
BlockDragSurfaceSvg.prototype.scale_ = 1;
/**
* Cached value for the translation of the drag surface.
* This translation is in pixel units, because the scale is applied to the
* drag group rather than the top-level SVG.
* @type {?Blockly.utils.Coordinate}
* @type {?Coordinate}
* @private
*/
Blockly.BlockDragSurfaceSvg.prototype.surfaceXY_ = null;
BlockDragSurfaceSvg.prototype.surfaceXY_ = null;
/**
* Cached value for the translation of the child drag surface in pixel units.
* Since the child drag surface tracks the translation of the workspace this is
* ultimately the translation of the workspace.
* @type {!Blockly.utils.Coordinate}
* @type {!Coordinate}
* @private
*/
Blockly.BlockDragSurfaceSvg.prototype.childSurfaceXY_ =
new Blockly.utils.Coordinate(0, 0);
BlockDragSurfaceSvg.prototype.childSurfaceXY_ = new Coordinate(0, 0);
/**
* Create the drag surface and inject it into the container.
*/
Blockly.BlockDragSurfaceSvg.prototype.createDom = function() {
BlockDragSurfaceSvg.prototype.createDom = function() {
if (this.SVG_) {
return; // Already created.
}
this.SVG_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.SVG, {
'xmlns': Blockly.utils.dom.SVG_NS,
'xmlns:html': Blockly.utils.dom.HTML_NS,
'xmlns:xlink': Blockly.utils.dom.XLINK_NS,
this.SVG_ = createSvgElement(
SVG, {
'xmlns': SVG_NS,
'xmlns:html': HTML_NS,
'xmlns:xlink': XLINK_NS,
'version': '1.1',
'class': 'blocklyBlockDragSurface'
},
this.container_);
this.dragGroup_ =
Blockly.utils.dom.createSvgElement(Blockly.utils.Svg.G, {}, this.SVG_);
this.dragGroup_ = createSvgElement(G, {}, this.SVG_);
};
/**
@@ -112,14 +112,14 @@ Blockly.BlockDragSurfaceSvg.prototype.createDom = function() {
* @param {!SVGElement} blocks Block or group of blocks to place on the drag
* surface.
*/
Blockly.BlockDragSurfaceSvg.prototype.setBlocksAndShow = function(blocks) {
BlockDragSurfaceSvg.prototype.setBlocksAndShow = function(blocks) {
if (this.dragGroup_.childNodes.length) {
throw Error('Already dragging a block.');
}
// appendChild removes the blocks from the previous parent
this.dragGroup_.appendChild(blocks);
this.SVG_.style.display = 'block';
this.surfaceXY_ = new Blockly.utils.Coordinate(0, 0);
this.surfaceXY_ = new Coordinate(0, 0);
};
/**
@@ -129,13 +129,12 @@ Blockly.BlockDragSurfaceSvg.prototype.setBlocksAndShow = function(blocks) {
* @param {number} y Y translation in pixel coordinates.
* @param {number} scale Scale of the group.
*/
Blockly.BlockDragSurfaceSvg.prototype.translateAndScaleGroup = function(
x, y, scale) {
BlockDragSurfaceSvg.prototype.translateAndScaleGroup = function(x, y, scale) {
this.scale_ = scale;
// This is a work-around to prevent a the blocks from rendering
// fuzzy while they are being dragged on the drag surface.
var fixedX = x.toFixed(0);
var fixedY = y.toFixed(0);
const fixedX = x.toFixed(0);
const fixedY = y.toFixed(0);
this.childSurfaceXY_.x = parseInt(fixedX, 10);
this.childSurfaceXY_.y = parseInt(fixedY, 10);
@@ -149,17 +148,16 @@ Blockly.BlockDragSurfaceSvg.prototype.translateAndScaleGroup = function(
* Translate the drag surface's SVG based on its internal state.
* @private
*/
Blockly.BlockDragSurfaceSvg.prototype.translateSurfaceInternal_ = function() {
var x = this.surfaceXY_.x;
var y = this.surfaceXY_.y;
BlockDragSurfaceSvg.prototype.translateSurfaceInternal_ = function() {
let x = this.surfaceXY_.x;
let y = this.surfaceXY_.y;
// This is a work-around to prevent a the blocks from rendering
// fuzzy while they are being dragged on the drag surface.
x = x.toFixed(0);
y = y.toFixed(0);
this.SVG_.style.display = 'block';
Blockly.utils.dom.setCssTransform(
this.SVG_, 'translate3d(' + x + 'px, ' + y + 'px, 0)');
setCssTransform(this.SVG_, 'translate3d(' + x + 'px, ' + y + 'px, 0)');
};
/**
@@ -167,10 +165,10 @@ Blockly.BlockDragSurfaceSvg.prototype.translateSurfaceInternal_ = function() {
* @param {number} deltaX Horizontal offset in pixel units.
* @param {number} deltaY Vertical offset in pixel units.
*/
Blockly.BlockDragSurfaceSvg.prototype.translateBy = function(deltaX, deltaY) {
var x = this.surfaceXY_.x + deltaX;
var y = this.surfaceXY_.y + deltaY;
this.surfaceXY_ = new Blockly.utils.Coordinate(x, y);
BlockDragSurfaceSvg.prototype.translateBy = function(deltaX, deltaY) {
const x = this.surfaceXY_.x + deltaX;
const y = this.surfaceXY_.y + deltaY;
this.surfaceXY_ = new Coordinate(x, y);
this.translateSurfaceInternal_();
};
@@ -182,20 +180,19 @@ Blockly.BlockDragSurfaceSvg.prototype.translateBy = function(deltaX, deltaY) {
* @param {number} x X translation for the entire surface.
* @param {number} y Y translation for the entire surface.
*/
Blockly.BlockDragSurfaceSvg.prototype.translateSurface = function(x, y) {
this.surfaceXY_ =
new Blockly.utils.Coordinate(x * this.scale_, y * this.scale_);
BlockDragSurfaceSvg.prototype.translateSurface = function(x, y) {
this.surfaceXY_ = new Coordinate(x * this.scale_, y * this.scale_);
this.translateSurfaceInternal_();
};
/**
* Reports the surface translation in scaled workspace coordinates.
* Use this when finishing a drag to return blocks to the correct position.
* @return {!Blockly.utils.Coordinate} Current translation of the surface.
* @return {!Coordinate} Current translation of the surface.
*/
Blockly.BlockDragSurfaceSvg.prototype.getSurfaceTranslation = function() {
var xy = Blockly.utils.getRelativeXY(/** @type {!SVGElement} */ (this.SVG_));
return new Blockly.utils.Coordinate(xy.x / this.scale_, xy.y / this.scale_);
BlockDragSurfaceSvg.prototype.getSurfaceTranslation = function() {
const xy = getRelativeXY(/** @type {!SVGElement} */ (this.SVG_));
return new Coordinate(xy.x / this.scale_, xy.y / this.scale_);
};
/**
@@ -203,7 +200,7 @@ Blockly.BlockDragSurfaceSvg.prototype.getSurfaceTranslation = function() {
* BlockSvg.getRelativeToSurfaceXY).
* @return {?SVGElement} Drag surface group element.
*/
Blockly.BlockDragSurfaceSvg.prototype.getGroup = function() {
BlockDragSurfaceSvg.prototype.getGroup = function() {
return this.dragGroup_;
};
@@ -211,16 +208,17 @@ Blockly.BlockDragSurfaceSvg.prototype.getGroup = function() {
* Returns the SVG drag surface.
* @returns {?SVGElement} The SVG drag surface.
*/
Blockly.BlockDragSurfaceSvg.prototype.getSvgRoot = function() {
BlockDragSurfaceSvg.prototype.getSvgRoot = function() {
return this.SVG_;
};
/**
* Get the current blocks on the drag surface, if any (primarily
* for BlockSvg.getRelativeToSurfaceXY).
* @return {?Element} Drag surface block DOM element, or null if no blocks exist.
* @return {?Element} Drag surface block DOM element, or null if no blocks
* exist.
*/
Blockly.BlockDragSurfaceSvg.prototype.getCurrentBlock = function() {
BlockDragSurfaceSvg.prototype.getCurrentBlock = function() {
return /** @type {Element} */ (this.dragGroup_.firstChild);
};
@@ -228,9 +226,9 @@ Blockly.BlockDragSurfaceSvg.prototype.getCurrentBlock = function() {
* Gets the translation of the child block surface
* This surface is in charge of keeping track of how much the workspace has
* moved.
* @return {!Blockly.utils.Coordinate} The amount the workspace has been moved.
* @return {!Coordinate} The amount the workspace has been moved.
*/
Blockly.BlockDragSurfaceSvg.prototype.getWsTranslation = function() {
BlockDragSurfaceSvg.prototype.getWsTranslation = function() {
// Returning a copy so the coordinate can not be changed outside this class.
return this.childSurfaceXY_.clone();
};
@@ -244,7 +242,7 @@ Blockly.BlockDragSurfaceSvg.prototype.getWsTranslation = function() {
* to, or null if the blocks should be removed from this surface without
* being moved to a different surface.
*/
Blockly.BlockDragSurfaceSvg.prototype.clearAndHide = function(opt_newSurface) {
BlockDragSurfaceSvg.prototype.clearAndHide = function(opt_newSurface) {
if (opt_newSurface) {
// appendChild removes the node from this.dragGroup_
opt_newSurface.appendChild(this.getCurrentBlock());
@@ -257,3 +255,5 @@ Blockly.BlockDragSurfaceSvg.prototype.clearAndHide = function(opt_newSurface) {
}
this.surfaceXY_ = null;
};
exports = BlockDragSurfaceSvg;

View File

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

View File

@@ -18,7 +18,6 @@ goog.require('Blockly.blockAnimations');
goog.require('Blockly.blockRendering.IPathObject');
goog.require('Blockly.browserEvents');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.ContextMenu');
goog.require('Blockly.ContextMenuRegistry');
@@ -31,6 +30,7 @@ goog.require('Blockly.IASTNodeLocationSvg');
goog.require('Blockly.IBoundedElement');
goog.require('Blockly.ICopyable');
goog.require('Blockly.IDraggable');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Msg');
goog.require('Blockly.RenderedConnection');
goog.require('Blockly.TabNavigateCursor');
@@ -688,7 +688,7 @@ Blockly.BlockSvg.prototype.updateCollapsed_ = function() {
icon.setVisible(false);
}
var text = this.toString(Blockly.COLLAPSE_CHARS);
var text = this.toString(Blockly.internalConstants.COLLAPSE_CHARS);
var field = this.getField(collapsedFieldName);
if (field) {
field.setValue(text);
@@ -1550,7 +1550,8 @@ Blockly.BlockSvg.prototype.bumpNeighbours = function() {
connection.targetBlock().bumpNeighbours();
}
var neighbours = connection.neighbours(Blockly.SNAP_RADIUS);
var neighbours =
connection.neighbours(Blockly.internalConstants.SNAP_RADIUS);
for (var j = 0, otherConnection; (otherConnection = neighbours[j]); j++) {
// If both connections are connected, that's probably fine. But if
@@ -1585,13 +1586,13 @@ Blockly.BlockSvg.prototype.scheduleSnapAndBump = function() {
Blockly.Events.setGroup(group);
block.snapToGrid();
Blockly.Events.setGroup(false);
}, Blockly.BUMP_DELAY / 2);
}, Blockly.internalConstants.BUMP_DELAY / 2);
setTimeout(function() {
Blockly.Events.setGroup(group);
block.bumpNeighbours();
Blockly.Events.setGroup(false);
}, Blockly.BUMP_DELAY);
}, Blockly.internalConstants.BUMP_DELAY);
};
/**

View File

@@ -35,6 +35,7 @@ goog.require('Blockly.Events.VarCreate');
/** @suppress {extraRequire} */
goog.require('Blockly.inject');
goog.require('Blockly.inputTypes');
goog.require('Blockly.internalConstants');
/** @suppress {extraRequire} */
goog.require('Blockly.Procedures');
goog.require('Blockly.ShortcutRegistry');
@@ -424,8 +425,9 @@ Blockly.isNumber = function(str) {
* @return {string} RGB code, e.g. '#5ba65b'.
*/
Blockly.hueToHex = function(hue) {
return Blockly.utils.colour.hsvToHex(hue, Blockly.HSV_SATURATION,
Blockly.HSV_VALUE * 255);
return Blockly.utils.colour.hsvToHex(
hue, Blockly.internalConstants.HSV_SATURATION,
Blockly.internalConstants.HSV_VALUE * 255);
};
/**
@@ -596,3 +598,38 @@ Blockly.TOOLBOX_AT_LEFT = Blockly.utils.toolbox.Position.LEFT;
* @see Blockly.utils.toolbox.Position.RIGHT
*/
Blockly.TOOLBOX_AT_RIGHT = Blockly.utils.toolbox.Position.RIGHT;
// Aliases to allow external code to access these values for legacy reasons.
Blockly.LINE_MODE_MULTIPLIER = Blockly.internalConstants.LINE_MODE_MULTIPLIER;
Blockly.PAGE_MODE_MULTIPLIER = Blockly.internalConstants.PAGE_MODE_MULTIPLIER;
Blockly.DRAG_RADIUS = Blockly.internalConstants.DRAG_RADIUS;
Blockly.FLYOUT_DRAG_RADIUS = Blockly.internalConstants.FLYOUT_DRAG_RADIUS;
Blockly.SNAP_RADIUS = Blockly.internalConstants.SNAP_RADIUS;
Blockly.CONNECTING_SNAP_RADIUS =
Blockly.internalConstants.CONNECTING_SNAP_RADIUS;
Blockly.CURRENT_CONNECTION_PREFERENCE =
Blockly.internalConstants.CURRENT_CONNECTION_PREFERENCE;
Blockly.BUMP_DELAY = Blockly.internalConstants.BUMP_DELAY;
Blockly.BUMP_RANDOMNESS = Blockly.internalConstants.BUMP_RANDOMNESS;
Blockly.COLLAPSE_CHARS = Blockly.internalConstants.COLLAPSE_CHARS;
Blockly.LONGPRESS = Blockly.internalConstants.LONGPRESS;
Blockly.SOUND_LIMIT = Blockly.internalConstants.SOUND_LIMIT;
Blockly.DRAG_STACK = Blockly.internalConstants.DRAG_STACK;
Blockly.HSV_SATURATION = Blockly.internalConstants.HSV_SATURATION;
Blockly.HSV_VALUE = Blockly.internalConstants.HSV_VALUE;
Blockly.SPRITE = Blockly.internalConstants.SPRITE;
Blockly.DRAG_NONE = Blockly.internalConstants.DRAG_NONE;
Blockly.DRAG_STICKY = Blockly.internalConstants.DRAG_STICKY;
Blockly.DRAG_BEGIN = Blockly.internalConstants.DRAG_BEGIN;
Blockly.DRAG_FREE = Blockly.internalConstants.DRAG_FREE;
Blockly.OPPOSITE_TYPE = Blockly.internalConstants.OPPOSITE_TYPE;
Blockly.VARIABLE_CATEGORY_NAME =
Blockly.internalConstants.VARIABLE_CATEGORY_NAME;
Blockly.VARIABLE_DYNAMIC_CATEGORY_NAME =
Blockly.internalConstants.VARIABLE_DYNAMIC_CATEGORY_NAME;
Blockly.PROCEDURE_CATEGORY_NAME =
Blockly.internalConstants.PROCEDURE_CATEGORY_NAME;
Blockly.RENAME_VARIABLE_ID = Blockly.internalConstants.RENAME_VARIABLE_ID;
Blockly.DELETE_VARIABLE_ID = Blockly.internalConstants.DELETE_VARIABLE_ID;
Blockly.COLLAPSED_INPUT_NAME = Blockly.constants.COLLAPSED_INPUT_NAME;
Blockly.COLLAPSED_FIELD_NAME = Blockly.constants.COLLAPSED_FIELD_NAME;

View File

@@ -14,10 +14,13 @@
* A mapping of block type names to block prototype objects.
* @name Blockly.Blocks
*/
goog.provide('Blockly.Blocks');
goog.module('Blockly.Blocks');
goog.module.declareLegacyNamespace();
/**
* A mapping of block type names to block prototype objects.
* @type {!Object<string,Object>}
* @type {!Object<string,!Object>}
*/
Blockly.Blocks = Object.create(null);
const Blocks = Object.create(null);
exports = Blocks;

View File

@@ -10,50 +10,57 @@
*/
'use strict';
goog.provide('Blockly.BubbleDragger');
goog.module('Blockly.BubbleDragger');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const BlockDragSurfaceSvg = goog.requireType('Blockly.BlockDragSurfaceSvg');
const ComponentManager = goog.require('Blockly.ComponentManager');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const IBubble = goog.requireType('Blockly.IBubble');
/* 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 WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const utils = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.Bubble');
goog.require('Blockly.ComponentManager');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.Events');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.CommentMove');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.requireType('Blockly.BlockDragSurfaceSvg');
goog.requireType('Blockly.IBubble');
goog.requireType('Blockly.WorkspaceSvg');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
/**
* Class for a bubble dragger. It moves things on the bubble canvas around the
* workspace when they are being dragged by a mouse or touch. These can be
* block comments, mutators, warnings, or workspace comments.
* @param {!Blockly.IBubble} bubble The item on the bubble canvas to drag.
* @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on.
* @param {!IBubble} bubble The item on the bubble canvas to drag.
* @param {!WorkspaceSvg} workspace The workspace to drag on.
* @constructor
*/
Blockly.BubbleDragger = function(bubble, workspace) {
const BubbleDragger = function(bubble, workspace) {
/**
* The item on the bubble canvas that is being dragged.
* @type {!Blockly.IBubble}
* @type {!IBubble}
* @private
*/
this.draggingBubble_ = bubble;
/**
* The workspace on which the bubble is being dragged.
* @type {!Blockly.WorkspaceSvg}
* @type {!WorkspaceSvg}
* @private
*/
this.workspace_ = workspace;
/**
* Which drag target the mouse pointer is over, if any.
* @type {?Blockly.IDragTarget}
* @type {?IDragTarget}
* @private
*/
this.dragTarget_ = null;
@@ -68,7 +75,7 @@ Blockly.BubbleDragger = function(bubble, workspace) {
/**
* The location of the top left corner of the dragging bubble's body at the
* beginning of the drag, in workspace coordinates.
* @type {!Blockly.utils.Coordinate}
* @type {!Coordinate}
* @private
*/
this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY();
@@ -76,11 +83,11 @@ Blockly.BubbleDragger = function(bubble, workspace) {
/**
* The drag surface to move bubbles to during a drag, or null if none should
* be used. Block dragging and bubble dragging use the same surface.
* @type {Blockly.BlockDragSurfaceSvg}
* @type {BlockDragSurfaceSvg}
* @private
*/
this.dragSurface_ =
Blockly.utils.is3dSupported() && !!workspace.getBlockDragSurface() ?
utils.is3dSupported() && !!workspace.getBlockDragSurface() ?
workspace.getBlockDragSurface() :
null;
};
@@ -90,7 +97,7 @@ Blockly.BubbleDragger = function(bubble, workspace) {
* @package
* @suppress {checkTypes}
*/
Blockly.BubbleDragger.prototype.dispose = function() {
BubbleDragger.prototype.dispose = function() {
this.draggingBubble_ = null;
this.workspace_ = null;
this.dragSurface_ = null;
@@ -100,9 +107,9 @@ Blockly.BubbleDragger.prototype.dispose = function() {
* Start dragging a bubble. This includes moving it to the drag surface.
* @package
*/
Blockly.BubbleDragger.prototype.startBubbleDrag = function() {
if (!Blockly.Events.getGroup()) {
Blockly.Events.setGroup(true);
BubbleDragger.prototype.startBubbleDrag = function() {
if (!Events.getGroup()) {
Events.setGroup(true);
}
this.workspace_.setResizesEnabled(false);
@@ -118,19 +125,19 @@ Blockly.BubbleDragger.prototype.startBubbleDrag = function() {
* Execute a step of bubble dragging, based on the given event. Update the
* display accordingly.
* @param {!Event} e The most recent move event.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at the start of the drag, in pixel units.
* @package
*/
Blockly.BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) {
var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
var newLoc = Blockly.utils.Coordinate.sum(this.startXY_, delta);
BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) {
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
const newLoc = Coordinate.sum(this.startXY_, delta);
this.draggingBubble_.moveDuringDrag(this.dragSurface_, newLoc);
var oldDragTarget = this.dragTarget_;
const oldDragTarget = this.dragTarget_;
this.dragTarget_ = this.workspace_.getDragTarget(e);
var oldWouldDeleteBubble = this.wouldDeleteBubble_;
const oldWouldDeleteBubble = this.wouldDeleteBubble_;
this.wouldDeleteBubble_ = this.shouldDelete_(this.dragTarget_);
if (oldWouldDeleteBubble != this.wouldDeleteBubble_) {
// Prevent unnecessary add/remove class calls.
@@ -147,19 +154,19 @@ Blockly.BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) {
/**
* Whether ending the drag would delete the bubble.
* @param {?Blockly.IDragTarget} dragTarget The drag target that the bubblee is
* @param {?IDragTarget} dragTarget The drag target that the bubblee is
* currently over.
* @return {boolean} Whether dropping the bubble immediately would delete the
* block.
* @private
*/
Blockly.BubbleDragger.prototype.shouldDelete_ = function(dragTarget) {
BubbleDragger.prototype.shouldDelete_ = function(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))
return (/** @type {!IDeleteArea} */ (dragTarget))
.wouldDelete(this.draggingBubble_, false);
}
}
@@ -171,29 +178,29 @@ Blockly.BubbleDragger.prototype.shouldDelete_ = function(dragTarget) {
* dragging bubble would be deleted if released immediately.
* @private
*/
Blockly.BubbleDragger.prototype.updateCursorDuringBubbleDrag_ = function() {
BubbleDragger.prototype.updateCursorDuringBubbleDrag_ = function() {
this.draggingBubble_.setDeleteStyle(this.wouldDeleteBubble_);
};
/**
* Finish a bubble drag and put the bubble back on the workspace.
* @param {!Event} e The mouseup/touchend event.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at the start of the drag, in pixel units.
* @package
*/
Blockly.BubbleDragger.prototype.endBubbleDrag = function(
e, currentDragDeltaXY) {
BubbleDragger.prototype.endBubbleDrag = function(e, currentDragDeltaXY) {
// Make sure internal state is fresh.
this.dragBubble(e, currentDragDeltaXY);
var preventMove = this.dragTarget_ &&
const preventMove = this.dragTarget_ &&
this.dragTarget_.shouldPreventMove(this.draggingBubble_);
let newLoc;
if (preventMove) {
var newLoc = this.startXY_;
newLoc = this.startXY_;
} else {
var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
var newLoc = Blockly.utils.Coordinate.sum(this.startXY_, delta);
const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
newLoc = Coordinate.sum(this.startXY_, delta);
}
// Move the bubble to its final location.
this.draggingBubble_.moveTo(newLoc.x, newLoc.y);
@@ -218,20 +225,21 @@ Blockly.BubbleDragger.prototype.endBubbleDrag = function(
}
this.workspace_.setResizesEnabled(true);
Blockly.Events.setGroup(false);
Events.setGroup(false);
};
/**
* Fire a move event at the end of a bubble drag.
* @private
*/
Blockly.BubbleDragger.prototype.fireMoveEvent_ = function() {
BubbleDragger.prototype.fireMoveEvent_ = function() {
if (this.draggingBubble_.isComment) {
var event = new (Blockly.Events.get(Blockly.Events.COMMENT_MOVE))(
// TODO (adodson): Resolve build errors when requiring WorkspaceCommentSvg.
const event = new (Events.get(Events.COMMENT_MOVE))(
/** @type {!Blockly.WorkspaceCommentSvg} */ (this.draggingBubble_));
event.setOldCoordinate(this.startXY_);
event.recordNew();
Blockly.Events.fire(event);
Events.fire(event);
}
// TODO (fenichel): move events for comments.
return;
@@ -242,14 +250,14 @@ Blockly.BubbleDragger.prototype.fireMoveEvent_ = function() {
* correction for mutator workspaces.
* This function does not consider differing origins. It simply scales the
* input's x and y values.
* @param {!Blockly.utils.Coordinate} pixelCoord A coordinate with x and y
* @param {!Coordinate} pixelCoord A coordinate with x and y
* values in CSS pixel units.
* @return {!Blockly.utils.Coordinate} The input coordinate divided by the
* @return {!Coordinate} The input coordinate divided by the
* workspace scale.
* @private
*/
Blockly.BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
var result = new Blockly.utils.Coordinate(
BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
const result = new Coordinate(
pixelCoord.x / this.workspace_.scale,
pixelCoord.y / this.workspace_.scale);
if (this.workspace_.isMutator) {
@@ -257,7 +265,7 @@ Blockly.BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
// oddities in our rendering optimizations. The actual scale is the same as
// the scale on the parent workspace.
// Fix that for dragging.
var mainScale = this.workspace_.options.parentWorkspace.scale;
const mainScale = this.workspace_.options.parentWorkspace.scale;
result.scale(1 / mainScale);
}
return result;
@@ -268,9 +276,11 @@ Blockly.BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
* drag surface to preserve the apparent location of the bubble.
* @private
*/
Blockly.BubbleDragger.prototype.moveToDragSurface_ = function() {
BubbleDragger.prototype.moveToDragSurface_ = function() {
this.draggingBubble_.moveTo(0, 0);
this.dragSurface_.translateSurface(this.startXY_.x, this.startXY_.y);
// Execute the move on the top-level SVG component.
this.dragSurface_.setBlocksAndShow(this.draggingBubble_.getSvgRoot());
};
exports = BubbleDragger;

View File

@@ -10,43 +10,49 @@
*/
'use strict';
goog.provide('Blockly.Comment');
goog.module('Blockly.Comment');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.Bubble');
goog.require('Blockly.Css');
goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Bubble = goog.require('Blockly.Bubble');
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
const Icon = goog.require('Blockly.Icon');
/* eslint-disable-next-line no-unused-vars */
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 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} */
goog.require('Blockly.Events.BubbleOpen');
goog.require('Blockly.Icon');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.userAgent');
/** @suppress {extraRequire} */
goog.require('Blockly.Warning');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.utils.Coordinate');
goog.requireType('Blockly.utils.Size');
goog.requireType('Blockly.WorkspaceSvg');
/**
* Class for a comment.
* @param {!Blockly.Block} block The block associated with this comment.
* @extends {Blockly.Icon}
* @param {!Block} block The block associated with this comment.
* @extends {Icon}
* @constructor
*/
Blockly.Comment = function(block) {
Blockly.Comment.superClass_.constructor.call(this, block);
const Comment = function(block) {
Comment.superClass_.constructor.call(this, block);
/**
* The model for this comment.
* @type {!Blockly.Block.CommentModel}
* @type {!Block.CommentModel}
* @private
*/
this.model_ = block.commentModel;
@@ -64,62 +70,60 @@ Blockly.Comment = function(block) {
/**
* Mouse up event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onMouseUpWrapper_ = null;
/**
* Wheel event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onWheelWrapper_ = null;
/**
* Change event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onChangeWrapper_ = null;
/**
* Input event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onInputWrapper_ = null;
this.createIcon();
};
Blockly.utils.object.inherits(Blockly.Comment, Blockly.Icon);
inherits(Comment, Icon);
/**
* Draw the comment icon.
* @param {!Element} group The icon group.
* @protected
*/
Blockly.Comment.prototype.drawIcon_ = function(group) {
Comment.prototype.drawIcon_ = function(group) {
// Circle.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.CIRCLE,
{'class': 'blocklyIconShape', 'r': '8', 'cx': '8', 'cy': '8'},
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.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.PATH,
{
createSvgElement(
Svg.PATH, {
'class': 'blocklyIconSymbol',
'd': 'm6.8,10h2c0.003,-0.617 0.271,-0.962 0.633,-1.266 2.875,-2.405' +
'0.607,-5.534 -3.765,-3.874v1.7c3.12,-1.657 3.698,0.118 2.336,1.25' +
'-1.201,0.998 -1.201,1.528 -1.204,2.19z'},
'0.607,-5.534 -3.765,-3.874v1.7c3.12,-1.657 3.698,0.118 2.336,1.25' +
'-1.201,0.998 -1.201,1.528 -1.204,2.19z'
},
group);
// Dot of question mark.
Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.RECT,
{
createSvgElement(
Svg.RECT, {
'class': 'blocklyIconSymbol',
'x': '6.8',
'y': '10.78',
@@ -134,7 +138,7 @@ Blockly.Comment.prototype.drawIcon_ = function(group) {
* @return {!SVGElement} The top-level node of the editor.
* @private
*/
Blockly.Comment.prototype.createEditor_ = function() {
Comment.prototype.createEditor_ = function() {
/* Create the editor. Here's the markup that will be generated in
* editable mode:
<foreignObject x="8" y="8" width="164" height="164">
@@ -147,18 +151,16 @@ Blockly.Comment.prototype.createEditor_ = function() {
* For non-editable mode see Warning.textToDom_.
*/
this.foreignObject_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.FOREIGNOBJECT,
{'x': Blockly.Bubble.BORDER_WIDTH, 'y': Blockly.Bubble.BORDER_WIDTH},
this.foreignObject_ = createSvgElement(
Svg.FOREIGNOBJECT, {'x': Bubble.BORDER_WIDTH, 'y': Bubble.BORDER_WIDTH},
null);
var body = document.createElementNS(Blockly.utils.dom.HTML_NS, 'body');
body.setAttribute('xmlns', Blockly.utils.dom.HTML_NS);
const body = document.createElementNS(HTML_NS, 'body');
body.setAttribute('xmlns', HTML_NS);
body.className = 'blocklyMinimalBody';
this.textarea_ = document.createElementNS(
Blockly.utils.dom.HTML_NS, 'textarea');
var textarea = this.textarea_;
this.textarea_ = document.createElementNS(HTML_NS, 'textarea');
const textarea = this.textarea_;
textarea.className = 'blocklyCommentTextarea';
textarea.setAttribute('dir', this.block_.RTL ? 'RTL' : 'LTR');
textarea.value = this.model_.text;
@@ -170,26 +172,23 @@ Blockly.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_ = Blockly.browserEvents.conditionalBind(
textarea, 'mouseup', this, this.startEdit_, true, true);
this.onMouseUpWrapper_ =
conditionalBind(textarea, 'mouseup', this, this.startEdit_, true, true);
// Don't zoom with mousewheel.
this.onWheelWrapper_ = Blockly.browserEvents.conditionalBind(
textarea, 'wheel', this, function(e) {
e.stopPropagation();
});
this.onChangeWrapper_ = Blockly.browserEvents.conditionalBind(
textarea, 'change', this, function(_e) {
this.onWheelWrapper_ = conditionalBind(textarea, 'wheel', this, function(e) {
e.stopPropagation();
});
this.onChangeWrapper_ =
conditionalBind(textarea, 'change', this, function(_e) {
if (this.cachedText_ != this.model_.text) {
Blockly.Events.fire(
new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))(
this.block_, 'comment', null, this.cachedText_,
this.model_.text));
Events.fire(new (Events.get(Events.BLOCK_CHANGE))(
this.block_, 'comment', null, this.cachedText_,
this.model_.text));
}
});
this.onInputWrapper_ = Blockly.browserEvents.conditionalBind(
textarea, 'input', this, function(_e) {
this.model_.text = textarea.value;
});
this.onInputWrapper_ = conditionalBind(textarea, 'input', this, function(_e) {
this.model_.text = textarea.value;
});
setTimeout(textarea.focus.bind(textarea), 0);
@@ -200,8 +199,8 @@ Blockly.Comment.prototype.createEditor_ = function() {
* Add or remove editability of the comment.
* @override
*/
Blockly.Comment.prototype.updateEditable = function() {
Blockly.Comment.superClass_.updateEditable.call(this);
Comment.prototype.updateEditable = function() {
Comment.superClass_.updateEditable.call(this);
if (this.isVisible()) {
// Recreate the bubble with the correct UI.
this.disposeBubble_();
@@ -214,7 +213,7 @@ Blockly.Comment.prototype.updateEditable = function() {
* Resize the text area accordingly.
* @private
*/
Blockly.Comment.prototype.onBubbleResize_ = function() {
Comment.prototype.onBubbleResize_ = function() {
if (!this.isVisible()) {
return;
}
@@ -227,11 +226,11 @@ Blockly.Comment.prototype.onBubbleResize_ = function() {
* the size of the bubble).
* @private
*/
Blockly.Comment.prototype.resizeTextarea_ = function() {
var size = this.model_.size;
var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH;
var widthMinusBorder = size.width - doubleBorderWidth;
var heightMinusBorder = size.height - doubleBorderWidth;
Comment.prototype.resizeTextarea_ = function() {
const size = this.model_.size;
const doubleBorderWidth = 2 * Bubble.BORDER_WIDTH;
const widthMinusBorder = size.width - doubleBorderWidth;
const heightMinusBorder = size.height - doubleBorderWidth;
this.foreignObject_.setAttribute('width', widthMinusBorder);
this.foreignObject_.setAttribute('height', heightMinusBorder);
this.textarea_.style.width = (widthMinusBorder - 4) + 'px';
@@ -242,12 +241,12 @@ Blockly.Comment.prototype.resizeTextarea_ = function() {
* Show or hide the comment bubble.
* @param {boolean} visible True if the bubble should be visible.
*/
Blockly.Comment.prototype.setVisible = function(visible) {
Comment.prototype.setVisible = function(visible) {
if (visible == this.isVisible()) {
return;
}
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.BUBBLE_OPEN))(
this.block_, visible, 'comment'));
Events.fire(
new (Events.get(Events.BUBBLE_OPEN))(this.block_, visible, 'comment'));
this.model_.pinned = visible;
if (visible) {
this.createBubble_();
@@ -260,8 +259,8 @@ Blockly.Comment.prototype.setVisible = function(visible) {
* Show the bubble. Handles deciding if it should be editable or not.
* @private
*/
Blockly.Comment.prototype.createBubble_ = function() {
if (!this.block_.isEditable() || Blockly.utils.userAgent.IE) {
Comment.prototype.createBubble_ = function() {
if (!this.block_.isEditable() || userAgent.IE) {
// MSIE does not support foreignobject; textareas are impossible.
// https://docs.microsoft.com/en-us/openspecs/ie_standards/ms-svg/56e6e04c-7c8c-44dd-8100-bd745ee42034
// Always treat comments in IE as uneditable.
@@ -275,12 +274,12 @@ Blockly.Comment.prototype.createBubble_ = function() {
* Show an editable bubble.
* @private
*/
Blockly.Comment.prototype.createEditableBubble_ = function() {
this.bubble_ = new Blockly.Bubble(
/** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace),
Comment.prototype.createEditableBubble_ = function() {
this.bubble_ = new Bubble(
/** @type {!WorkspaceSvg} */ (this.block_.workspace),
this.createEditor_(), this.block_.pathObject.svgPath,
/** @type {!Blockly.utils.Coordinate} */ (this.iconXY_),
this.model_.size.width, this.model_.size.height);
/** @type {!Coordinate} */ (this.iconXY_), this.model_.size.width,
this.model_.size.height);
// Expose this comment's block's ID on its top-level SVG group.
this.bubble_.setSvgId(this.block_.id);
this.bubble_.registerResizeEvent(this.onBubbleResize_.bind(this));
@@ -292,12 +291,12 @@ Blockly.Comment.prototype.createEditableBubble_ = function() {
* @private
* @suppress {checkTypes} Suppress `this` type mismatch.
*/
Blockly.Comment.prototype.createNonEditableBubble_ = function() {
Comment.prototype.createNonEditableBubble_ = function() {
// TODO (#2917): It would be great if the comment could support line breaks.
this.paragraphElement_ = Blockly.Bubble.textToDom(this.block_.getCommentText());
this.bubble_ = Blockly.Bubble.createNonEditableBubble(
this.paragraphElement_, /** @type {!Blockly.BlockSvg} */ (this.block_),
/** @type {!Blockly.utils.Coordinate} */ (this.iconXY_));
this.paragraphElement_ = Bubble.textToDom(this.block_.getCommentText());
this.bubble_ = Bubble.createNonEditableBubble(
this.paragraphElement_, /** @type {!BlockSvg} */ (this.block_),
/** @type {!Coordinate} */ (this.iconXY_));
this.applyColour();
};
@@ -306,21 +305,21 @@ Blockly.Comment.prototype.createNonEditableBubble_ = function() {
* @private
* @suppress {checkTypes} Suppress `this` type mismatch.
*/
Blockly.Comment.prototype.disposeBubble_ = function() {
Comment.prototype.disposeBubble_ = function() {
if (this.onMouseUpWrapper_) {
Blockly.browserEvents.unbind(this.onMouseUpWrapper_);
unbind(this.onMouseUpWrapper_);
this.onMouseUpWrapper_ = null;
}
if (this.onWheelWrapper_) {
Blockly.browserEvents.unbind(this.onWheelWrapper_);
unbind(this.onWheelWrapper_);
this.onWheelWrapper_ = null;
}
if (this.onChangeWrapper_) {
Blockly.browserEvents.unbind(this.onChangeWrapper_);
unbind(this.onChangeWrapper_);
this.onChangeWrapper_ = null;
}
if (this.onInputWrapper_) {
Blockly.browserEvents.unbind(this.onInputWrapper_);
unbind(this.onInputWrapper_);
this.onInputWrapper_ = null;
}
this.bubble_.dispose();
@@ -338,7 +337,7 @@ Blockly.Comment.prototype.disposeBubble_ = function() {
* @param {!Event} _e Mouse up event.
* @private
*/
Blockly.Comment.prototype.startEdit_ = function(_e) {
Comment.prototype.startEdit_ = function(_e) {
if (this.bubble_.promote()) {
// Since the act of moving this node within the DOM causes a loss of focus,
// we need to reapply the focus.
@@ -350,9 +349,9 @@ Blockly.Comment.prototype.startEdit_ = function(_e) {
/**
* Get the dimensions of this comment's bubble.
* @return {Blockly.utils.Size} Object with width and height properties.
* @return {Size} Object with width and height properties.
*/
Blockly.Comment.prototype.getBubbleSize = function() {
Comment.prototype.getBubbleSize = function() {
return this.model_.size;
};
@@ -361,7 +360,7 @@ Blockly.Comment.prototype.getBubbleSize = function() {
* @param {number} width Width of the bubble.
* @param {number} height Height of the bubble.
*/
Blockly.Comment.prototype.setBubbleSize = function(width, height) {
Comment.prototype.setBubbleSize = function(width, height) {
if (this.bubble_) {
this.bubble_.setBubbleSize(width, height);
} else {
@@ -374,7 +373,7 @@ Blockly.Comment.prototype.setBubbleSize = function(width, height) {
* Update the comment's view to match the model.
* @package
*/
Blockly.Comment.prototype.updateText = function() {
Comment.prototype.updateText = function() {
if (this.textarea_) {
this.textarea_.value = this.model_.text;
} else if (this.paragraphElement_) {
@@ -390,25 +389,20 @@ Blockly.Comment.prototype.updateText = function() {
* If you want to receive a comment "delete" event (newValue: null), then this
* should not be called directly. Instead call block.setCommentText(null);
*/
Blockly.Comment.prototype.dispose = function() {
Comment.prototype.dispose = function() {
this.block_.comment = null;
Blockly.Icon.prototype.dispose.call(this);
Icon.prototype.dispose.call(this);
};
/**
* CSS for block comment. See css.js for use.
*/
Blockly.Css.register([
register([
/* eslint-disable indent */
'.blocklyCommentTextarea {',
'background-color: #fef49c;',
'border: 0;',
'outline: 0;',
'margin: 0;',
'padding: 3px;',
'resize: none;',
'display: block;',
'text-overflow: hidden;',
'}'
'.blocklyCommentTextarea {', 'background-color: #fef49c;', 'border: 0;',
'outline: 0;', 'margin: 0;', 'padding: 3px;', 'resize: none;',
'display: block;', 'text-overflow: hidden;', '}'
/* eslint-enable indent */
]);
exports = Comment;

View File

@@ -11,23 +11,29 @@
'use strict';
goog.provide('Blockly.ComponentManager');
goog.module('Blockly.ComponentManager');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.IAutoHideable');
goog.requireType('Blockly.IComponent');
goog.requireType('Blockly.IDeleteArea');
goog.requireType('Blockly.IDragTarget');
goog.requireType('Blockly.IPositionable');
/* eslint-disable-next-line no-unused-vars */
const IAutoHideable = goog.requireType('Blockly.IAutoHideable');
/* eslint-disable-next-line no-unused-vars */
const IComponent = goog.requireType('Blockly.IComponent');
/* 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 IPositionable = goog.requireType('Blockly.IPositionable');
/**
* Manager for all items registered with the workspace.
* @constructor
*/
Blockly.ComponentManager = function() {
const ComponentManager = function() {
/**
* A map of the components registered with the workspace, mapped to id.
* @type {!Object<string, !Blockly.ComponentManager.ComponentDatum>}
* @type {!Object<string, !ComponentManager.ComponentDatum>}
* @private
*/
this.componentData_ = Object.create(null);
@@ -43,35 +49,35 @@ Blockly.ComponentManager = function() {
/**
* An object storing component information.
* @typedef {{
* component: !Blockly.IComponent,
* component: !IComponent,
* capabilities: (
* !Array<string|!Blockly.ComponentManager.Capability<!Blockly.IComponent>>
* !Array<string|!ComponentManager.Capability<!IComponent>>
* ),
* weight: number
* }}
*/
Blockly.ComponentManager.ComponentDatum;
ComponentManager.ComponentDatum;
/**
* Adds a component.
* @param {!Blockly.ComponentManager.ComponentDatum} componentInfo The data for
* @param {!ComponentManager.ComponentDatum} componentInfo The data for
* the component to register.
* @param {boolean=} opt_allowOverrides True to prevent an error when overriding
* an already registered item.
*/
Blockly.ComponentManager.prototype.addComponent = function(
ComponentManager.prototype.addComponent = function(
componentInfo, opt_allowOverrides) {
// Don't throw an error if opt_allowOverrides is true.
var id = componentInfo.component.id;
const id = componentInfo.component.id;
if (!opt_allowOverrides && this.componentData_[id]) {
throw Error(
'Plugin "' + id + '" with capabilities "' +
this.componentData_[id].capabilities + '" already added.');
}
this.componentData_[id] = componentInfo;
var stringCapabilities = [];
for (var i = 0; i < componentInfo.capabilities.length; i++) {
var capability = String(componentInfo.capabilities[i]).toLowerCase();
const stringCapabilities = [];
for (let i = 0; i < componentInfo.capabilities.length; i++) {
const capability = String(componentInfo.capabilities[i]).toLowerCase();
stringCapabilities.push(capability);
if (this.capabilityToComponentIds_[capability] === undefined) {
this.capabilityToComponentIds_[capability] = [id];
@@ -86,13 +92,13 @@ Blockly.ComponentManager.prototype.addComponent = function(
* Removes a component.
* @param {string} id The ID of the component to remove.
*/
Blockly.ComponentManager.prototype.removeComponent = function(id) {
var componentInfo = this.componentData_[id];
ComponentManager.prototype.removeComponent = function(id) {
const componentInfo = this.componentData_[id];
if (!componentInfo) {
return;
}
for (var i = 0; i < componentInfo.capabilities.length; i++) {
var capability = String(componentInfo.capabilities[i]).toLowerCase();
for (let i = 0; i < componentInfo.capabilities.length; i++) {
const capability = String(componentInfo.capabilities[i]).toLowerCase();
this.capabilityToComponentIds_[capability].splice(
this.capabilityToComponentIds_[capability].indexOf(id), 1);
}
@@ -102,18 +108,19 @@ Blockly.ComponentManager.prototype.removeComponent = function(id) {
/**
* Adds a capability to a existing registered component.
* @param {string} id The ID of the component to add the capability to.
* @param {string|!Blockly.ComponentManager.Capability<T>} capability The
* @param {string|!ComponentManager.Capability<T>} capability The
* capability to add.
* @template T
*/
Blockly.ComponentManager.prototype.addCapability = function(id, capability) {
ComponentManager.prototype.addCapability = function(id, capability) {
if (!this.getComponent(id)) {
throw Error('Cannot add capability, "' + capability + '". Plugin "' +
id + '" has not been added to the ComponentManager');
throw Error(
'Cannot add capability, "' + capability + '". Plugin "' + id +
'" has not been added to the ComponentManager');
}
if (this.hasCapability(id, capability)) {
console.warn('Plugin "' + id + 'already has capability "' +
capability + '"');
console.warn(
'Plugin "' + id + 'already has capability "' + capability + '"');
return;
}
capability = String(capability).toLowerCase();
@@ -124,18 +131,20 @@ Blockly.ComponentManager.prototype.addCapability = function(id, capability) {
/**
* Removes a capability from an existing registered component.
* @param {string} id The ID of the component to remove the capability from.
* @param {string|!Blockly.ComponentManager.Capability<T>} capability The
* @param {string|!ComponentManager.Capability<T>} capability The
* capability to remove.
* @template T
*/
Blockly.ComponentManager.prototype.removeCapability = function(id, capability) {
ComponentManager.prototype.removeCapability = function(id, capability) {
if (!this.getComponent(id)) {
throw Error('Cannot remove capability, "' + capability + '". Plugin "' +
id + '" has not been added to the ComponentManager');
throw Error(
'Cannot remove capability, "' + capability + '". Plugin "' + id +
'" has not been added to the ComponentManager');
}
if (!this.hasCapability(id, capability)) {
console.warn('Plugin "' + id + 'doesn\'t have capability "' +
capability + '" to remove');
console.warn(
'Plugin "' + id + 'doesn\'t have capability "' + capability +
'" to remove');
return;
}
capability = String(capability).toLowerCase();
@@ -148,12 +157,12 @@ Blockly.ComponentManager.prototype.removeCapability = function(id, capability) {
/**
* Returns whether the component with this id has the specified capability.
* @param {string} id The ID of the component to check.
* @param {string|!Blockly.ComponentManager.Capability<T>} capability The
* @param {string|!ComponentManager.Capability<T>} capability The
* capability to check for.
* @return {boolean} Whether the component has the capability.
* @template T
*/
Blockly.ComponentManager.prototype.hasCapability = function(id, capability) {
ComponentManager.prototype.hasCapability = function(id, capability) {
capability = String(capability).toLowerCase();
return this.componentData_[id].capabilities.indexOf(capability) !== -1;
};
@@ -161,31 +170,31 @@ Blockly.ComponentManager.prototype.hasCapability = function(id, capability) {
/**
* Gets the component with the given ID.
* @param {string} id The ID of the component to get.
* @return {!Blockly.IComponent|undefined} The component with the given name
* @return {!IComponent|undefined} The component with the given name
* or undefined if not found.
*/
Blockly.ComponentManager.prototype.getComponent = function(id) {
ComponentManager.prototype.getComponent = function(id) {
return this.componentData_[id] && this.componentData_[id].component;
};
/**
* Gets all the components with the specified capability.
* @param {string|!Blockly.ComponentManager.Capability<T>
* @param {string|!ComponentManager.Capability<T>
* } capability The capability of the component.
* @param {boolean} sorted Whether to return list ordered by weights.
* @return {!Array<T>} The components that match the specified capability.
* @template T
*/
Blockly.ComponentManager.prototype.getComponents = function(capability, sorted) {
ComponentManager.prototype.getComponents = function(capability, sorted) {
capability = String(capability).toLowerCase();
var componentIds = this.capabilityToComponentIds_[capability];
const componentIds = this.capabilityToComponentIds_[capability];
if (!componentIds) {
return [];
}
var components = [];
const components = [];
if (sorted) {
var componentDataList = [];
var componentData = this.componentData_;
const componentDataList = [];
const componentData = this.componentData_;
componentIds.forEach(function(id) {
componentDataList.push(componentData[id]);
});
@@ -196,7 +205,7 @@ Blockly.ComponentManager.prototype.getComponents = function(capability, sorted)
components.push(ComponentDatum.component);
});
} else {
var componentData = this.componentData_;
const componentData = this.componentData_;
componentIds.forEach(function(id) {
components.push(componentData[id].component);
});
@@ -210,7 +219,7 @@ Blockly.ComponentManager.prototype.getComponents = function(capability, sorted)
* @constructor
* @template T
*/
Blockly.ComponentManager.Capability = function(name) {
ComponentManager.Capability = function(name) {
/**
* @type {string}
* @private
@@ -223,22 +232,24 @@ Blockly.ComponentManager.Capability = function(name) {
* @return {string} The name.
* @override
*/
Blockly.ComponentManager.Capability.prototype.toString = function() {
ComponentManager.Capability.prototype.toString = function() {
return this.name_;
};
/** @type {!Blockly.ComponentManager.Capability<!Blockly.IPositionable>} */
Blockly.ComponentManager.Capability.POSITIONABLE =
new Blockly.ComponentManager.Capability('positionable');
/** @type {!ComponentManager.Capability<!IPositionable>} */
ComponentManager.Capability.POSITIONABLE =
new ComponentManager.Capability('positionable');
/** @type {!Blockly.ComponentManager.Capability<!Blockly.IDragTarget>} */
Blockly.ComponentManager.Capability.DRAG_TARGET =
new Blockly.ComponentManager.Capability('drag_target');
/** @type {!ComponentManager.Capability<!IDragTarget>} */
ComponentManager.Capability.DRAG_TARGET =
new ComponentManager.Capability('drag_target');
/** @type {!Blockly.ComponentManager.Capability<!Blockly.IDeleteArea>} */
Blockly.ComponentManager.Capability.DELETE_AREA =
new Blockly.ComponentManager.Capability('delete_area');
/** @type {!ComponentManager.Capability<!IDeleteArea>} */
ComponentManager.Capability.DELETE_AREA =
new ComponentManager.Capability('delete_area');
/** @type {!Blockly.ComponentManager.Capability<!Blockly.IAutoHideable>} */
Blockly.ComponentManager.Capability.AUTOHIDEABLE =
new Blockly.ComponentManager.Capability('autohideable');
/** @type {!ComponentManager.Capability<!IAutoHideable>} */
ComponentManager.Capability.AUTOHIDEABLE =
new ComponentManager.Capability('autohideable');
exports = ComponentManager;

View File

@@ -10,33 +10,37 @@
*/
'use strict';
goog.provide('Blockly.Connection');
goog.module('Blockly.Connection');
goog.module.declareLegacyNamespace();
goog.require('Blockly.connectionTypes');
/* eslint-disable-next-line no-unused-vars */
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');
/* eslint-disable-next-line no-unused-vars */
const IConnectionChecker = goog.requireType('Blockly.IConnectionChecker');
/* eslint-disable-next-line no-unused-vars */
const Input = goog.requireType('Blockly.Input');
const Xml = goog.require('Blockly.Xml');
const connectionTypes = goog.require('Blockly.connectionTypes');
const deprecation = goog.require('Blockly.utils.deprecation');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.Events');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockMove');
goog.require('Blockly.IASTNodeLocationWithBlock');
goog.require('Blockly.utils.deprecation');
goog.require('Blockly.Xml');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.IConnectionChecker');
goog.requireType('Blockly.Input');
/**
* Class for a connection between blocks.
* @param {!Blockly.Block} source The block establishing this connection.
* @param {!Block} source The block establishing this connection.
* @param {number} type The type of the connection.
* @constructor
* @implements {Blockly.IASTNodeLocationWithBlock}
* @implements {IASTNodeLocationWithBlock}
*/
Blockly.Connection = function(source, type) {
const Connection = function(source, type) {
/**
* @type {!Blockly.Block}
* @type {!Block}
* @protected
*/
this.sourceBlock_ = source;
@@ -47,67 +51,67 @@ Blockly.Connection = function(source, type) {
/**
* Constants for checking whether two connections are compatible.
*/
Blockly.Connection.CAN_CONNECT = 0;
Blockly.Connection.REASON_SELF_CONNECTION = 1;
Blockly.Connection.REASON_WRONG_TYPE = 2;
Blockly.Connection.REASON_TARGET_NULL = 3;
Blockly.Connection.REASON_CHECKS_FAILED = 4;
Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5;
Blockly.Connection.REASON_SHADOW_PARENT = 6;
Blockly.Connection.REASON_DRAG_CHECKS_FAILED = 7;
Connection.CAN_CONNECT = 0;
Connection.REASON_SELF_CONNECTION = 1;
Connection.REASON_WRONG_TYPE = 2;
Connection.REASON_TARGET_NULL = 3;
Connection.REASON_CHECKS_FAILED = 4;
Connection.REASON_DIFFERENT_WORKSPACES = 5;
Connection.REASON_SHADOW_PARENT = 6;
Connection.REASON_DRAG_CHECKS_FAILED = 7;
/**
* Connection this connection connects to. Null if not connected.
* @type {Blockly.Connection}
* @type {Connection}
*/
Blockly.Connection.prototype.targetConnection = null;
Connection.prototype.targetConnection = null;
/**
* Has this connection been disposed of?
* @type {boolean}
* @package
*/
Blockly.Connection.prototype.disposed = false;
Connection.prototype.disposed = false;
/**
* List of compatible value types. Null if all types are compatible.
* @type {Array}
* @private
*/
Blockly.Connection.prototype.check_ = null;
Connection.prototype.check_ = null;
/**
* DOM representation of a shadow block, or null if none.
* @type {Element}
* @private
*/
Blockly.Connection.prototype.shadowDom_ = null;
Connection.prototype.shadowDom_ = null;
/**
* Horizontal location of this connection.
* @type {number}
* @package
*/
Blockly.Connection.prototype.x = 0;
Connection.prototype.x = 0;
/**
* Vertical location of this connection.
* @type {number}
* @package
*/
Blockly.Connection.prototype.y = 0;
Connection.prototype.y = 0;
/**
* Connect two connections together. This is the connection on the superior
* block.
* @param {!Blockly.Connection} childConnection Connection on inferior block.
* @param {!Connection} childConnection Connection on inferior block.
* @protected
*/
Blockly.Connection.prototype.connect_ = function(childConnection) {
var INPUT = Blockly.connectionTypes.INPUT_VALUE;
var parentConnection = this;
var parentBlock = parentConnection.getSourceBlock();
var childBlock = childConnection.getSourceBlock();
Connection.prototype.connect_ = function(childConnection) {
const INPUT = connectionTypes.INPUT_VALUE;
const parentConnection = this;
const parentBlock = parentConnection.getSourceBlock();
const childBlock = childConnection.getSourceBlock();
// Make sure the childConnection is available.
if (childConnection.isConnected()) {
@@ -115,11 +119,11 @@ Blockly.Connection.prototype.connect_ = function(childConnection) {
}
// Make sure the parentConnection is available.
var orphan;
let orphan;
if (parentConnection.isConnected()) {
var shadowDom = parentConnection.getShadowDom(true);
const shadowDom = parentConnection.getShadowDom(true);
parentConnection.shadowDom_ = null; // Set to null so it doesn't respawn.
var target = parentConnection.targetBlock();
const target = parentConnection.targetBlock();
if (target.isShadow()) {
target.dispose(false);
} else {
@@ -130,23 +134,24 @@ Blockly.Connection.prototype.connect_ = function(childConnection) {
}
// Connect the new connection to the parent.
var event;
if (Blockly.Events.isEnabled()) {
event = new (Blockly.Events.get(Blockly.Events.BLOCK_MOVE))(childBlock);
let event;
if (Events.isEnabled()) {
event = new (Events.get(Events.BLOCK_MOVE))(childBlock);
}
Blockly.Connection.connectReciprocally_(parentConnection, childConnection);
connectReciprocally(parentConnection, childConnection);
childBlock.setParent(parentBlock);
if (event) {
event.recordNew();
Blockly.Events.fire(event);
Events.fire(event);
}
// Deal with the orphan if it exists.
if (orphan) {
var orphanConnection = parentConnection.type === INPUT ?
orphan.outputConnection : orphan.previousConnection;
var connection = Blockly.Connection.getConnectionForOrphanedConnection(
childBlock, /** @type {!Blockly.Connection} */ (orphanConnection));
const orphanConnection = parentConnection.type === INPUT ?
orphan.outputConnection :
orphan.previousConnection;
const connection = Connection.getConnectionForOrphanedConnection(
childBlock, /** @type {!Connection} */ (orphanConnection));
if (connection) {
orphanConnection.connect(connection);
} else {
@@ -160,14 +165,13 @@ Blockly.Connection.prototype.connect_ = function(childConnection) {
* Dispose of this connection and deal with connected blocks.
* @package
*/
Blockly.Connection.prototype.dispose = function() {
Connection.prototype.dispose = function() {
// isConnected returns true for shadows and non-shadows.
if (this.isConnected()) {
// Destroy the attached shadow block & its children (if it exists).
this.setShadowDom(null);
var targetBlock = this.targetBlock();
const targetBlock = this.targetBlock();
if (targetBlock) {
// Disconnect the attached normal block.
targetBlock.unplug();
@@ -179,9 +183,9 @@ Blockly.Connection.prototype.dispose = function() {
/**
* Get the source block for this connection.
* @return {!Blockly.Block} The source block.
* @return {!Block} The source block.
*/
Blockly.Connection.prototype.getSourceBlock = function() {
Connection.prototype.getSourceBlock = function() {
return this.sourceBlock_;
};
@@ -189,82 +193,75 @@ Blockly.Connection.prototype.getSourceBlock = function() {
* Does the connection belong to a superior block (higher in the source stack)?
* @return {boolean} True if connection faces down or right.
*/
Blockly.Connection.prototype.isSuperior = function() {
return this.type == Blockly.connectionTypes.INPUT_VALUE ||
this.type == Blockly.connectionTypes.NEXT_STATEMENT;
Connection.prototype.isSuperior = function() {
return this.type == connectionTypes.INPUT_VALUE ||
this.type == connectionTypes.NEXT_STATEMENT;
};
/**
* Is the connection connected?
* @return {boolean} True if connection is connected to another connection.
*/
Blockly.Connection.prototype.isConnected = function() {
Connection.prototype.isConnected = function() {
return !!this.targetConnection;
};
/**
* Checks whether the current connection can connect with the target
* connection.
* @param {Blockly.Connection} target Connection to check compatibility with.
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
* @param {Connection} target Connection to check compatibility with.
* @return {number} Connection.CAN_CONNECT if the connection is legal,
* an error code otherwise.
* @deprecated July 2020. Will be deleted July 2021. Use the workspace's
* connectionChecker instead.
*/
Blockly.Connection.prototype.canConnectWithReason = function(target) {
Blockly.utils.deprecation.warn(
'Connection.prototype.canConnectWithReason',
'July 2020',
'July 2021',
Connection.prototype.canConnectWithReason = function(target) {
deprecation.warn(
'Connection.prototype.canConnectWithReason', 'July 2020', 'July 2021',
'the workspace\'s connection checker');
return this.getConnectionChecker().canConnectWithReason(
this, target, false);
return this.getConnectionChecker().canConnectWithReason(this, target, false);
};
/**
* Checks whether the current connection and target connection are compatible
* and throws an exception if they are not.
* @param {Blockly.Connection} target The connection to check compatibility
* @param {Connection} target The connection to check compatibility
* with.
* @package
* @deprecated July 2020. Will be deleted July 2021. Use the workspace's
* connectionChecker instead.
*/
Blockly.Connection.prototype.checkConnection = function(target) {
Blockly.utils.deprecation.warn(
'Connection.prototype.checkConnection',
'July 2020',
'July 2021',
Connection.prototype.checkConnection = function(target) {
deprecation.warn(
'Connection.prototype.checkConnection', 'July 2020', 'July 2021',
'the workspace\'s connection checker');
var checker = this.getConnectionChecker();
var reason = checker.canConnectWithReason(this, target, false);
if (reason != Blockly.Connection.CAN_CONNECT) {
const checker = this.getConnectionChecker();
const reason = checker.canConnectWithReason(this, target, false);
if (reason != Connection.CAN_CONNECT) {
throw new Error(checker.getErrorMessage(reason, this, target));
}
};
/**
* Get the workspace's connection type checker object.
* @return {!Blockly.IConnectionChecker} The connection type checker for the
* @return {!IConnectionChecker} The connection type checker for the
* source block's workspace.
* @package
*/
Blockly.Connection.prototype.getConnectionChecker = function() {
Connection.prototype.getConnectionChecker = function() {
return this.sourceBlock_.workspace.connectionChecker;
};
/**
* 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.
* @return {boolean} True if the connection is allowed, false otherwise.
* @deprecated July 2020. Will be deleted July 2021. Use the workspace's
* connectionChecker instead.
*/
Blockly.Connection.prototype.isConnectionAllowed = function(candidate) {
Blockly.utils.deprecation.warn(
'Connection.prototype.isConnectionAllowed',
'July 2020',
'July 2021',
Connection.prototype.isConnectionAllowed = function(candidate) {
deprecation.warn(
'Connection.prototype.isConnectionAllowed', 'July 2020', 'July 2021',
'the workspace\'s connection checker');
return this.getConnectionChecker().canConnect(this, candidate, true);
};
@@ -272,29 +269,29 @@ Blockly.Connection.prototype.isConnectionAllowed = function(candidate) {
/**
* Called when an attempted connection fails. NOP by default (i.e. for headless
* workspaces).
* @param {!Blockly.Connection} _otherConnection Connection that this connection
* @param {!Connection} _otherConnection Connection that this connection
* failed to connect to.
* @package
*/
Blockly.Connection.prototype.onFailedConnect = function(_otherConnection) {
Connection.prototype.onFailedConnect = function(_otherConnection) {
// NOP
};
/**
* Connect this connection to another connection.
* @param {!Blockly.Connection} otherConnection Connection to connect to.
* @param {!Connection} otherConnection Connection to connect to.
*/
Blockly.Connection.prototype.connect = function(otherConnection) {
Connection.prototype.connect = function(otherConnection) {
if (this.targetConnection == otherConnection) {
// Already connected together. NOP.
return;
}
var checker = this.getConnectionChecker();
const checker = this.getConnectionChecker();
if (checker.canConnect(this, otherConnection, false)) {
var eventGroup = Blockly.Events.getGroup();
const eventGroup = Events.getGroup();
if (!eventGroup) {
Blockly.Events.setGroup(true);
Events.setGroup(true);
}
// Determine which block is superior (higher in the source stack).
if (this.isSuperior()) {
@@ -305,18 +302,17 @@ Blockly.Connection.prototype.connect = function(otherConnection) {
otherConnection.connect_(this);
}
if (!eventGroup) {
Blockly.Events.setGroup(false);
Events.setGroup(false);
}
}
};
/**
* Update two connections to target each other.
* @param {Blockly.Connection} first The first connection to update.
* @param {Blockly.Connection} second The second connection to update.
* @private
* @param {Connection} first The first connection to update.
* @param {Connection} second The second connection to update.
*/
Blockly.Connection.connectReciprocally_ = function(first, second) {
const connectReciprocally = function(first, second) {
if (!first || !second) {
throw Error('Cannot connect null connections.');
}
@@ -329,19 +325,18 @@ Blockly.Connection.connectReciprocally_ = function(first, second) {
* block, if one can be found. If the block has multiple compatible connections
* (even if they are filled) this returns null. If the block has no compatible
* connections, this returns null.
* @param {!Blockly.Block} block The superior block.
* @param {!Blockly.Block} orphanBlock The inferior block.
* @return {?Blockly.Connection} The suitable connection point on 'block',
* @param {!Block} block The superior block.
* @param {!Block} orphanBlock The inferior block.
* @return {?Connection} The suitable connection point on 'block',
* or null.
* @private
*/
Blockly.Connection.getSingleConnection_ = function(block, orphanBlock) {
var foundConnection = null;
var output = orphanBlock.outputConnection;
var typeChecker = output.getConnectionChecker();
const getSingleConnection = function(block, orphanBlock) {
let foundConnection = null;
const output = orphanBlock.outputConnection;
const typeChecker = output.getConnectionChecker();
for (var i = 0, input; (input = block.inputList[i]); i++) {
var connection = input.connection;
for (let i = 0, input; (input = block.inputList[i]); i++) {
const connection = input.connection;
if (connection && typeChecker.canConnect(output, connection, false)) {
if (foundConnection) {
return null; // More than one connection.
@@ -358,64 +353,62 @@ Blockly.Connection.getSingleConnection_ = function(block, orphanBlock) {
* are zero or multiple eligible connections, returns null. Otherwise
* returns the only input on the last block in the chain.
* Terminates early for shadow blocks.
* @param {!Blockly.Block} startBlock The block on which to start the search.
* @param {!Blockly.Block} orphanBlock The block that is looking for a home.
* @return {?Blockly.Connection} The suitable connection point on the chain
* @param {!Block} startBlock The block on which to start the search.
* @param {!Block} orphanBlock The block that is looking for a home.
* @return {?Connection} The suitable connection point on the chain
* of blocks, or null.
* @private
*/
Blockly.Connection.getConnectionForOrphanedOutput_ =
function(startBlock, orphanBlock) {
var newBlock = startBlock;
var connection;
while ((connection = Blockly.Connection.getSingleConnection_(
/** @type {!Blockly.Block} */ (newBlock), orphanBlock))) {
newBlock = connection.targetBlock();
if (!newBlock || newBlock.isShadow()) {
return connection;
}
}
return null;
};
const getConnectionForOrphanedOutput = function(startBlock, orphanBlock) {
let newBlock = startBlock;
let connection;
while (
(connection = getSingleConnection(
/** @type {!Block} */ (newBlock), orphanBlock))) {
newBlock = connection.targetBlock();
if (!newBlock || newBlock.isShadow()) {
return connection;
}
}
return null;
};
/**
* Returns the connection (starting at the startBlock) which will accept
* the given connection. This includes compatible connection types and
* connection checks.
* @param {!Blockly.Block} startBlock The block on which to start the search.
* @param {!Blockly.Connection} orphanConnection The connection that is looking
* @param {!Block} startBlock The block on which to start the search.
* @param {!Connection} orphanConnection The connection that is looking
* for a home.
* @return {?Blockly.Connection} The suitable connection point on the chain of
* @return {?Connection} The suitable connection point on the chain of
* blocks, or null.
*/
Blockly.Connection.getConnectionForOrphanedConnection =
function(startBlock, orphanConnection) {
if (orphanConnection.type === Blockly.connectionTypes.OUTPUT_VALUE) {
return Blockly.Connection.getConnectionForOrphanedOutput_(
startBlock, orphanConnection.getSourceBlock());
}
// Otherwise we're dealing with a stack.
var connection = startBlock.lastConnectionInStack(true);
var checker = orphanConnection.getConnectionChecker();
if (connection &&
checker.canConnect(orphanConnection, connection, false)) {
return connection;
}
return null;
};
Connection.getConnectionForOrphanedConnection = function(
startBlock, orphanConnection) {
if (orphanConnection.type === connectionTypes.OUTPUT_VALUE) {
return getConnectionForOrphanedOutput(
startBlock, orphanConnection.getSourceBlock());
}
// Otherwise we're dealing with a stack.
const connection = startBlock.lastConnectionInStack(true);
const checker = orphanConnection.getConnectionChecker();
if (connection && checker.canConnect(orphanConnection, connection, false)) {
return connection;
}
return null;
};
/**
* Disconnect this connection.
*/
Blockly.Connection.prototype.disconnect = function() {
var otherConnection = this.targetConnection;
Connection.prototype.disconnect = function() {
const otherConnection = this.targetConnection;
if (!otherConnection) {
throw Error('Source connection not connected.');
}
if (otherConnection.targetConnection != this) {
throw Error('Target connection not connected to source connection.');
}
var parentBlock, childBlock, parentConnection;
let parentBlock, childBlock, parentConnection;
if (this.isSuperior()) {
// Superior block.
parentBlock = this.sourceBlock_;
@@ -428,9 +421,9 @@ Blockly.Connection.prototype.disconnect = function() {
parentConnection = otherConnection;
}
var eventGroup = Blockly.Events.getGroup();
const eventGroup = Events.getGroup();
if (!eventGroup) {
Blockly.Events.setGroup(true);
Events.setGroup(true);
}
this.disconnectInternal_(parentBlock, childBlock);
if (!childBlock.isShadow()) {
@@ -438,29 +431,28 @@ Blockly.Connection.prototype.disconnect = function() {
parentConnection.respawnShadow_();
}
if (!eventGroup) {
Blockly.Events.setGroup(false);
Events.setGroup(false);
}
};
/**
* 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
*/
Blockly.Connection.prototype.disconnectInternal_ = function(parentBlock,
childBlock) {
var event;
if (Blockly.Events.isEnabled()) {
event = new (Blockly.Events.get(Blockly.Events.BLOCK_MOVE))(childBlock);
Connection.prototype.disconnectInternal_ = function(parentBlock, childBlock) {
let event;
if (Events.isEnabled()) {
event = new (Events.get(Events.BLOCK_MOVE))(childBlock);
}
var otherConnection = this.targetConnection;
const otherConnection = this.targetConnection;
otherConnection.targetConnection = null;
this.targetConnection = null;
childBlock.setParent(null);
if (event) {
event.recordNew();
Blockly.Events.fire(event);
Events.fire(event);
}
};
@@ -468,11 +460,11 @@ Blockly.Connection.prototype.disconnectInternal_ = function(parentBlock,
* Respawn the shadow block if there was one connected to the this connection.
* @protected
*/
Blockly.Connection.prototype.respawnShadow_ = function() {
var parentBlock = this.getSourceBlock();
var shadow = this.getShadowDom();
Connection.prototype.respawnShadow_ = function() {
const parentBlock = this.getSourceBlock();
const shadow = this.getShadowDom();
if (parentBlock.workspace && shadow) {
var blockShadow = Blockly.Xml.domToBlock(shadow, parentBlock.workspace);
const blockShadow = Xml.domToBlock(shadow, parentBlock.workspace);
if (blockShadow.outputConnection) {
this.connect(blockShadow.outputConnection);
} else if (blockShadow.previousConnection) {
@@ -485,9 +477,9 @@ Blockly.Connection.prototype.respawnShadow_ = function() {
/**
* Returns the block that this connection connects to.
* @return {?Blockly.Block} The connected block or null if none is connected.
* @return {?Block} The connected block or null if none is connected.
*/
Blockly.Connection.prototype.targetBlock = function() {
Connection.prototype.targetBlock = function() {
if (this.isConnected()) {
return this.targetConnection.getSourceBlock();
}
@@ -497,36 +489,31 @@ Blockly.Connection.prototype.targetBlock = function() {
/**
* Is this connection compatible with another connection with respect to the
* value type system. E.g. square_root("Hello") is not compatible.
* @param {!Blockly.Connection} otherConnection Connection to compare against.
* @param {!Connection} otherConnection Connection to compare against.
* @return {boolean} True if the connections share a type.
* @deprecated July 2020. Will be deleted July 2021. Use the workspace's
* connectionChecker instead.
*/
Blockly.Connection.prototype.checkType = function(otherConnection) {
Blockly.utils.deprecation.warn(
'Connection.prototype.checkType',
'October 2019',
'January 2021',
Connection.prototype.checkType = function(otherConnection) {
deprecation.warn(
'Connection.prototype.checkType', 'October 2019', 'January 2021',
'the workspace\'s connection checker');
return this.getConnectionChecker().canConnect(this, otherConnection,
false);
return this.getConnectionChecker().canConnect(this, otherConnection, false);
};
/**
* Is this connection compatible with another connection with respect to the
* value type system. E.g. square_root("Hello") is not compatible.
* @param {!Blockly.Connection} otherConnection Connection to compare against.
* @param {!Connection} otherConnection Connection to compare against.
* @return {boolean} True if the connections share a type.
* @private
* @deprecated October 2019. Will be deleted January 2021. Use the workspace's
* connectionChecker instead.
* @suppress {unusedPrivateMembers}
*/
Blockly.Connection.prototype.checkType_ = function(otherConnection) {
Blockly.utils.deprecation.warn(
'Connection.prototype.checkType_',
'October 2019',
'January 2021',
Connection.prototype.checkType_ = function(otherConnection) {
deprecation.warn(
'Connection.prototype.checkType_', 'October 2019', 'January 2021',
'the workspace\'s connection checker');
return this.checkType(otherConnection);
};
@@ -535,12 +522,13 @@ Blockly.Connection.prototype.checkType_ = function(otherConnection) {
* Function to be called when this connection's compatible types have changed.
* @protected
*/
Blockly.Connection.prototype.onCheckChanged_ = function() {
Connection.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();
}
};
@@ -549,10 +537,10 @@ Blockly.Connection.prototype.onCheckChanged_ = function() {
* Change a connection's compatibility.
* @param {?(string|!Array<string>)} check Compatible value type or list of
* value types. Null if all types are compatible.
* @return {!Blockly.Connection} The connection being modified
* @return {!Connection} The connection being modified
* (to allow chaining).
*/
Blockly.Connection.prototype.setCheck = function(check) {
Connection.prototype.setCheck = function(check) {
if (check) {
// Ensure that check is in an array.
if (!Array.isArray(check)) {
@@ -572,7 +560,7 @@ Blockly.Connection.prototype.setCheck = function(check) {
* Null if all types are compatible.
* @public
*/
Blockly.Connection.prototype.getCheck = function() {
Connection.prototype.getCheck = function() {
return this.check_;
};
@@ -580,9 +568,9 @@ Blockly.Connection.prototype.getCheck = function() {
* Changes the connection's shadow block.
* @param {?Element} shadow DOM representation of a block or null.
*/
Blockly.Connection.prototype.setShadowDom = function(shadow) {
Connection.prototype.setShadowDom = function(shadow) {
this.shadowDom_ = shadow;
var target = this.targetBlock();
const target = this.targetBlock();
if (!target) {
this.respawnShadow_();
} else if (target.isShadow()) {
@@ -600,10 +588,10 @@ Blockly.Connection.prototype.setShadowDom = function(shadow) {
* shadowDom is just returned.
* @return {?Element} Shadow DOM representation of a block or null.
*/
Blockly.Connection.prototype.getShadowDom = function(returnCurrent) {
Connection.prototype.getShadowDom = function(returnCurrent) {
return (returnCurrent && this.targetBlock().isShadow()) ?
/** @type {!Element} */ (Blockly.Xml.blockToDom(
/** @type {!Blockly.Block} */ (this.targetBlock()))) :
/** @type {!Element} */ (Xml.blockToDom(
/** @type {!Block} */ (this.targetBlock()))) :
this.shadowDom_;
};
@@ -616,23 +604,23 @@ Blockly.Connection.prototype.getShadowDom = function(returnCurrent) {
* {@link Blockly.RenderedConnection} overrides this behavior with a list
* computed from the rendered positioning.
* @param {number} _maxLimit The maximum radius to another connection.
* @return {!Array<!Blockly.Connection>} List of connections.
* @return {!Array<!Connection>} List of connections.
* @package
*/
Blockly.Connection.prototype.neighbours = function(_maxLimit) {
Connection.prototype.neighbours = function(_maxLimit) {
return [];
};
/**
* Get the parent input of a connection.
* @return {?Blockly.Input} The input that the connection belongs to or null if
* @return {?Input} The input that the connection belongs to or null if
* no parent exists.
* @package
*/
Blockly.Connection.prototype.getParentInput = function() {
var parentInput = null;
var inputs = this.sourceBlock_.inputList;
for (var i = 0; i < inputs.length; i++) {
Connection.prototype.getParentInput = function() {
let parentInput = null;
const inputs = this.sourceBlock_.inputList;
for (let i = 0; i < inputs.length; i++) {
if (inputs[i].connection === this) {
parentInput = inputs[i];
break;
@@ -646,12 +634,12 @@ Blockly.Connection.prototype.getParentInput = function() {
* (English only). Intended to on be used in console logs and errors.
* @return {string} The description.
*/
Blockly.Connection.prototype.toString = function() {
var block = this.sourceBlock_;
Connection.prototype.toString = function() {
const block = this.sourceBlock_;
if (!block) {
return 'Orphan Connection';
}
var msg;
let msg;
if (block.outputConnection == this) {
msg = 'Output Connection of ';
} else if (block.previousConnection == this) {
@@ -659,8 +647,8 @@ Blockly.Connection.prototype.toString = function() {
} else if (block.nextConnection == this) {
msg = 'Next Connection of ';
} else {
var parentInput = null;
for (var i = 0, input; (input = block.inputList[i]); i++) {
let parentInput = null;
for (let i = 0, input; (input = block.inputList[i]); i++) {
if (input.connection == this) {
parentInput = input;
break;
@@ -675,3 +663,5 @@ Blockly.Connection.prototype.toString = function() {
}
return msg + block.toDevString();
};
exports = Connection;

View File

@@ -5,37 +5,37 @@
*/
/**
* @fileoverview An object that encapsulates logic for checking whether a potential
* connection is safe and valid.
* @fileoverview An object that encapsulates logic for checking whether a
* potential connection is safe and valid.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.provide('Blockly.ConnectionChecker');
goog.module('Blockly.ConnectionChecker');
goog.module.declareLegacyNamespace();
goog.require('Blockly.Connection');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.IConnectionChecker');
goog.require('Blockly.registry');
goog.requireType('Blockly.RenderedConnection');
const Connection = goog.require('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const IConnectionChecker = goog.require('Blockly.IConnectionChecker');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
const connectionTypes = goog.require('Blockly.connectionTypes');
const registry = goog.require('Blockly.registry');
const {OPPOSITE_TYPE} = goog.require('Blockly.internalConstants');
/**
* Class for connection type checking logic.
* @implements {Blockly.IConnectionChecker}
* @implements {IConnectionChecker}
* @constructor
*/
Blockly.ConnectionChecker = function() {
};
const ConnectionChecker = function() {};
/**
* Check whether the current connection can connect with the target
* connection.
* @param {Blockly.Connection} a Connection to check compatibility with.
* @param {Blockly.Connection} b Connection to check compatibility with.
* @param {Connection} a Connection to check compatibility with.
* @param {Connection} b Connection to check compatibility with.
* @param {boolean} isDragging True if the connection is being made by dragging
* a block.
* @param {number=} opt_distance The max allowable distance between the
@@ -43,80 +43,80 @@ Blockly.ConnectionChecker = function() {
* @return {boolean} Whether the connection is legal.
* @public
*/
Blockly.ConnectionChecker.prototype.canConnect = function(a, b,
isDragging, opt_distance) {
ConnectionChecker.prototype.canConnect = function(
a, b, isDragging, opt_distance) {
return this.canConnectWithReason(a, b, isDragging, opt_distance) ==
Blockly.Connection.CAN_CONNECT;
Connection.CAN_CONNECT;
};
/**
* Checks whether the current connection can connect with the target
* connection, and return an error code if there are problems.
* @param {Blockly.Connection} a Connection to check compatibility with.
* @param {Blockly.Connection} b Connection to check compatibility with.
* @param {Connection} a Connection to check compatibility with.
* @param {Connection} b Connection to check compatibility with.
* @param {boolean} isDragging True if the connection is being made by dragging
* a block.
* @param {number=} opt_distance The max allowable distance between the
* connections for drag checks.
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
* @return {number} Connection.CAN_CONNECT if the connection is legal,
* an error code otherwise.
* @public
*/
Blockly.ConnectionChecker.prototype.canConnectWithReason = function(
ConnectionChecker.prototype.canConnectWithReason = function(
a, b, isDragging, opt_distance) {
var safety = this.doSafetyChecks(a, b);
if (safety != Blockly.Connection.CAN_CONNECT) {
const safety = this.doSafetyChecks(a, b);
if (safety != Connection.CAN_CONNECT) {
return safety;
}
// If the safety checks passed, both connections are non-null.
var connOne = /** @type {!Blockly.Connection} **/ (a);
var connTwo = /** @type {!Blockly.Connection} **/ (b);
const connOne = /** @type {!Connection} **/ (a);
const connTwo = /** @type {!Connection} **/ (b);
if (!this.doTypeChecks(connOne, connTwo)) {
return Blockly.Connection.REASON_CHECKS_FAILED;
return Connection.REASON_CHECKS_FAILED;
}
if (isDragging &&
!this.doDragChecks(
/** @type {!Blockly.RenderedConnection} **/ (a),
/** @type {!Blockly.RenderedConnection} **/ (b),
opt_distance || 0)) {
return Blockly.Connection.REASON_DRAG_CHECKS_FAILED;
/** @type {!RenderedConnection} **/ (a),
/** @type {!RenderedConnection} **/ (b), opt_distance || 0)) {
return Connection.REASON_DRAG_CHECKS_FAILED;
}
return Blockly.Connection.CAN_CONNECT;
return Connection.CAN_CONNECT;
};
/**
* Helper method that translates a connection error code into a string.
* @param {number} errorCode The error code.
* @param {Blockly.Connection} a One of the two connections being checked.
* @param {Blockly.Connection} b The second of the two connections being
* @param {Connection} a One of the two connections being checked.
* @param {Connection} b The second of the two connections being
* checked.
* @return {string} A developer-readable error string.
* @public
*/
Blockly.ConnectionChecker.prototype.getErrorMessage = function(errorCode,
a, b) {
ConnectionChecker.prototype.getErrorMessage = function(errorCode, a, b) {
switch (errorCode) {
case Blockly.Connection.REASON_SELF_CONNECTION:
case Connection.REASON_SELF_CONNECTION:
return 'Attempted to connect a block to itself.';
case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:
case Connection.REASON_DIFFERENT_WORKSPACES:
// Usually this means one block has been deleted.
return 'Blocks not on same workspace.';
case Blockly.Connection.REASON_WRONG_TYPE:
case Connection.REASON_WRONG_TYPE:
return 'Attempt to connect incompatible types.';
case Blockly.Connection.REASON_TARGET_NULL:
case Connection.REASON_TARGET_NULL:
return 'Target connection is null.';
case Blockly.Connection.REASON_CHECKS_FAILED:
var connOne = /** @type {!Blockly.Connection} **/ (a);
var connTwo = /** @type {!Blockly.Connection} **/ (b);
var msg = 'Connection checks failed. ';
msg += connOne + ' expected ' + connOne.getCheck() + ', found ' + connTwo.getCheck();
case Connection.REASON_CHECKS_FAILED: {
const connOne = /** @type {!Connection} **/ (a);
const connTwo = /** @type {!Connection} **/ (b);
let msg = 'Connection checks failed. ';
msg += connOne + ' expected ' + connOne.getCheck() + ', found ' +
connTwo.getCheck();
return msg;
case Blockly.Connection.REASON_SHADOW_PARENT:
}
case Connection.REASON_SHADOW_PARENT:
return 'Connecting non-shadow to shadow block.';
case Blockly.Connection.REASON_DRAG_CHECKS_FAILED:
case Connection.REASON_DRAG_CHECKS_FAILED:
return 'Drag checks failed.';
default:
return 'Unknown connection failure: this should never happen!';
@@ -126,53 +126,54 @@ Blockly.ConnectionChecker.prototype.getErrorMessage = function(errorCode,
/**
* Check that connecting the given connections is safe, meaning that it would
* not break any of Blockly's basic assumptions (e.g. no self connections).
* @param {Blockly.Connection} a The first of the connections to check.
* @param {Blockly.Connection} b The second of the connections to check.
* @param {Connection} a The first of the connections to check.
* @param {Connection} b The second of the connections to check.
* @return {number} An enum with the reason this connection is safe or unsafe.
* @public
*/
Blockly.ConnectionChecker.prototype.doSafetyChecks = function(a, b) {
ConnectionChecker.prototype.doSafetyChecks = function(a, b) {
if (!a || !b) {
return Blockly.Connection.REASON_TARGET_NULL;
return Connection.REASON_TARGET_NULL;
}
let blockA, blockB;
if (a.isSuperior()) {
var blockA = a.getSourceBlock();
var blockB = b.getSourceBlock();
blockA = a.getSourceBlock();
blockB = b.getSourceBlock();
} else {
var blockB = a.getSourceBlock();
var blockA = b.getSourceBlock();
blockB = a.getSourceBlock();
blockA = b.getSourceBlock();
}
if (blockA == blockB) {
return Blockly.Connection.REASON_SELF_CONNECTION;
} else if (b.type != Blockly.OPPOSITE_TYPE[a.type]) {
return Blockly.Connection.REASON_WRONG_TYPE;
return Connection.REASON_SELF_CONNECTION;
} else if (b.type != OPPOSITE_TYPE[a.type]) {
return Connection.REASON_WRONG_TYPE;
} else if (blockA.workspace !== blockB.workspace) {
return Blockly.Connection.REASON_DIFFERENT_WORKSPACES;
return Connection.REASON_DIFFERENT_WORKSPACES;
} else if (blockA.isShadow() && !blockB.isShadow()) {
return Blockly.Connection.REASON_SHADOW_PARENT;
return Connection.REASON_SHADOW_PARENT;
}
return Blockly.Connection.CAN_CONNECT;
return Connection.CAN_CONNECT;
};
/**
* Check whether this connection is compatible with another connection with
* respect to the value type system. E.g. square_root("Hello") is not
* compatible.
* @param {!Blockly.Connection} a Connection to compare.
* @param {!Blockly.Connection} b Connection to compare against.
* @param {!Connection} a Connection to compare.
* @param {!Connection} b Connection to compare against.
* @return {boolean} True if the connections share a type.
* @public
*/
Blockly.ConnectionChecker.prototype.doTypeChecks = function(a, b) {
var checkArrayOne = a.getCheck();
var checkArrayTwo = b.getCheck();
ConnectionChecker.prototype.doTypeChecks = function(a, b) {
const checkArrayOne = a.getCheck();
const checkArrayTwo = b.getCheck();
if (!checkArrayOne || !checkArrayTwo) {
// One or both sides are promiscuous enough that anything will fit.
return true;
}
// Find any intersection in the check lists.
for (var i = 0; i < checkArrayOne.length; i++) {
for (let i = 0; i < checkArrayOne.length; i++) {
if (checkArrayTwo.indexOf(checkArrayOne[i]) != -1) {
return true;
}
@@ -183,13 +184,13 @@ Blockly.ConnectionChecker.prototype.doTypeChecks = function(a, b) {
/**
* Check whether this connection can be made by dragging.
* @param {!Blockly.RenderedConnection} a Connection to compare.
* @param {!Blockly.RenderedConnection} b Connection to compare against.
* @param {!RenderedConnection} a Connection to compare.
* @param {!RenderedConnection} b Connection to compare against.
* @param {number} distance The maximum allowable distance between connections.
* @return {boolean} True if the connection is allowed during a drag.
* @public
*/
Blockly.ConnectionChecker.prototype.doDragChecks = function(a, b, distance) {
ConnectionChecker.prototype.doDragChecks = function(a, b, distance) {
if (a.distanceFrom(b) > distance) {
return false;
}
@@ -200,38 +201,34 @@ Blockly.ConnectionChecker.prototype.doDragChecks = function(a, b, distance) {
}
switch (b.type) {
case Blockly.connectionTypes.PREVIOUS_STATEMENT:
case connectionTypes.PREVIOUS_STATEMENT:
return this.canConnectToPrevious_(a, b);
case Blockly.connectionTypes.OUTPUT_VALUE: {
case connectionTypes.OUTPUT_VALUE: {
// Don't offer to connect an already connected left (male) value plug to
// an available right (female) value plug.
if ((b.isConnected() &&
!b.targetBlock().isInsertionMarker()) ||
if ((b.isConnected() && !b.targetBlock().isInsertionMarker()) ||
a.isConnected()) {
return false;
}
break;
}
case Blockly.connectionTypes.INPUT_VALUE: {
case connectionTypes.INPUT_VALUE: {
// Offering to connect the left (male) of a value block to an already
// connected value pair is ok, we'll splice it in.
// However, don't offer to splice into an immovable block.
if (b.isConnected() &&
!b.targetBlock().isMovable() &&
if (b.isConnected() && !b.targetBlock().isMovable() &&
!b.targetBlock().isShadow()) {
return false;
}
break;
}
case Blockly.connectionTypes.NEXT_STATEMENT: {
case connectionTypes.NEXT_STATEMENT: {
// Don't let a block with no next connection bump other blocks out of the
// stack. But covering up a shadow block or stack of shadow blocks is
// fine. Similarly, replacing a terminal statement with another terminal
// statement is allowed.
if (b.isConnected() &&
!a.getSourceBlock().nextConnection &&
!b.targetBlock().isShadow() &&
b.targetBlock().nextConnection) {
if (b.isConnected() && !a.getSourceBlock().nextConnection &&
!b.targetBlock().isShadow() && b.targetBlock().nextConnection) {
return false;
}
break;
@@ -251,14 +248,14 @@ Blockly.ConnectionChecker.prototype.doDragChecks = function(a, b, distance) {
/**
* Helper function for drag checking.
* @param {!Blockly.Connection} a The connection to check, which must be a
* @param {!Connection} a The connection to check, which must be a
* statement input or next connection.
* @param {!Blockly.Connection} b A nearby connection to check, which
* @param {!Connection} b A nearby connection to check, which
* must be a previous connection.
* @return {boolean} True if the connection is allowed, false otherwise.
* @protected
*/
Blockly.ConnectionChecker.prototype.canConnectToPrevious_ = function(a, b) {
ConnectionChecker.prototype.canConnectToPrevious_ = function(a, b) {
if (a.targetConnection) {
// This connection is already occupied.
// A next connection will never disconnect itself mid-drag.
@@ -274,7 +271,7 @@ Blockly.ConnectionChecker.prototype.canConnectToPrevious_ = function(a, b) {
return true;
}
var targetBlock = b.targetBlock();
const targetBlock = b.targetBlock();
// If it is connected to a real block, game over.
if (!targetBlock.isInsertionMarker()) {
return false;
@@ -285,5 +282,7 @@ Blockly.ConnectionChecker.prototype.canConnectToPrevious_ = function(a, b) {
return !targetBlock.getPreviousBlock();
};
Blockly.registry.register(Blockly.registry.Type.CONNECTION_CHECKER,
Blockly.registry.DEFAULT, Blockly.ConnectionChecker);
registry.register(
registry.Type.CONNECTION_CHECKER, registry.DEFAULT, ConnectionChecker);
exports = ConnectionChecker;

View File

@@ -12,37 +12,40 @@
*/
'use strict';
goog.provide('Blockly.ConnectionDB');
goog.module('Blockly.ConnectionDB');
goog.module.declareLegacyNamespace();
goog.require('Blockly.connectionTypes');
/* eslint-disable-next-line no-unused-vars */
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 connectionTypes = goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.RenderedConnection');
goog.requireType('Blockly.IConnectionChecker');
goog.requireType('Blockly.utils.Coordinate');
/**
* Database of connections.
* Connections are stored in order of their vertical component. This way
* connections in an area may be looked up quickly using a binary search.
* @param {!Blockly.IConnectionChecker} checker The workspace's
* @param {!IConnectionChecker} checker The workspace's
* connection type checker, used to decide if connections are valid during a
* drag.
* @constructor
*/
Blockly.ConnectionDB = function(checker) {
const ConnectionDB = function(checker) {
/**
* Array of connections sorted by y position in workspace units.
* @type {!Array<!Blockly.RenderedConnection>}
* @type {!Array<!RenderedConnection>}
* @private
*/
this.connections_ = [];
/**
* The workspace's connection type checker, used to decide if connections are
* valid during a drag.
* @type {!Blockly.IConnectionChecker}
* @type {!IConnectionChecker}
* @private
*/
this.connectionChecker_ = checker;
@@ -50,13 +53,13 @@ Blockly.ConnectionDB = function(checker) {
/**
* Add a connection to the database. Should not already exist in the database.
* @param {!Blockly.RenderedConnection} connection The connection to be added.
* @param {!RenderedConnection} connection The connection to be added.
* @param {number} yPos The y position used to decide where to insert the
* connection.
* @package
*/
Blockly.ConnectionDB.prototype.addConnection = function(connection, yPos) {
var index = this.calculateIndexForYPos_(yPos);
ConnectionDB.prototype.addConnection = function(connection, yPos) {
const index = this.calculateIndexForYPos_(yPos);
this.connections_.splice(index, 0, connection);
};
@@ -65,18 +68,18 @@ Blockly.ConnectionDB.prototype.addConnection = function(connection, yPos) {
*
* Starts by doing a binary search to find the approximate location, then
* linearly searches nearby for the exact connection.
* @param {!Blockly.RenderedConnection} conn The connection to find.
* @param {!RenderedConnection} conn The connection to find.
* @param {number} yPos The y position used to find the index of the connection.
* @return {number} The index of the connection, or -1 if the connection was
* not found.
* @private
*/
Blockly.ConnectionDB.prototype.findIndexOfConnection_ = function(conn, yPos) {
ConnectionDB.prototype.findIndexOfConnection_ = function(conn, yPos) {
if (!this.connections_.length) {
return -1;
}
var bestGuess = this.calculateIndexForYPos_(yPos);
const bestGuess = this.calculateIndexForYPos_(yPos);
if (bestGuess >= this.connections_.length) {
// Not in list
return -1;
@@ -84,7 +87,7 @@ Blockly.ConnectionDB.prototype.findIndexOfConnection_ = function(conn, yPos) {
yPos = conn.y;
// Walk forward and back on the y axis looking for the connection.
var pointer = bestGuess;
let pointer = bestGuess;
while (pointer >= 0 && this.connections_[pointer].y == yPos) {
if (this.connections_[pointer] == conn) {
return pointer;
@@ -110,14 +113,14 @@ Blockly.ConnectionDB.prototype.findIndexOfConnection_ = function(conn, yPos) {
* @return {number} The candidate index.
* @private
*/
Blockly.ConnectionDB.prototype.calculateIndexForYPos_ = function(yPos) {
ConnectionDB.prototype.calculateIndexForYPos_ = function(yPos) {
if (!this.connections_.length) {
return 0;
}
var pointerMin = 0;
var pointerMax = this.connections_.length;
let pointerMin = 0;
let pointerMax = this.connections_.length;
while (pointerMin < pointerMax) {
var pointerMid = Math.floor((pointerMin + pointerMax) / 2);
const pointerMid = Math.floor((pointerMin + pointerMax) / 2);
if (this.connections_[pointerMid].y < yPos) {
pointerMin = pointerMid + 1;
} else if (this.connections_[pointerMid].y > yPos) {
@@ -132,12 +135,12 @@ Blockly.ConnectionDB.prototype.calculateIndexForYPos_ = function(yPos) {
/**
* Remove a connection from the database. Must already exist in DB.
* @param {!Blockly.RenderedConnection} connection The connection to be removed.
* @param {!RenderedConnection} connection The connection to be removed.
* @param {number} yPos The y position used to find the index of the connection.
* @throws {Error} If the connection cannot be found in the database.
*/
Blockly.ConnectionDB.prototype.removeConnection = function(connection, yPos) {
var index = this.findIndexOfConnection_(connection, yPos);
ConnectionDB.prototype.removeConnection = function(connection, yPos) {
const index = this.findIndexOfConnection_(connection, yPos);
if (index == -1) {
throw Error('Unable to find connection in connectionDB.');
}
@@ -147,20 +150,20 @@ Blockly.ConnectionDB.prototype.removeConnection = function(connection, yPos) {
/**
* Find all nearby connections to the given connection.
* Type checking does not apply, since this function is used for bumping.
* @param {!Blockly.RenderedConnection} connection The connection whose
* @param {!RenderedConnection} connection The connection whose
* neighbours should be returned.
* @param {number} maxRadius The maximum radius to another connection.
* @return {!Array<!Blockly.RenderedConnection>} List of connections.
* @return {!Array<!RenderedConnection>} List of connections.
*/
Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) {
var db = this.connections_;
var currentX = connection.x;
var currentY = connection.y;
ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) {
const db = this.connections_;
const currentX = connection.x;
const currentY = connection.y;
// Binary search to find the closest y location.
var pointerMin = 0;
var pointerMax = db.length - 2;
var pointerMid = pointerMax;
let pointerMin = 0;
let pointerMax = db.length - 2;
let pointerMid = pointerMax;
while (pointerMin < pointerMid) {
if (db[pointerMid].y < currentY) {
pointerMin = pointerMid;
@@ -170,7 +173,7 @@ Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) {
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
}
var neighbours = [];
const neighbours = [];
/**
* Computes if the current connection is within the allowed radius of another
* connection.
@@ -180,9 +183,9 @@ Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) {
* the other connection is less than the allowed radius.
*/
function checkConnection_(yIndex) {
var dx = currentX - db[yIndex].x;
var dy = currentY - db[yIndex].y;
var r = Math.sqrt(dx * dx + dy * dy);
const dx = currentX - db[yIndex].x;
const dy = currentY - db[yIndex].y;
const r = Math.sqrt(dx * dx + dy * dy);
if (r <= maxRadius) {
neighbours.push(db[yIndex]);
}
@@ -213,32 +216,31 @@ Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) {
* @return {boolean} True if connection is in range.
* @private
*/
Blockly.ConnectionDB.prototype.isInYRange_ = function(index, baseY, maxRadius) {
ConnectionDB.prototype.isInYRange_ = function(index, baseY, maxRadius) {
return (Math.abs(this.connections_[index].y - baseY) <= maxRadius);
};
/**
* Find the closest compatible connection to this connection.
* @param {!Blockly.RenderedConnection} conn The connection searching for a compatible
* @param {!RenderedConnection} conn The connection searching for a compatible
* mate.
* @param {number} maxRadius The maximum radius to another connection.
* @param {!Blockly.utils.Coordinate} dxy Offset between this connection's
* @param {!Coordinate} dxy Offset between this connection's
* location in the database and the current location (as a result of
* dragging).
* @return {!{connection: Blockly.RenderedConnection, radius: number}}
* @return {!{connection: RenderedConnection, radius: number}}
* Contains two properties: 'connection' which is either another
* connection or null, and 'radius' which is the distance.
*/
Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius,
dxy) {
ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, dxy) {
if (!this.connections_.length) {
// Don't bother.
return {connection: null, radius: maxRadius};
}
// Stash the values of x and y from before the drag.
var baseY = conn.y;
var baseX = conn.x;
const baseY = conn.y;
const baseX = conn.x;
conn.x = baseX + dxy.x;
conn.y = baseY + dxy.y;
@@ -246,14 +248,14 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius,
// calculateIndexForYPos_ finds an index for insertion, which is always
// after any block with the same y index. We want to search both forward
// and back, so search on both sides of the index.
var closestIndex = this.calculateIndexForYPos_(conn.y);
const closestIndex = this.calculateIndexForYPos_(conn.y);
var bestConnection = null;
var bestRadius = maxRadius;
var temp;
let bestConnection = null;
let bestRadius = maxRadius;
let temp;
// Walk forward and back on the y axis looking for the closest x,y point.
var pointerMin = closestIndex - 1;
let pointerMin = closestIndex - 1;
while (pointerMin >= 0 && this.isInYRange_(pointerMin, conn.y, maxRadius)) {
temp = this.connections_[pointerMin];
if (this.connectionChecker_.canConnect(conn, temp, true, bestRadius)) {
@@ -263,9 +265,9 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius,
pointerMin--;
}
var pointerMax = closestIndex;
let pointerMax = closestIndex;
while (pointerMax < this.connections_.length &&
this.isInYRange_(pointerMax, conn.y, maxRadius)) {
this.isInYRange_(pointerMax, conn.y, maxRadius)) {
temp = this.connections_[pointerMax];
if (this.connectionChecker_.canConnect(conn, temp, true, bestRadius)) {
bestConnection = temp;
@@ -284,20 +286,19 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius,
/**
* Initialize a set of connection DBs for a workspace.
* @param {!Blockly.IConnectionChecker} checker The workspace's
* connection checker, used to decide if connections are valid during a drag.
* @return {!Array<!Blockly.ConnectionDB>} Array of databases.
* @param {!IConnectionChecker} checker The workspace's
* connection checker, used to decide if connections are valid during a
* drag.
* @return {!Array<!ConnectionDB>} Array of databases.
*/
Blockly.ConnectionDB.init = function(checker) {
ConnectionDB.init = function(checker) {
// Create four databases, one for each connection type.
var dbList = [];
dbList[Blockly.connectionTypes.INPUT_VALUE] =
new Blockly.ConnectionDB(checker);
dbList[Blockly.connectionTypes.OUTPUT_VALUE] =
new Blockly.ConnectionDB(checker);
dbList[Blockly.connectionTypes.NEXT_STATEMENT] =
new Blockly.ConnectionDB(checker);
dbList[Blockly.connectionTypes.PREVIOUS_STATEMENT] =
new Blockly.ConnectionDB(checker);
const dbList = [];
dbList[connectionTypes.INPUT_VALUE] = new ConnectionDB(checker);
dbList[connectionTypes.OUTPUT_VALUE] = new ConnectionDB(checker);
dbList[connectionTypes.NEXT_STATEMENT] = new ConnectionDB(checker);
dbList[connectionTypes.PREVIOUS_STATEMENT] = new ConnectionDB(checker);
return dbList;
};
exports = ConnectionDB;

View File

@@ -11,13 +11,14 @@
'use strict';
goog.provide('Blockly.connectionTypes');
goog.module('Blockly.connectionTypes');
goog.module.declareLegacyNamespace();
/**
* Enum for the type of a connection or input.
* @enum {number}
*/
Blockly.connectionTypes = {
const connectionTypes = {
// A right-facing value input. E.g. 'set item to' or 'return'.
INPUT_VALUE: 1,
// A left-facing value output. E.g. 'random fraction'.
@@ -27,3 +28,5 @@ Blockly.connectionTypes = {
// An up-facing block stack. E.g. 'break out of loop'.
PREVIOUS_STATEMENT: 4
};
exports = connectionTypes;

View File

@@ -10,204 +10,31 @@
*/
'use strict';
goog.provide('Blockly.constants');
goog.module('Blockly.constants');
goog.module.declareLegacyNamespace();
goog.require('Blockly.connectionTypes');
/**
* The multiplier for scroll wheel deltas using the line delta mode.
* @type {number}
*/
Blockly.LINE_MODE_MULTIPLIER = 40;
/**
* The multiplier for scroll wheel deltas using the page delta mode.
* @type {number}
*/
Blockly.PAGE_MODE_MULTIPLIER = 125;
/**
* Number of pixels the mouse must move before a drag starts.
*/
Blockly.DRAG_RADIUS = 5;
/**
* Number of pixels the mouse must move before a drag/scroll starts from the
* flyout. Because the drag-intention is determined when this is reached, it is
* larger than Blockly.DRAG_RADIUS so that the drag-direction is clearer.
*/
Blockly.FLYOUT_DRAG_RADIUS = 10;
/**
* Maximum misalignment between connections for them to snap together.
*/
Blockly.SNAP_RADIUS = 28;
/**
* Maximum misalignment between connections for them to snap together,
* when a connection is already highlighted.
*/
Blockly.CONNECTING_SNAP_RADIUS = Blockly.SNAP_RADIUS;
/**
* How much to prefer staying connected to the current connection over moving to
* a new connection. The current previewed connection is considered to be this
* much closer to the matching connection on the block than it actually is.
*/
Blockly.CURRENT_CONNECTION_PREFERENCE = 8;
/**
* Delay in ms between trigger and bumping unconnected block out of alignment.
*/
Blockly.BUMP_DELAY = 250;
/**
* Maximum randomness in workspace units for bumping a block.
*/
Blockly.BUMP_RANDOMNESS = 10;
/**
* Number of characters to truncate a collapsed block to.
*/
Blockly.COLLAPSE_CHARS = 30;
/**
* Length in ms for a touch to become a long press.
*/
Blockly.LONGPRESS = 750;
/**
* Prevent a sound from playing if another sound preceded it within this many
* milliseconds.
*/
Blockly.SOUND_LIMIT = 100;
/**
* When dragging a block out of a stack, split the stack in two (true), or drag
* out the block healing the stack (false).
*/
Blockly.DRAG_STACK = true;
/**
* The richness of block colours, regardless of the hue.
* Must be in the range of 0 (inclusive) to 1 (exclusive).
*/
Blockly.HSV_SATURATION = 0.45;
/**
* The intensity of block colours, regardless of the hue.
* Must be in the range of 0 (inclusive) to 1 (exclusive).
*/
Blockly.HSV_VALUE = 0.65;
/**
* Sprited icons and images.
*/
Blockly.SPRITE = {
width: 96,
height: 124,
url: 'sprites.png'
};
// Constants below this point are not intended to be changed.
/**
* Enum for alignment of inputs.
* @enum {number}
*/
Blockly.constants.ALIGN = {
const ALIGN = {
LEFT: -1,
CENTRE: 0,
RIGHT: 1
};
/**
* ENUM for no drag operation.
* @const
*/
Blockly.DRAG_NONE = 0;
/**
* ENUM for inside the sticky DRAG_RADIUS.
* @const
*/
Blockly.DRAG_STICKY = 1;
/**
* ENUM for inside the non-sticky DRAG_RADIUS, for differentiating between
* clicks and drags.
* @const
*/
Blockly.DRAG_BEGIN = 1;
/**
* ENUM for freely draggable (outside the DRAG_RADIUS, if one applies).
* @const
*/
Blockly.DRAG_FREE = 2;
/**
* Lookup table for determining the opposite type of a connection.
* @const
*/
Blockly.OPPOSITE_TYPE = [];
Blockly.OPPOSITE_TYPE[Blockly.connectionTypes.INPUT_VALUE] =
Blockly.connectionTypes.OUTPUT_VALUE;
Blockly.OPPOSITE_TYPE[Blockly.connectionTypes.OUTPUT_VALUE] =
Blockly.connectionTypes.INPUT_VALUE;
Blockly.OPPOSITE_TYPE[Blockly.connectionTypes.NEXT_STATEMENT] =
Blockly.connectionTypes.PREVIOUS_STATEMENT;
Blockly.OPPOSITE_TYPE[Blockly.connectionTypes.PREVIOUS_STATEMENT] =
Blockly.connectionTypes.NEXT_STATEMENT;
/**
* String for use in the "custom" attribute of a category in toolbox XML.
* This string indicates that the category should be dynamically populated with
* variable blocks.
* @const {string}
*/
Blockly.VARIABLE_CATEGORY_NAME = 'VARIABLE';
/**
* String for use in the "custom" attribute of a category in toolbox XML.
* This string indicates that the category should be dynamically populated with
* variable blocks.
* @const {string}
*/
Blockly.VARIABLE_DYNAMIC_CATEGORY_NAME = 'VARIABLE_DYNAMIC';
/**
* String for use in the "custom" attribute of a category in toolbox XML.
* This string indicates that the category should be dynamically populated with
* procedure blocks.
* @const {string}
*/
Blockly.PROCEDURE_CATEGORY_NAME = 'PROCEDURE';
/**
* String for use in the dropdown created in field_variable.
* This string indicates that this option in the dropdown is 'Rename
* variable...' and if selected, should trigger the prompt to rename a variable.
* @const {string}
*/
Blockly.RENAME_VARIABLE_ID = 'RENAME_VARIABLE_ID';
/**
* String for use in the dropdown created in field_variable.
* This string indicates that this option in the dropdown is 'Delete the "%1"
* variable' and if selected, should trigger the prompt to delete a variable.
* @const {string}
*/
Blockly.DELETE_VARIABLE_ID = 'DELETE_VARIABLE_ID';
exports.ALIGN = ALIGN;
/**
* The language-neutral ID given to the collapsed input.
* @const {string}
*/
Blockly.constants.COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT';
const COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT';
exports.COLLAPSED_INPUT_NAME = COLLAPSED_INPUT_NAME;
/**
* The language-neutral ID given to the collapsed field.
* @const {string}
*/
Blockly.constants.COLLAPSED_FIELD_NAME = '_TEMP_COLLAPSED_FIELD';
const COLLAPSED_FIELD_NAME = '_TEMP_COLLAPSED_FIELD';
exports.COLLAPSED_FIELD_NAME = COLLAPSED_FIELD_NAME;

View File

@@ -17,11 +17,10 @@
goog.provide('Blockly.ContextMenu');
goog.require('Blockly.browserEvents');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.Events');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockCreate');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Menu');
goog.require('Blockly.MenuItem');
goog.require('Blockly.Msg');
@@ -195,11 +194,11 @@ Blockly.ContextMenu.callbackFactory = function(block, xml) {
// Move the new block next to the old block.
var xy = block.getRelativeToSurfaceXY();
if (block.RTL) {
xy.x -= Blockly.SNAP_RADIUS;
xy.x -= Blockly.internalConstants.SNAP_RADIUS;
} else {
xy.x += Blockly.SNAP_RADIUS;
xy.x += Blockly.internalConstants.SNAP_RADIUS;
}
xy.y += Blockly.SNAP_RADIUS * 2;
xy.y += Blockly.internalConstants.SNAP_RADIUS * 2;
newBlock.moveBy(xy.x, xy.y);
} finally {
Blockly.Events.enable();

View File

@@ -14,7 +14,8 @@
* @name Blockly.Css
* @namespace
*/
goog.provide('Blockly.Css');
goog.module('Blockly.Css');
goog.module.declareLegacyNamespace();
/**
@@ -22,7 +23,7 @@ goog.provide('Blockly.Css');
* @type {boolean}
* @private
*/
Blockly.Css.injected_ = false;
let injected = false;
/**
* Add some CSS to the blob that will be injected later. Allows optional
@@ -30,14 +31,15 @@ Blockly.Css.injected_ = false;
* The provided array of CSS will be destroyed by this function.
* @param {!Array<string>} cssArray Array of CSS strings.
*/
Blockly.Css.register = function(cssArray) {
if (Blockly.Css.injected_) {
const register = function(cssArray) {
if (injected) {
throw Error('CSS already injected');
}
// Concatenate cssArray onto Blockly.Css.CONTENT.
Array.prototype.push.apply(Blockly.Css.CONTENT, cssArray);
// Concatenate cssArray onto CONTENT.
Array.prototype.push.apply(CONTENT, cssArray);
cssArray.length = 0; // Garbage collect provided CSS content.
};
exports.register = register;
/**
* Inject the CSS into the DOM. This is preferable over using a regular CSS
@@ -49,504 +51,508 @@ Blockly.Css.register = function(cssArray) {
* (providing CSS becomes the document's responsibility).
* @param {string} pathToMedia Path from page to the Blockly media directory.
*/
Blockly.Css.inject = function(hasCss, pathToMedia) {
const inject = function(hasCss, pathToMedia) {
// Only inject the CSS once.
if (Blockly.Css.injected_) {
if (injected) {
return;
}
Blockly.Css.injected_ = true;
var text = Blockly.Css.CONTENT.join('\n');
Blockly.Css.CONTENT.length = 0; // Garbage collect CSS content.
injected = true;
let text = CONTENT.join('\n');
CONTENT.length = 0; // Garbage collect CSS content.
if (!hasCss) {
return;
}
// Strip off any trailing slash (either Unix or Windows).
var mediaPath = pathToMedia.replace(/[\\/]$/, '');
const mediaPath = pathToMedia.replace(/[\\/]$/, '');
text = text.replace(/<<<PATH>>>/g, mediaPath);
// Inject CSS tag at start of head.
var cssNode = document.createElement('style');
const cssNode = document.createElement('style');
cssNode.id = 'blockly-common-style';
var cssTextNode = document.createTextNode(text);
const cssTextNode = document.createTextNode(text);
cssNode.appendChild(cssTextNode);
document.head.insertBefore(cssNode, document.head.firstChild);
};
exports.inject = inject;
/**
* Array making up the CSS content for Blockly.
*/
Blockly.Css.CONTENT = [
/* eslint-disable indent */
'.blocklySvg {',
'background-color: #fff;',
'outline: none;',
'overflow: hidden;', /* IE overflows by default. */
'position: absolute;',
'display: block;',
'}',
const CONTENT = [
`.blocklySvg {
background-color: #fff;
outline: none;
overflow: hidden; /* IE overflows by default. */
position: absolute;
display: block;
}`,
'.blocklyWidgetDiv {',
'display: none;',
'position: absolute;',
'z-index: 99999;', /* big value for bootstrap3 compatibility */
'}',
`.blocklyWidgetDiv {
display: none;
position: absolute;
z-index: 99999; /* big value for bootstrap3 compatibility */
}`,
'.injectionDiv {',
'height: 100%;',
'position: relative;',
'overflow: hidden;', /* So blocks in drag surface disappear at edges */
'touch-action: none;',
'}',
`.injectionDiv {
height: 100%;
position: relative;
overflow: hidden; /* So blocks in drag surface disappear at edges */
touch-action: none;
}`,
'.blocklyNonSelectable {',
'user-select: none;',
'-ms-user-select: none;',
'-webkit-user-select: none;',
'}',
`.blocklyNonSelectable {
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}`,
`.blocklyWsDragSurface {
display: none;
position: absolute;
top: 0;
left: 0;
}`,
'.blocklyWsDragSurface {',
'display: none;',
'position: absolute;',
'top: 0;',
'left: 0;',
'}',
/* Added as a separate rule with multiple classes to make it more specific
than a bootstrap rule that selects svg:root. See issue #1275 for context.
*/
'.blocklyWsDragSurface.blocklyOverflowVisible {',
'overflow: visible;',
'}',
`.blocklyWsDragSurface.blocklyOverflowVisible {
overflow: visible;
}`,
'.blocklyBlockDragSurface {',
'display: none;',
'position: absolute;',
'top: 0;',
'left: 0;',
'right: 0;',
'bottom: 0;',
'overflow: visible !important;',
'z-index: 50;', /* Display below toolbox, but above everything else. */
'}',
`.blocklyBlockDragSurface {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: visible !important;
z-index: 50;', /* Display below toolbox, but above everything else. */
}`,
'.blocklyBlockCanvas.blocklyCanvasTransitioning,',
'.blocklyBubbleCanvas.blocklyCanvasTransitioning {',
'transition: transform .5s;',
'}',
`.blocklyBlockCanvas.blocklyCanvasTransitioning,
.blocklyBubbleCanvas.blocklyCanvasTransitioning {
transition: transform .5s;
}`,
'.blocklyTooltipDiv {',
'background-color: #ffffc7;',
'border: 1px solid #ddc;',
'box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15);',
'color: #000;',
'display: none;',
'font: 9pt sans-serif;',
'opacity: .9;',
'padding: 2px;',
'position: absolute;',
'z-index: 100000;', /* big value for bootstrap3 compatibility */
'}',
`.blocklyTooltipDiv {
background-color: #ffffc7;
border: 1px solid #ddc;
box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15);
color: #000;
display: none;
font: 9pt sans-serif;
opacity: .9;
padding: 2px;
position: absolute;
z-index: 100000;', /* big value for bootstrap3 compatibility */
}`,
'.blocklyDropDownDiv {',
'position: absolute;',
'left: 0;',
'top: 0;',
'z-index: 1000;',
'display: none;',
'border: 1px solid;',
'border-color: #dadce0;',
'background-color: #fff;',
'border-radius: 2px;',
'padding: 4px;',
'box-shadow: 0 0 3px 1px rgba(0,0,0,.3);',
'}',
`.blocklyDropDownDiv {
position: absolute;
left: 0;
top: 0;
z-index: 1000;
display: none;
border: 1px solid;
border-color: #dadce0;
background-color: #fff;
border-radius: 2px;
padding: 4px;
box-shadow: 0 0 3px 1px rgba(0,0,0,.3);
}`,
'.blocklyDropDownDiv.blocklyFocused {',
'box-shadow: 0 0 6px 1px rgba(0,0,0,.3);',
'}',
`.blocklyDropDownDiv.blocklyFocused {
box-shadow: 0 0 6px 1px rgba(0,0,0,.3);
}`,
'.blocklyDropDownContent {',
'max-height: 300px;', // @todo: spec for maximum height.
'overflow: auto;',
'overflow-x: hidden;',
'position: relative;',
'}',
`.blocklyDropDownContent {
max-height: 300px;', // @todo: spec for maximum height.
overflow: auto;
overflow-x: hidden;
position: relative;
}`,
'.blocklyDropDownArrow {',
'position: absolute;',
'left: 0;',
'top: 0;',
'width: 16px;',
'height: 16px;',
'z-index: -1;',
'background-color: inherit;',
'border-color: inherit;',
'}',
`.blocklyDropDownArrow {
position: absolute;
left: 0;
top: 0;
width: 16px;
height: 16px;
z-index: -1;
background-color: inherit;
border-color: inherit;
}`,
'.blocklyDropDownButton {',
'display: inline-block;',
'float: left;',
'padding: 0;',
'margin: 4px;',
'border-radius: 4px;',
'outline: none;',
'border: 1px solid;',
'transition: box-shadow .1s;',
'cursor: pointer;',
'}',
`.blocklyDropDownButton {
display: inline-block;
float: left;
padding: 0;
margin: 4px;
border-radius: 4px;
outline: none;
border: 1px solid;
transition: box-shadow .1s;
cursor: pointer;
}`,
'.blocklyArrowTop {',
'border-top: 1px solid;',
'border-left: 1px solid;',
'border-top-left-radius: 4px;',
'border-color: inherit;',
'}',
`.blocklyArrowTop {
border-top: 1px solid;
border-left: 1px solid;
border-top-left-radius: 4px;
border-color: inherit;
}`,
'.blocklyArrowBottom {',
'border-bottom: 1px solid;',
'border-right: 1px solid;',
'border-bottom-right-radius: 4px;',
'border-color: inherit;',
'}',
`.blocklyArrowBottom {
border-bottom: 1px solid;
border-right: 1px solid;
border-bottom-right-radius: 4px;
border-color: inherit;
}`,
'.blocklyResizeSE {',
'cursor: se-resize;',
'fill: #aaa;',
'}',
`.blocklyResizeSE {
cursor: se-resize;
fill: #aaa;
}`,
'.blocklyResizeSW {',
'cursor: sw-resize;',
'fill: #aaa;',
'}',
`.blocklyResizeSW {
cursor: sw-resize;
fill: #aaa;
}`,
'.blocklyResizeLine {',
'stroke: #515A5A;',
'stroke-width: 1;',
'}',
`.blocklyResizeLine {
stroke: #515A5A;
stroke-width: 1;
}`,
'.blocklyHighlightedConnectionPath {',
'fill: none;',
'stroke: #fc3;',
'stroke-width: 4px;',
'}',
`.blocklyHighlightedConnectionPath {
fill: none;
stroke: #fc3;
stroke-width: 4px;
}`,
'.blocklyPathLight {',
'fill: none;',
'stroke-linecap: round;',
'stroke-width: 1;',
'}',
`.blocklyPathLight {
fill: none;
stroke-linecap: round;
stroke-width: 1;
}`,
'.blocklySelected>.blocklyPathLight {',
'display: none;',
'}',
`.blocklySelected>.blocklyPathLight {
display: none;
}`,
'.blocklyDraggable {',
/* backup for browsers (e.g. IE11) that don't support grab */
'cursor: url("<<<PATH>>>/handopen.cur"), auto;',
'cursor: grab;',
'cursor: -webkit-grab;',
'}',
`.blocklyDraggable {
/* backup for browsers (e.g. IE11) that don't support grab */
cursor: url("<<<PATH>>>/handopen.cur"), auto;
cursor: grab;
cursor: -webkit-grab;
}`,
/* backup for browsers (e.g. IE11) that don't support grabbing */
`.blocklyDragging {
/* backup for browsers (e.g. IE11) that don't support grabbing */
cursor: url("<<<PATH>>>/handclosed.cur"), auto;
cursor: grabbing;
cursor: -webkit-grabbing;
}`,
'.blocklyDragging {',
/* backup for browsers (e.g. IE11) that don't support grabbing */
'cursor: url("<<<PATH>>>/handclosed.cur"), auto;',
'cursor: grabbing;',
'cursor: -webkit-grabbing;',
'}',
/* Changes cursor on mouse down. Not effective in Firefox because of
https://bugzilla.mozilla.org/show_bug.cgi?id=771241 */
'.blocklyDraggable:active {',
/* backup for browsers (e.g. IE11) that don't support grabbing */
'cursor: url("<<<PATH>>>/handclosed.cur"), auto;',
'cursor: grabbing;',
'cursor: -webkit-grabbing;',
'}',
https://bugzilla.mozilla.org/show_bug.cgi?id=771241 */
`.blocklyDraggable:active {
/* backup for browsers (e.g. IE11) that don't support grabbing */
cursor: url("<<<PATH>>>/handclosed.cur"), auto;
cursor: grabbing;
cursor: -webkit-grabbing;
}`,
/* Change the cursor on the whole drag surface in case the mouse gets
ahead of block during a drag. This way the cursor is still a closed hand.
*/
'.blocklyBlockDragSurface .blocklyDraggable {',
/* backup for browsers (e.g. IE11) that don't support grabbing */
'cursor: url("<<<PATH>>>/handclosed.cur"), auto;',
'cursor: grabbing;',
'cursor: -webkit-grabbing;',
'}',
`.blocklyBlockDragSurface .blocklyDraggable {
/* backup for browsers (e.g. IE11) that don't support grabbing */
cursor: url("<<<PATH>>>/handclosed.cur"), auto;
cursor: grabbing;
cursor: -webkit-grabbing;
}`,
'.blocklyDragging.blocklyDraggingDelete {',
'cursor: url("<<<PATH>>>/handdelete.cur"), auto;',
'}',
`.blocklyDragging.blocklyDraggingDelete {
cursor: url("<<<PATH>>>/handdelete.cur"), auto;
}`,
'.blocklyDragging>.blocklyPath,',
'.blocklyDragging>.blocklyPathLight {',
'fill-opacity: .8;',
'stroke-opacity: .8;',
'}',
`.blocklyDragging>.blocklyPath,
.blocklyDragging>.blocklyPathLight {
fill-opacity: .8;
stroke-opacity: .8;
}`,
'.blocklyDragging>.blocklyPathDark {',
'display: none;',
'}',
`.blocklyDragging>.blocklyPathDark {
display: none;
}`,
'.blocklyDisabled>.blocklyPath {',
'fill-opacity: .5;',
'stroke-opacity: .5;',
'}',
`.blocklyDisabled>.blocklyPath {
fill-opacity: .5;
stroke-opacity: .5;
}`,
'.blocklyDisabled>.blocklyPathLight,',
'.blocklyDisabled>.blocklyPathDark {',
'display: none;',
'}',
`.blocklyDisabled>.blocklyPathLight,
.blocklyDisabled>.blocklyPathDark {
display: none;
}`,
'.blocklyInsertionMarker>.blocklyPath,',
'.blocklyInsertionMarker>.blocklyPathLight,',
'.blocklyInsertionMarker>.blocklyPathDark {',
'fill-opacity: .2;',
'stroke: none;',
'}',
`.blocklyInsertionMarker>.blocklyPath,
.blocklyInsertionMarker>.blocklyPathLight,
.blocklyInsertionMarker>.blocklyPathDark {
fill-opacity: .2;
stroke: none;
}`,
'.blocklyMultilineText {',
'font-family: monospace;',
'}',
`.blocklyMultilineText {
font-family: monospace;
}`,
'.blocklyNonEditableText>text {',
'pointer-events: none;',
'}',
`.blocklyNonEditableText>text {
pointer-events: none;
}`,
'.blocklyFlyout {',
'position: absolute;',
'z-index: 20;',
'}',
`.blocklyFlyout {
position: absolute;
z-index: 20;
}`,
'.blocklyText text {',
'cursor: default;',
'}',
`.blocklyText text {
cursor: default;
}`,
/*
Don't allow users to select text. It gets annoying when trying to
drag a block and selected text moves instead.
*/
'.blocklySvg text,',
'.blocklyBlockDragSurface text {',
'user-select: none;',
'-ms-user-select: none;',
'-webkit-user-select: none;',
'cursor: inherit;',
'}',
`.blocklySvg text,
.blocklyBlockDragSurface text {
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
cursor: inherit;
}`,
'.blocklyHidden {',
'display: none;',
'}',
`.blocklyHidden {
display: none;
}`,
'.blocklyFieldDropdown:not(.blocklyHidden) {',
'display: block;',
'}',
`.blocklyFieldDropdown:not(.blocklyHidden) {
display: block;
}`,
'.blocklyIconGroup {',
'cursor: default;',
'}',
`.blocklyIconGroup {
cursor: default;
}`,
'.blocklyIconGroup:not(:hover),',
'.blocklyIconGroupReadonly {',
'opacity: .6;',
'}',
`.blocklyIconGroup:not(:hover),
.blocklyIconGroupReadonly {
opacity: .6;
}`,
'.blocklyIconShape {',
'fill: #00f;',
'stroke: #fff;',
'stroke-width: 1px;',
'}',
`.blocklyIconShape {
fill: #00f;
stroke: #fff;
stroke-width: 1px;
}`,
'.blocklyIconSymbol {',
'fill: #fff;',
'}',
`.blocklyIconSymbol {
fill: #fff;
}`,
'.blocklyMinimalBody {',
'margin: 0;',
'padding: 0;',
'}',
`.blocklyMinimalBody {
margin: 0;
padding: 0;
}`,
'.blocklyHtmlInput {',
'border: none;',
'border-radius: 4px;',
'height: 100%;',
'margin: 0;',
'outline: none;',
'padding: 0;',
'width: 100%;',
'text-align: center;',
'display: block;',
'box-sizing: border-box;',
'}',
`.blocklyHtmlInput {
border: none;
border-radius: 4px;
height: 100%;
margin: 0;
outline: none;
padding: 0;
width: 100%;
text-align: center;
display: block;
box-sizing: border-box;
}`,
/* Edge and IE introduce a close icon when the input value is longer than a
certain length. This affects our sizing calculations of the text input.
Hiding the close icon to avoid that. */
'.blocklyHtmlInput::-ms-clear {',
'display: none;',
'}',
`.blocklyHtmlInput::-ms-clear {
display: none;
}`,
'.blocklyMainBackground {',
'stroke-width: 1;',
'stroke: #c6c6c6;', /* Equates to #ddd due to border being off-pixel. */
'}',
`.blocklyMainBackground {
stroke-width: 1;
stroke: #c6c6c6;', /* Equates to #ddd due to border being off-pixel. */
}`,
'.blocklyMutatorBackground {',
'fill: #fff;',
'stroke: #ddd;',
'stroke-width: 1;',
'}',
`.blocklyMutatorBackground {
fill: #fff;
stroke: #ddd;
stroke-width: 1;
}`,
'.blocklyFlyoutBackground {',
'fill: #ddd;',
'fill-opacity: .8;',
'}',
`.blocklyFlyoutBackground {
fill: #ddd;
fill-opacity: .8;
}`,
'.blocklyMainWorkspaceScrollbar {',
'z-index: 20;',
'}',
`.blocklyMainWorkspaceScrollbar {
z-index: 20;
}`,
'.blocklyFlyoutScrollbar {',
'z-index: 30;',
'}',
`.blocklyFlyoutScrollbar {
z-index: 30;
}`,
'.blocklyScrollbarHorizontal,',
'.blocklyScrollbarVertical {',
'position: absolute;',
'outline: none;',
'}',
`.blocklyScrollbarHorizontal,
.blocklyScrollbarVertical {
position: absolute;
outline: none;
}`,
'.blocklyScrollbarBackground {',
'opacity: 0;',
'}',
`.blocklyScrollbarBackground {
opacity: 0;
}`,
'.blocklyScrollbarHandle {',
'fill: #ccc;',
'}',
`.blocklyScrollbarHandle {
fill: #ccc;
}`,
'.blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,',
'.blocklyScrollbarHandle:hover {',
'fill: #bbb;',
'}',
`.blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,
.blocklyScrollbarHandle:hover {
fill: #bbb;
}`,
/* Darken flyout scrollbars due to being on a grey background. */
/* By contrast, workspace scrollbars are on a white background. */
'.blocklyFlyout .blocklyScrollbarHandle {',
'fill: #bbb;',
'}',
`.blocklyFlyout .blocklyScrollbarHandle {
fill: #bbb;
}`,
'.blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,',
'.blocklyFlyout .blocklyScrollbarHandle:hover {',
'fill: #aaa;',
'}',
`.blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,
.blocklyFlyout .blocklyScrollbarHandle:hover {
fill: #aaa;
}`,
'.blocklyInvalidInput {',
'background: #faa;',
'}',
`.blocklyInvalidInput {
background: #faa;
}`,
'.blocklyVerticalMarker {',
'stroke-width: 3px;',
'fill: rgba(255,255,255,.5);',
'pointer-events: none;',
'}',
`.blocklyVerticalMarker {
stroke-width: 3px;
fill: rgba(255,255,255,.5);
pointer-events: none;
}`,
'.blocklyComputeCanvas {',
'position: absolute;',
'width: 0;',
'height: 0;',
'}',
`.blocklyComputeCanvas {
position: absolute;
width: 0;
height: 0;
}`,
'.blocklyNoPointerEvents {',
'pointer-events: none;',
'}',
`.blocklyNoPointerEvents {
pointer-events: none;
}`,
'.blocklyContextMenu {',
'border-radius: 4px;',
'max-height: 100%;',
'}',
`.blocklyContextMenu {
border-radius: 4px;
max-height: 100%;
}`,
'.blocklyDropdownMenu {',
'border-radius: 2px;',
'padding: 0 !important;',
'}',
`.blocklyDropdownMenu {
border-radius: 2px;
padding: 0 !important;
}`,
'.blocklyDropdownMenu .blocklyMenuItem {',
/* 28px on the left for icon or checkbox. */
'padding-left: 28px;',
'}',
`.blocklyDropdownMenu .blocklyMenuItem {
/* 28px on the left for icon or checkbox. */
padding-left: 28px;
}`,
/* BiDi override for the resting state. */
'.blocklyDropdownMenu .blocklyMenuItemRtl {',
/* Flip left/right padding for BiDi. */
'padding-left: 5px;',
'padding-right: 28px;',
'}',
`.blocklyDropdownMenu .blocklyMenuItemRtl {
/* Flip left/right padding for BiDi. */
padding-left: 5px;
padding-right: 28px;
}`,
'.blocklyWidgetDiv .blocklyMenu {',
'background: #fff;',
'border: 1px solid transparent;',
'box-shadow: 0 0 3px 1px rgba(0,0,0,.3);',
'font: normal 13px Arial, sans-serif;',
'margin: 0;',
'outline: none;',
'padding: 4px 0;',
'position: absolute;',
'overflow-y: auto;',
'overflow-x: hidden;',
'max-height: 100%;',
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
'}',
`.blocklyWidgetDiv .blocklyMenu {
background: #fff;
border: 1px solid transparent;
box-shadow: 0 0 3px 1px rgba(0,0,0,.3);
font: normal 13px Arial, sans-serif;
margin: 0;
outline: none;
padding: 4px 0;
position: absolute;
overflow-y: auto;
overflow-x: hidden;
max-height: 100%;
z-index: 20000;', /* Arbitrary, but some apps depend on it... */
}`,
'.blocklyWidgetDiv .blocklyMenu.blocklyFocused {',
'box-shadow: 0 0 6px 1px rgba(0,0,0,.3);',
'}',
`.blocklyWidgetDiv .blocklyMenu.blocklyFocused {
box-shadow: 0 0 6px 1px rgba(0,0,0,.3);
}`,
'.blocklyDropDownDiv .blocklyMenu {',
'background: inherit;', /* Compatibility with gapi, reset from goog-menu */
'border: inherit;', /* Compatibility with gapi, reset from goog-menu */
'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;',
'outline: none;',
'position: relative;', /* Compatibility with gapi, reset from goog-menu */
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
'}',
`.blocklyDropDownDiv .blocklyMenu {
background: inherit;', /* Compatibility with gapi, reset from goog-menu */
border: inherit;', /* Compatibility with gapi, reset from goog-menu */
font: normal 13px "Helvetica Neue", Helvetica, sans-serif;
outline: none;
position: relative;', /* Compatibility with gapi, reset from goog-menu */
z-index: 20000;', /* Arbitrary, but some apps depend on it... */
}`,
/* State: resting. */
'.blocklyMenuItem {',
'border: none;',
'color: #000;',
'cursor: pointer;',
'list-style: none;',
'margin: 0;',
/* 7em on the right for shortcut. */
'min-width: 7em;',
'padding: 6px 15px;',
'white-space: nowrap;',
'}',
`.blocklyMenuItem {
border: none;
color: #000;
cursor: pointer;
list-style: none;
margin: 0;
/* 7em on the right for shortcut. */
min-width: 7em;
padding: 6px 15px;
white-space: nowrap;
}`,
/* State: disabled. */
'.blocklyMenuItemDisabled {',
'color: #ccc;',
'cursor: inherit;',
'}',
`.blocklyMenuItemDisabled {
color: #ccc;
cursor: inherit;
}`,
/* State: hover. */
'.blocklyMenuItemHighlight {',
'background-color: rgba(0,0,0,.1);',
'}',
`.blocklyMenuItemHighlight {
background-color: rgba(0,0,0,.1);
}`,
/* State: selected/checked. */
'.blocklyMenuItemCheckbox {',
'height: 16px;',
'position: absolute;',
'width: 16px;',
'}',
`.blocklyMenuItemCheckbox {
height: 16px;
position: absolute;
width: 16px;
}`,
'.blocklyMenuItemSelected .blocklyMenuItemCheckbox {',
'background: url(<<<PATH>>>/sprites.png) no-repeat -48px -16px;',
'float: left;',
'margin-left: -24px;',
'position: static;', /* Scroll with the menu. */
'}',
`.blocklyMenuItemSelected .blocklyMenuItemCheckbox {
background: url(<<<PATH>>>/sprites.png) no-repeat -48px -16px;
float: left;
margin-left: -24px;
position: static;', /* Scroll with the menu. */
}`,
'.blocklyMenuItemRtl .blocklyMenuItemCheckbox {',
'float: right;',
'margin-right: -24px;',
'}',
/* eslint-enable indent */
`.blocklyMenuItemRtl .blocklyMenuItemCheckbox {
float: right;
margin-right: -24px;
}`,
];
exports.CONTENT = CONTENT;

View File

@@ -12,23 +12,26 @@
'use strict';
goog.provide('Blockly.DeleteArea');
goog.module('Blockly.DeleteArea');
goog.module.declareLegacyNamespace();
goog.require('Blockly.BlockSvg');
goog.require('Blockly.DragTarget');
goog.require('Blockly.IDeleteArea');
goog.requireType('Blockly.IDraggable');
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');
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.requireType('Blockly.IDraggable');
const {inherits} = goog.require('Blockly.utils.object');
/**
* Abstract class for a component that can delete a block or bubble that is
* dropped on top of it.
* @extends {Blockly.DragTarget}
* @implements {Blockly.IDeleteArea}
* @extends {DragTarget}
* @implements {IDeleteArea}
* @constructor
*/
Blockly.DeleteArea = function() {
Blockly.DeleteArea.superClass_.constructor.call(this);
const DeleteArea = function() {
DeleteArea.superClass_.constructor.call(this);
/**
* Whether the last block or bubble dragged over this delete area would be
@@ -39,24 +42,24 @@ Blockly.DeleteArea = function() {
*/
this.wouldDelete_ = false;
};
Blockly.utils.object.inherits(Blockly.DeleteArea, Blockly.DragTarget);
inherits(DeleteArea, DragTarget);
/**
* Returns whether the provided block or bubble would be deleted if dropped on
* this area.
* This method should check if the element is deletable and is always called
* before onDragEnter/onDragOver/onDragExit.
* @param {!Blockly.IDraggable} element The block or bubble currently being
* @param {!IDraggable} element The block or bubble currently being
* dragged.
* @param {boolean} couldConnect Whether the element could could connect to
* another.
* @return {boolean} Whether the element provided would be deleted if dropped on
* this area.
*/
Blockly.DeleteArea.prototype.wouldDelete = function(element, couldConnect) {
if (element instanceof Blockly.BlockSvg) {
var block = /** @type {Blockly.BlockSvg} */ (element);
var couldDeleteBlock = !block.getParent() && block.isDeletable();
DeleteArea.prototype.wouldDelete = function(element, couldConnect) {
if (element instanceof BlockSvg) {
const block = /** @type {BlockSvg} */ (element);
const couldDeleteBlock = !block.getParent() && block.isDeletable();
this.updateWouldDelete_(couldDeleteBlock && !couldConnect);
} else {
this.updateWouldDelete_(element.isDeletable());
@@ -69,6 +72,8 @@ Blockly.DeleteArea.prototype.wouldDelete = function(element, couldConnect) {
* @param {boolean} wouldDelete The new value for the wouldDelete state.
* @protected
*/
Blockly.DeleteArea.prototype.updateWouldDelete_ = function(wouldDelete) {
DeleteArea.prototype.updateWouldDelete_ = function(wouldDelete) {
this.wouldDelete_ = wouldDelete;
};
exports = DeleteArea;

View File

@@ -12,65 +12,68 @@
'use strict';
goog.provide('Blockly.DragTarget');
goog.module('Blockly.DragTarget');
goog.module.declareLegacyNamespace();
goog.require('Blockly.IDragTarget');
goog.requireType('Blockly.IDraggable');
goog.requireType('Blockly.utils.Rect');
/* eslint-disable-next-line no-unused-vars */
const IDragTarget = goog.require('Blockly.IDragTarget');
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.requireType('Blockly.IDraggable');
/* eslint-disable-next-line no-unused-vars */
const Rect = goog.requireType('Blockly.utils.Rect');
/**
* Abstract class for a component with custom behaviour when a block or bubble
* is dragged over or dropped on top of it.
* @implements {Blockly.IDragTarget}
* @implements {IDragTarget}
* @constructor
*/
Blockly.DragTarget = function() {};
const DragTarget = function() {};
/**
* Returns the bounding rectangle of the drag target area in pixel units
* relative to the Blockly injection div.
* @return {?Blockly.utils.Rect} The component's bounding box. Null if drag
* @return {?Rect} The component's bounding box. Null if drag
* target area should be ignored.
*/
Blockly.DragTarget.prototype.getClientRect;
DragTarget.prototype.getClientRect;
/**
* Handles when a cursor with a block or bubble enters this drag target.
* @param {!Blockly.IDraggable} _dragElement The block or bubble currently being
* @param {!IDraggable} _dragElement The block or bubble currently being
* dragged.
*/
Blockly.DragTarget.prototype.onDragEnter = function(_dragElement) {
DragTarget.prototype.onDragEnter = function(_dragElement) {
// no-op
};
/**
* Handles when a cursor with a block or bubble is dragged over this drag
* target.
* @param {!Blockly.IDraggable} _dragElement The block or bubble currently being
* @param {!IDraggable} _dragElement The block or bubble currently being
* dragged.
*/
Blockly.DragTarget.prototype.onDragOver = function(_dragElement) {
DragTarget.prototype.onDragOver = function(_dragElement) {
// no-op
};
/**
* Handles when a cursor with a block or bubble exits this drag target.
* @param {!Blockly.IDraggable} _dragElement The block or bubble currently being
* @param {!IDraggable} _dragElement The block or bubble currently being
* dragged.
*/
Blockly.DragTarget.prototype.onDragExit = function(_dragElement) {
DragTarget.prototype.onDragExit = function(_dragElement) {
// no-op
};
/**
* Handles when a block or bubble is dropped on this component.
* Should not handle delete here.
* @param {!Blockly.IDraggable} _dragElement The block or bubble currently being
* @param {!IDraggable} _dragElement The block or bubble currently being
* dragged.
*/
Blockly.DragTarget.prototype.onDrop = function(_dragElement) {
DragTarget.prototype.onDrop = function(_dragElement) {
// no-op
};
@@ -78,11 +81,13 @@ Blockly.DragTarget.prototype.onDrop = function(_dragElement) {
* Returns whether the provided block or bubble should not be moved after being
* dropped on this component. If true, the element will return to where it was
* when the drag started.
* @param {!Blockly.IDraggable} _dragElement The block or bubble currently being
* @param {!IDraggable} _dragElement The block or bubble currently being
* dragged.
* @return {boolean} Whether the block or bubble provided should be returned to
* drag start.
*/
Blockly.DragTarget.prototype.shouldPreventMove = function(_dragElement) {
DragTarget.prototype.shouldPreventMove = function(_dragElement) {
return false;
};
exports = DragTarget;

View File

@@ -17,18 +17,20 @@
* @name Blockly.Extensions
* @namespace
*/
goog.provide('Blockly.Extensions');
goog.module('Blockly.Extensions');
goog.module.declareLegacyNamespace();
goog.require('Blockly.utils');
goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
const {checkMessageReferences, replaceMessageReferences, runAfterPageLoad} = goog.require('Blockly.utils');
/**
* The set of all registered extensions, keyed by extension name/id.
* @private
*/
Blockly.Extensions.ALL_ = Object.create(null);
const allExtensions = Object.create(null);
exports.ALL_ = allExtensions;
/**
* Registers a new extension function. Extensions are functions that help
@@ -40,18 +42,19 @@ Blockly.Extensions.ALL_ = Object.create(null);
* @throws {Error} if the extension name is empty, the extension is already
* registered, or extensionFn is not a function.
*/
Blockly.Extensions.register = function(name, initFn) {
const register = function(name, initFn) {
if ((typeof name != 'string') || (name.trim() == '')) {
throw Error('Error: Invalid extension name "' + name + '"');
}
if (Blockly.Extensions.ALL_[name]) {
if (allExtensions[name]) {
throw Error('Error: Extension "' + name + '" is already registered.');
}
if (typeof initFn != 'function') {
throw Error('Error: Extension "' + name + '" must be a function');
}
Blockly.Extensions.ALL_[name] = initFn;
allExtensions[name] = initFn;
};
exports.register = register;
/**
* Registers a new extension function that adds all key/value of mixinObj.
@@ -60,14 +63,15 @@ Blockly.Extensions.register = function(name, initFn) {
* @throws {Error} if the extension name is empty or the extension is already
* registered.
*/
Blockly.Extensions.registerMixin = function(name, mixinObj) {
const registerMixin = function(name, mixinObj) {
if (!mixinObj || typeof mixinObj != 'object') {
throw Error('Error: Mixin "' + name + '" must be a object');
}
Blockly.Extensions.register(name, function() {
register(name, function() {
this.mixin(mixinObj);
});
};
exports.registerMixin = registerMixin;
/**
* Registers a new extension function that adds a mutator to the block.
@@ -82,25 +86,21 @@ Blockly.Extensions.registerMixin = function(name, mixinObj) {
* flyout of the mutator dialog.
* @throws {Error} if the mutation is invalid or can't be applied to the block.
*/
Blockly.Extensions.registerMutator = function(name, mixinObj, opt_helperFn,
opt_blockList) {
var errorPrefix = 'Error when registering mutator "' + name + '": ';
const registerMutator = function(name, mixinObj, opt_helperFn, opt_blockList) {
const errorPrefix = 'Error when registering mutator "' + name + '": ';
// Sanity check the mixin object before registering it.
Blockly.Extensions.checkHasFunction_(
errorPrefix, mixinObj.domToMutation, 'domToMutation');
Blockly.Extensions.checkHasFunction_(
errorPrefix, mixinObj.mutationToDom, 'mutationToDom');
checkHasFunction(errorPrefix, mixinObj.domToMutation, 'domToMutation');
checkHasFunction(errorPrefix, mixinObj.mutationToDom, 'mutationToDom');
var hasMutatorDialog =
Blockly.Extensions.checkMutatorDialog_(mixinObj, errorPrefix);
const hasMutatorDialog = checkMutatorDialog(mixinObj, errorPrefix);
if (opt_helperFn && (typeof opt_helperFn != 'function')) {
throw Error('Extension "' + name + '" is not a function');
}
// Sanity checks passed.
Blockly.Extensions.register(name, function() {
register(name, function() {
if (hasMutatorDialog) {
if (!Blockly.Mutator) {
throw Error(errorPrefix + 'Missing require for Blockly.Mutator');
@@ -115,54 +115,59 @@ Blockly.Extensions.registerMutator = function(name, mixinObj, opt_helperFn,
}
});
};
exports.registerMutator = registerMutator;
/**
* Unregisters the extension registered with the given name.
* @param {string} name The name of the extension to unregister.
*/
Blockly.Extensions.unregister = function(name) {
if (Blockly.Extensions.ALL_[name]) {
delete Blockly.Extensions.ALL_[name];
const unregister = function(name) {
if (allExtensions[name]) {
delete allExtensions[name];
} else {
console.warn('No extension mapping for name "' + name +
'" found to unregister');
console.warn(
'No extension mapping for name "' + name + '" found to unregister');
}
};
exports.unregister = unregister;
/**
* Applies an extension method to a block. This should only be called during
* block construction.
* @param {string} name The name of the extension.
* @param {!Blockly.Block} block The block to apply the named extension to.
* @param {!Block} block The block to apply the named extension to.
* @param {boolean} isMutator True if this extension defines a mutator.
* @throws {Error} if the extension is not found.
*/
Blockly.Extensions.apply = function(name, block, isMutator) {
var extensionFn = Blockly.Extensions.ALL_[name];
const apply = function(name, block, isMutator) {
const extensionFn = allExtensions[name];
if (typeof extensionFn != 'function') {
throw Error('Error: Extension "' + name + '" not found.');
}
let mutatorProperties;
if (isMutator) {
// Fail early if the block already has mutation properties.
Blockly.Extensions.checkNoMutatorProperties_(name, block);
checkNoMutatorProperties(name, block);
} else {
// Record the old properties so we can make sure they don't change after
// applying the extension.
var mutatorProperties = Blockly.Extensions.getMutatorProperties_(block);
mutatorProperties = getMutatorProperties(block);
}
extensionFn.apply(block);
if (isMutator) {
var errorPrefix = 'Error after applying mutator "' + name + '": ';
Blockly.Extensions.checkBlockHasMutatorProperties_(errorPrefix, block);
const errorPrefix = 'Error after applying mutator "' + name + '": ';
checkBlockHasMutatorProperties(errorPrefix, block);
} else {
if (!Blockly.Extensions.mutatorPropertiesMatch_(
/** @type {!Array<Object>} */ (mutatorProperties), block)) {
throw Error('Error when applying extension "' + name + '": ' +
if (!mutatorPropertiesMatch(
/** @type {!Array<Object>} */ (mutatorProperties), block)) {
throw Error(
'Error when applying extension "' + name + '": ' +
'mutation properties changed when applying a non-mutator extension.');
}
}
};
exports.apply = apply;
/**
* Check that the given value is a function.
@@ -172,14 +177,14 @@ Blockly.Extensions.apply = function(name, block, isMutator) {
* @throws {Error} if the property does not exist or is not a function.
* @private
*/
Blockly.Extensions.checkHasFunction_ = function(errorPrefix, func,
propertyName) {
const checkHasFunction = function(errorPrefix, func, propertyName) {
if (!func) {
throw Error(errorPrefix +
'missing required property "' + propertyName + '"');
throw Error(
errorPrefix + 'missing required property "' + propertyName + '"');
} else if (typeof func != 'function') {
throw Error(errorPrefix +
'" required property "' + propertyName + '" must be a function');
throw Error(
errorPrefix + '" required property "' + propertyName +
'" must be a function');
}
};
@@ -189,14 +194,15 @@ Blockly.Extensions.checkHasFunction_ = function(errorPrefix, func,
* extension to a block, to make sure we are not overwriting properties.
* @param {string} mutationName The name of the mutation to reference in error
* messages.
* @param {!Blockly.Block} block The block to check.
* @param {!Block} block The block to check.
* @throws {Error} if any of the properties already exist on the block.
* @private
*/
Blockly.Extensions.checkNoMutatorProperties_ = function(mutationName, block) {
var properties = Blockly.Extensions.getMutatorProperties_(block);
const checkNoMutatorProperties = function(mutationName, block) {
const properties = getMutatorProperties(block);
if (properties.length) {
throw Error('Error: tried to apply mutation "' + mutationName +
throw Error(
'Error: tried to apply mutation "' + mutationName +
'" to a block that already has mutator functions.' +
' Block id: ' + block.id);
}
@@ -214,9 +220,9 @@ Blockly.Extensions.checkNoMutatorProperties_ = function(mutationName, block) {
* @throws {Error} if the object has only one of the functions.
* @private
*/
Blockly.Extensions.checkMutatorDialog_ = function(object, errorPrefix) {
var hasCompose = object.compose !== undefined;
var hasDecompose = object.decompose !== undefined;
const checkMutatorDialog = function(object, errorPrefix) {
const hasCompose = object.compose !== undefined;
const hasDecompose = object.decompose !== undefined;
if (hasCompose && hasDecompose) {
if (typeof object.compose != 'function') {
@@ -228,19 +234,18 @@ Blockly.Extensions.checkMutatorDialog_ = function(object, errorPrefix) {
} else if (!hasCompose && !hasDecompose) {
return false;
}
throw Error(errorPrefix +
'Must have both or neither of "compose" and "decompose"');
throw Error(
errorPrefix + 'Must have both or neither of "compose" and "decompose"');
};
/**
* Check that a block has required mutator properties. This should be called
* after applying a mutation extension.
* @param {string} errorPrefix The string to prepend to any error message.
* @param {!Blockly.Block} block The block to inspect.
* @param {!Block} block The block to inspect.
* @private
*/
Blockly.Extensions.checkBlockHasMutatorProperties_ = function(errorPrefix,
block) {
const checkBlockHasMutatorProperties = function(errorPrefix, block) {
if (typeof block.domToMutation != 'function') {
throw Error(errorPrefix + 'Applying a mutator didn\'t add "domToMutation"');
}
@@ -250,18 +255,18 @@ Blockly.Extensions.checkBlockHasMutatorProperties_ = function(errorPrefix,
// A block with a mutator isn't required to have a mutation dialog, but
// it should still have both or neither of compose and decompose.
Blockly.Extensions.checkMutatorDialog_(block, errorPrefix);
checkMutatorDialog(block, errorPrefix);
};
/**
* Get a list of values of mutator properties on the given block.
* @param {!Blockly.Block} block The block to inspect.
* @param {!Block} block The block to inspect.
* @return {!Array<Object>} A list with all of the defined properties, which
* should be functions, but may be anything other than undefined.
* @private
*/
Blockly.Extensions.getMutatorProperties_ = function(block) {
var result = [];
const getMutatorProperties = function(block) {
const result = [];
// List each function explicitly by reference to allow for renaming
// during compilation.
if (block.domToMutation !== undefined) {
@@ -284,16 +289,16 @@ Blockly.Extensions.getMutatorProperties_ = function(block) {
* properties. This should be called after applying a non-mutator extension,
* to verify that the extension didn't change properties it shouldn't.
* @param {!Array<Object>} oldProperties The old values to compare to.
* @param {!Blockly.Block} block The block to inspect for new values.
* @param {!Block} block The block to inspect for new values.
* @return {boolean} True if the property lists match.
* @private
*/
Blockly.Extensions.mutatorPropertiesMatch_ = function(oldProperties, block) {
var newProperties = Blockly.Extensions.getMutatorProperties_(block);
const mutatorPropertiesMatch = function(oldProperties, block) {
const newProperties = getMutatorProperties(block);
if (newProperties.length != oldProperties.length) {
return false;
}
for (var i = 0; i < newProperties.length; i++) {
for (let i = 0; i < newProperties.length; i++) {
if (oldProperties[i] != newProperties[i]) {
return false;
}
@@ -320,76 +325,75 @@ Blockly.Extensions.mutatorPropertiesMatch_ = function(oldProperties, block) {
* tooltip text.
* @return {!Function} The extension function.
*/
Blockly.Extensions.buildTooltipForDropdown = function(dropdownName,
lookupTable) {
const buildTooltipForDropdown = function(dropdownName, lookupTable) {
// List of block types already validated, to minimize duplicate warnings.
var blockTypesChecked = [];
const blockTypesChecked = [];
// 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.
if (typeof document == 'object') { // Relies on document.readyState
Blockly.utils.runAfterPageLoad(function() {
for (var key in lookupTable) {
runAfterPageLoad(function() {
for (let key in lookupTable) {
// Will print warnings if reference is missing.
Blockly.utils.checkMessageReferences(lookupTable[key]);
checkMessageReferences(lookupTable[key]);
}
});
}
/**
* The actual extension.
* @this {Blockly.Block}
* @this {Block}
*/
var extensionFn = function() {
const extensionFn = function() {
if (this.type && blockTypesChecked.indexOf(this.type) == -1) {
Blockly.Extensions.checkDropdownOptionsInTable_(
this, dropdownName, lookupTable);
checkDropdownOptionsInTable(this, dropdownName, lookupTable);
blockTypesChecked.push(this.type);
}
this.setTooltip(function() {
var value = String(this.getFieldValue(dropdownName));
var tooltip = lookupTable[value];
const value = String(this.getFieldValue(dropdownName));
let tooltip = lookupTable[value];
if (tooltip == null) {
if (blockTypesChecked.indexOf(this.type) == -1) {
// Warn for missing values on generated tooltips.
var warning = 'No tooltip mapping for value ' + value +
' of field ' + dropdownName;
let warning = 'No tooltip mapping for value ' + value + ' of field ' +
dropdownName;
if (this.type != null) {
warning += (' of block type ' + this.type);
}
console.warn(warning + '.');
}
} else {
tooltip = Blockly.utils.replaceMessageReferences(tooltip);
tooltip = replaceMessageReferences(tooltip);
}
return tooltip;
}.bind(this));
};
return extensionFn;
};
exports.buildTooltipForDropdown = buildTooltipForDropdown;
/**
* Checks all options keys are present in the provided string lookup table.
* Emits console warnings when they are not.
* @param {!Blockly.Block} block The block containing the dropdown
* @param {!Block} block The block containing the dropdown
* @param {string} dropdownName The name of the dropdown
* @param {!Object<string, string>} lookupTable The string lookup table
* @private
*/
Blockly.Extensions.checkDropdownOptionsInTable_ = function(block, dropdownName,
lookupTable) {
const checkDropdownOptionsInTable = function(block, dropdownName, lookupTable) {
// Validate all dropdown options have values.
var dropdown = block.getField(dropdownName);
const dropdown = block.getField(dropdownName);
if (!dropdown.isOptionListDynamic()) {
var options = dropdown.getOptions();
for (var i = 0; i < options.length; ++i) {
var optionKey = options[i][1]; // label, then value
const options = dropdown.getOptions();
for (let i = 0; i < options.length; ++i) {
const optionKey = options[i][1]; // label, then value
if (lookupTable[optionKey] == null) {
console.warn('No tooltip mapping for value ' + optionKey +
' of field ' + dropdownName + ' of block type ' + block.type);
console.warn(
'No tooltip mapping for value ' + optionKey + ' of field ' +
dropdownName + ' of block type ' + block.type);
}
}
}
@@ -404,48 +408,47 @@ Blockly.Extensions.checkDropdownOptionsInTable_ = function(block, dropdownName,
* @param {string} fieldName The field with the replacement text.
* @return {!Function} The extension function.
*/
Blockly.Extensions.buildTooltipWithFieldText = function(msgTemplate,
fieldName) {
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.
if (typeof document == 'object') { // Relies on document.readyState
Blockly.utils.runAfterPageLoad(function() {
runAfterPageLoad(function() {
// Will print warnings if reference is missing.
Blockly.utils.checkMessageReferences(msgTemplate);
checkMessageReferences(msgTemplate);
});
}
/**
* The actual extension.
* @this {Blockly.Block}
* @this {Block}
*/
var extensionFn = function() {
const extensionFn = function() {
this.setTooltip(function() {
var field = this.getField(fieldName);
return Blockly.utils.replaceMessageReferences(msgTemplate)
const field = this.getField(fieldName);
return replaceMessageReferences(msgTemplate)
.replace('%1', field ? field.getText() : '');
}.bind(this));
};
return extensionFn;
};
exports.buildTooltipWithFieldText = buildTooltipWithFieldText;
/**
* Configures the tooltip to mimic the parent block when connected. Otherwise,
* uses the tooltip text at the time this extension is initialized. This takes
* advantage of the fact that all other values from JSON are initialized before
* extensions.
* @this {Blockly.Block}
* @this {Block}
* @private
*/
Blockly.Extensions.extensionParentTooltip_ = function() {
this.tooltipWhenNotConnected_ = this.tooltip;
const extensionParentTooltip = function() {
this.tooltipWhenNotConnected = this.tooltip;
this.setTooltip(function() {
var parent = this.getParent();
const parent = this.getParent();
return (parent && parent.getInputsInline() && parent.tooltip) ||
this.tooltipWhenNotConnected_;
this.tooltipWhenNotConnected;
}.bind(this));
};
Blockly.Extensions.register('parent_tooltip_when_inline',
Blockly.Extensions.extensionParentTooltip_);
register('parent_tooltip_when_inline', extensionParentTooltip);

File diff suppressed because it is too large Load Diff

View File

@@ -10,14 +10,15 @@
*/
'use strict';
goog.provide('Blockly.FieldCheckbox');
goog.module('Blockly.FieldCheckbox');
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');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
/**
@@ -29,12 +30,13 @@ goog.require('Blockly.utils.object');
* returns a validated value ('TRUE' or 'FALSE'), or null to abort the
* change.
* @param {Object=} opt_config A map of options used to configure the field.
* See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/checkbox#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/checkbox#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.Field}
* @extends {Field}
* @constructor
*/
Blockly.FieldCheckbox = function(opt_value, opt_validator, opt_config) {
const FieldCheckbox = function(opt_value, opt_validator, opt_config) {
/**
* Character for the check mark. Used to apply a different check mark
* character to individual fields.
@@ -43,26 +45,26 @@ Blockly.FieldCheckbox = function(opt_value, opt_validator, opt_config) {
*/
this.checkChar_ = null;
Blockly.FieldCheckbox.superClass_.constructor.call(
FieldCheckbox.superClass_.constructor.call(
this, opt_value, opt_validator, opt_config);
};
Blockly.utils.object.inherits(Blockly.FieldCheckbox, Blockly.Field);
inherits(FieldCheckbox, Field);
/**
* The default value for this field.
* @type {*}
* @protected
*/
Blockly.FieldCheckbox.prototype.DEFAULT_VALUE = false;
FieldCheckbox.prototype.DEFAULT_VALUE = false;
/**
* Construct a FieldCheckbox from a JSON arg object.
* @param {!Object} options A JSON object with options (checked).
* @return {!Blockly.FieldCheckbox} The new field instance.
* @return {!FieldCheckbox} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldCheckbox.fromJson = function(options) {
FieldCheckbox.fromJson = function(options) {
// `this` might be a subclass of FieldCheckbox if that class doesn't override
// the static fromJson method.
return new this(options['checked'], undefined, options);
@@ -73,19 +75,19 @@ Blockly.FieldCheckbox.fromJson = function(options) {
* @type {string}
* @const
*/
Blockly.FieldCheckbox.CHECK_CHAR = '\u2713';
FieldCheckbox.CHECK_CHAR = '\u2713';
/**
* Serializable fields are saved by the XML renderer, non-serializable fields
* are not. Editable fields should also be serializable.
* @type {boolean}
*/
Blockly.FieldCheckbox.prototype.SERIALIZABLE = true;
FieldCheckbox.prototype.SERIALIZABLE = true;
/**
* Mouse cursor style when over the hotspot that initiates editability.
*/
Blockly.FieldCheckbox.prototype.CURSOR = 'default';
FieldCheckbox.prototype.CURSOR = 'default';
/**
* Configure the field based on the given map of options.
@@ -93,8 +95,8 @@ Blockly.FieldCheckbox.prototype.CURSOR = 'default';
* @protected
* @override
*/
Blockly.FieldCheckbox.prototype.configure_ = function(config) {
Blockly.FieldCheckbox.superClass_.configure_.call(this, config);
FieldCheckbox.prototype.configure_ = function(config) {
FieldCheckbox.superClass_.configure_.call(this, config);
if (config['checkCharacter']) {
this.checkChar_ = config['checkCharacter'];
}
@@ -104,10 +106,10 @@ Blockly.FieldCheckbox.prototype.configure_ = function(config) {
* Create the block UI for this checkbox.
* @package
*/
Blockly.FieldCheckbox.prototype.initView = function() {
Blockly.FieldCheckbox.superClass_.initView.call(this);
FieldCheckbox.prototype.initView = function() {
FieldCheckbox.superClass_.initView.call(this);
Blockly.utils.dom.addClass(
dom.addClass(
/** @type {!SVGTextElement} **/ (this.textElement_), 'blocklyCheckbox');
this.textElement_.style.display = this.value_ ? 'block' : 'none';
};
@@ -115,7 +117,7 @@ Blockly.FieldCheckbox.prototype.initView = function() {
/**
* @override
*/
Blockly.FieldCheckbox.prototype.render_ = function() {
FieldCheckbox.prototype.render_ = function() {
if (this.textContent_) {
this.textContent_.nodeValue = this.getDisplayText_();
}
@@ -125,8 +127,8 @@ Blockly.FieldCheckbox.prototype.render_ = function() {
/**
* @override
*/
Blockly.FieldCheckbox.prototype.getDisplayText_ = function() {
return this.checkChar_ || Blockly.FieldCheckbox.CHECK_CHAR;
FieldCheckbox.prototype.getDisplayText_ = function() {
return this.checkChar_ || FieldCheckbox.CHECK_CHAR;
};
/**
@@ -134,7 +136,7 @@ Blockly.FieldCheckbox.prototype.getDisplayText_ = function() {
* @param {?string} character The character to use for the check mark, or
* null to use the default.
*/
Blockly.FieldCheckbox.prototype.setCheckCharacter = function(character) {
FieldCheckbox.prototype.setCheckCharacter = function(character) {
this.checkChar_ = character;
this.forceRerender();
};
@@ -143,7 +145,7 @@ Blockly.FieldCheckbox.prototype.setCheckCharacter = function(character) {
* Toggle the state of the checkbox on click.
* @protected
*/
Blockly.FieldCheckbox.prototype.showEditor_ = function() {
FieldCheckbox.prototype.showEditor_ = function() {
this.setValue(!this.value_);
};
@@ -153,7 +155,7 @@ Blockly.FieldCheckbox.prototype.showEditor_ = function() {
* @return {?string} A valid value ('TRUE' or 'FALSE), or null if invalid.
* @protected
*/
Blockly.FieldCheckbox.prototype.doClassValidation_ = function(opt_newValue) {
FieldCheckbox.prototype.doClassValidation_ = function(opt_newValue) {
if (opt_newValue === true || opt_newValue === 'TRUE') {
return 'TRUE';
}
@@ -169,7 +171,7 @@ Blockly.FieldCheckbox.prototype.doClassValidation_ = function(opt_newValue) {
* that this is a either 'TRUE' or 'FALSE'.
* @protected
*/
Blockly.FieldCheckbox.prototype.doValueUpdate_ = function(newValue) {
FieldCheckbox.prototype.doValueUpdate_ = function(newValue) {
this.value_ = this.convertValueToBool_(newValue);
// Update visual.
if (this.textElement_) {
@@ -181,7 +183,7 @@ Blockly.FieldCheckbox.prototype.doValueUpdate_ = function(newValue) {
* Get the value of this field, either 'TRUE' or 'FALSE'.
* @return {string} The value of this field.
*/
Blockly.FieldCheckbox.prototype.getValue = function() {
FieldCheckbox.prototype.getValue = function() {
return this.value_ ? 'TRUE' : 'FALSE';
};
@@ -189,7 +191,7 @@ Blockly.FieldCheckbox.prototype.getValue = function() {
* Get the boolean value of this field.
* @return {boolean} The boolean value of this field.
*/
Blockly.FieldCheckbox.prototype.getValueBoolean = function() {
FieldCheckbox.prototype.getValueBoolean = function() {
return /** @type {boolean} */ (this.value_);
};
@@ -198,7 +200,7 @@ Blockly.FieldCheckbox.prototype.getValueBoolean = function() {
* @return {string} Text representing the value of this field
* ('true' or 'false').
*/
Blockly.FieldCheckbox.prototype.getText = function() {
FieldCheckbox.prototype.getText = function() {
return String(this.convertValueToBool_(this.value_));
};
@@ -211,7 +213,7 @@ Blockly.FieldCheckbox.prototype.getText = function() {
* @return {boolean} The converted value.
* @private
*/
Blockly.FieldCheckbox.prototype.convertValueToBool_ = function(value) {
FieldCheckbox.prototype.convertValueToBool_ = function(value) {
if (typeof value == 'string') {
return value == 'TRUE';
} else {
@@ -219,4 +221,6 @@ Blockly.FieldCheckbox.prototype.convertValueToBool_ = function(value) {
}
};
Blockly.fieldRegistry.register('field_checkbox', Blockly.FieldCheckbox);
fieldRegistry.register('field_checkbox', FieldCheckbox);
exports = FieldCheckbox;

View File

@@ -10,22 +10,24 @@
*/
'use strict';
goog.provide('Blockly.FieldColour');
goog.module('Blockly.FieldColour');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.Css');
goog.require('Blockly.DropDownDiv');
const Css = goog.require('Blockly.Css');
const DropDownDiv = goog.require('Blockly.DropDownDiv');
const Field = goog.require('Blockly.Field');
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 colour = goog.require('Blockly.utils.colour');
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');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.colour');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.IdGenerator');
goog.require('Blockly.utils.KeyCodes');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Size');
/**
@@ -34,15 +36,17 @@ goog.require('Blockly.utils.Size');
* '#rrggbb' format. Defaults to the first value in the default colour array.
* @param {Function=} opt_validator A function that is called to validate
* changes to the field's value. Takes in a colour string & returns a
* validated colour string ('#rrggbb' format), or null to abort the change.
* validated colour string ('#rrggbb' format), or null to abort the
* change.Blockly.
* @param {Object=} opt_config A map of options used to configure the field.
* See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/colour}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/colour}
* for a list of properties this parameter supports.
* @extends {Blockly.Field}
* @extends {Field}
* @constructor
*/
Blockly.FieldColour = function(opt_value, opt_validator, opt_config) {
Blockly.FieldColour.superClass_.constructor.call(
const FieldColour = function(opt_value, opt_validator, opt_config) {
FieldColour.superClass_.constructor.call(
this, opt_value, opt_validator, opt_config);
/**
@@ -61,49 +65,49 @@ Blockly.FieldColour = function(opt_value, opt_validator, opt_config) {
/**
* Mouse click event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onClickWrapper_ = null;
/**
* Mouse move event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onMouseMoveWrapper_ = null;
/**
* Mouse enter event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onMouseEnterWrapper_ = null;
/**
* Mouse leave event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onMouseLeaveWrapper_ = null;
/**
* Key down event data.
* @type {?Blockly.browserEvents.Data}
* @type {?Data}
* @private
*/
this.onKeyDownWrapper_ = null;
};
Blockly.utils.object.inherits(Blockly.FieldColour, Blockly.Field);
inherits(FieldColour, Field);
/**
* Construct a FieldColour from a JSON arg object.
* @param {!Object} options A JSON object with options (colour).
* @return {!Blockly.FieldColour} The new field instance.
* @return {!FieldColour} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldColour.fromJson = function(options) {
FieldColour.fromJson = function(options) {
// `this` might be a subclass of FieldColour if that class doesn't override
// the static fromJson method.
return new this(options['colour'], undefined, options);
@@ -114,12 +118,12 @@ Blockly.FieldColour.fromJson = function(options) {
* are not. Editable fields should also be serializable.
* @type {boolean}
*/
Blockly.FieldColour.prototype.SERIALIZABLE = true;
FieldColour.prototype.SERIALIZABLE = true;
/**
* Mouse cursor style when over the hotspot that initiates the editor.
*/
Blockly.FieldColour.prototype.CURSOR = 'default';
FieldColour.prototype.CURSOR = 'default';
/**
* Used to tell if the field needs to be rendered the next time the block is
@@ -128,21 +132,21 @@ Blockly.FieldColour.prototype.CURSOR = 'default';
* @type {boolean}
* @protected
*/
Blockly.FieldColour.prototype.isDirty_ = false;
FieldColour.prototype.isDirty_ = false;
/**
* Array of colours used by this field. If null, use the global list.
* @type {Array<string>}
* @private
*/
Blockly.FieldColour.prototype.colours_ = null;
FieldColour.prototype.colours_ = null;
/**
* Array of colour tooltips used by this field. If null, use the global list.
* @type {Array<string>}
* @private
*/
Blockly.FieldColour.prototype.titles_ = null;
FieldColour.prototype.titles_ = null;
/**
* Number of colour columns used by this field. If 0, use the global setting.
@@ -150,7 +154,7 @@ Blockly.FieldColour.prototype.titles_ = null;
* @type {number}
* @private
*/
Blockly.FieldColour.prototype.columns_ = 0;
FieldColour.prototype.columns_ = 0;
/**
* Configure the field based on the given map of options.
@@ -158,8 +162,8 @@ Blockly.FieldColour.prototype.columns_ = 0;
* @protected
* @override
*/
Blockly.FieldColour.prototype.configure_ = function(config) {
Blockly.FieldColour.superClass_.configure_.call(this, config);
FieldColour.prototype.configure_ = function(config) {
FieldColour.superClass_.configure_.call(this, config);
if (config['colourOptions']) {
this.colours_ = config['colourOptions'];
this.titles_ = config['colourTitles'];
@@ -173,8 +177,8 @@ Blockly.FieldColour.prototype.configure_ = function(config) {
* Create the block UI for this colour field.
* @package
*/
Blockly.FieldColour.prototype.initView = function() {
this.size_ = new Blockly.utils.Size(
FieldColour.prototype.initView = function() {
this.size_ = new Size(
this.getConstants().FIELD_COLOUR_DEFAULT_WIDTH,
this.getConstants().FIELD_COLOUR_DEFAULT_HEIGHT);
if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) {
@@ -188,7 +192,7 @@ Blockly.FieldColour.prototype.initView = function() {
/**
* @override
*/
Blockly.FieldColour.prototype.applyColour = function() {
FieldColour.prototype.applyColour = function() {
if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) {
if (this.borderRect_) {
this.borderRect_.style.fill = /** @type {string} */ (this.getValue());
@@ -205,11 +209,11 @@ Blockly.FieldColour.prototype.applyColour = function() {
* @return {?string} A valid colour, or null if invalid.
* @protected
*/
Blockly.FieldColour.prototype.doClassValidation_ = function(opt_newValue) {
FieldColour.prototype.doClassValidation_ = function(opt_newValue) {
if (typeof opt_newValue != 'string') {
return null;
}
return Blockly.utils.colour.parse(opt_newValue);
return colour.parse(opt_newValue);
};
/**
@@ -218,7 +222,7 @@ Blockly.FieldColour.prototype.doClassValidation_ = function(opt_newValue) {
* that this is a colour in '#rrggbb' format.
* @protected
*/
Blockly.FieldColour.prototype.doValueUpdate_ = function(newValue) {
FieldColour.prototype.doValueUpdate_ = function(newValue) {
this.value_ = newValue;
if (this.borderRect_) {
this.borderRect_.style.fill = /** @type {string} */ (newValue);
@@ -232,8 +236,8 @@ Blockly.FieldColour.prototype.doValueUpdate_ = function(newValue) {
* Get the text for this field. Used when the block is collapsed.
* @return {string} Text representing the value of this field.
*/
Blockly.FieldColour.prototype.getText = function() {
var colour = /** @type {string} */ (this.value_);
FieldColour.prototype.getText = function() {
let colour = /** @type {string} */ (this.value_);
// Try to use #rgb format if possible, rather than #rrggbb.
if (/^#(.)\1(.)\2(.)\3$/.test(colour)) {
colour = '#' + colour[1] + colour[3] + colour[5];
@@ -247,7 +251,7 @@ Blockly.FieldColour.prototype.getText = function() {
* All colour pickers use this unless overridden with setColours.
* @type {!Array<string>}
*/
Blockly.FieldColour.COLOURS = [
FieldColour.COLOURS = [
// grays
'#ffffff', '#cccccc', '#c0c0c0', '#999999', '#666666', '#333333', '#000000',
// reds
@@ -275,7 +279,7 @@ Blockly.FieldColour.COLOURS = [
* @type {*}
* @protected
*/
Blockly.FieldColour.prototype.DEFAULT_VALUE = Blockly.FieldColour.COLOURS[0];
FieldColour.prototype.DEFAULT_VALUE = FieldColour.COLOURS[0];
/**
* An array of tooltip strings for the palette. If not the same length as
@@ -283,23 +287,23 @@ Blockly.FieldColour.prototype.DEFAULT_VALUE = Blockly.FieldColour.COLOURS[0];
* All colour pickers use this unless overridden with setColours.
* @type {!Array<string>}
*/
Blockly.FieldColour.TITLES = [];
FieldColour.TITLES = [];
/**
* Number of columns in the palette.
* All colour pickers use this unless overridden with setColumns.
*/
Blockly.FieldColour.COLUMNS = 7;
FieldColour.COLUMNS = 7;
/**
* Set a custom colour grid for this field.
* @param {Array<string>} colours Array of colours for this block,
* or null to use default (Blockly.FieldColour.COLOURS).
* or null to use default (FieldColour.COLOURS).
* @param {Array<string>=} opt_titles Optional array of colour tooltips,
* or null to use default (Blockly.FieldColour.TITLES).
* @return {!Blockly.FieldColour} Returns itself (for method chaining).
* or null to use default (FieldColour.TITLES).
* @return {!FieldColour} Returns itself (for method chaining).
*/
Blockly.FieldColour.prototype.setColours = function(colours, opt_titles) {
FieldColour.prototype.setColours = function(colours, opt_titles) {
this.colours_ = colours;
if (opt_titles) {
this.titles_ = opt_titles;
@@ -310,10 +314,10 @@ Blockly.FieldColour.prototype.setColours = function(colours, opt_titles) {
/**
* Set a custom grid size for this field.
* @param {number} columns Number of columns for this block,
* or 0 to use default (Blockly.FieldColour.COLUMNS).
* @return {!Blockly.FieldColour} Returns itself (for method chaining).
* or 0 to use default (FieldColour.COLUMNS).
* @return {!FieldColour} Returns itself (for method chaining).
*/
Blockly.FieldColour.prototype.setColumns = function(columns) {
FieldColour.prototype.setColumns = function(columns) {
this.columns_ = columns;
return this;
};
@@ -322,15 +326,14 @@ Blockly.FieldColour.prototype.setColumns = function(columns) {
* Create and show the colour field's editor.
* @protected
*/
Blockly.FieldColour.prototype.showEditor_ = function() {
FieldColour.prototype.showEditor_ = function() {
this.dropdownCreate_();
Blockly.DropDownDiv.getContentDiv().appendChild(this.picker_);
DropDownDiv.getContentDiv().appendChild(this.picker_);
Blockly.DropDownDiv.showPositionedByField(
this, this.dropdownDispose_.bind(this));
DropDownDiv.showPositionedByField(this, this.dropdownDispose_.bind(this));
// Focus so we can start receiving keyboard events.
this.picker_.focus({preventScroll:true});
this.picker_.focus({preventScroll: true});
};
/**
@@ -338,12 +341,12 @@ Blockly.FieldColour.prototype.showEditor_ = function() {
* @param {!MouseEvent} e Mouse event.
* @private
*/
Blockly.FieldColour.prototype.onClick_ = function(e) {
var cell = /** @type {!Element} */ (e.target);
var colour = cell && cell.label;
FieldColour.prototype.onClick_ = function(e) {
const cell = /** @type {!Element} */ (e.target);
const colour = cell && cell.label;
if (colour !== null) {
this.setValue(colour);
Blockly.DropDownDiv.hideIfOwner(this);
DropDownDiv.hideIfOwner(this);
}
};
@@ -353,30 +356,30 @@ Blockly.FieldColour.prototype.onClick_ = function(e) {
* @param {!KeyboardEvent} e Keyboard event.
* @private
*/
Blockly.FieldColour.prototype.onKeyDown_ = function(e) {
var handled = false;
if (e.keyCode === Blockly.utils.KeyCodes.UP) {
FieldColour.prototype.onKeyDown_ = function(e) {
let handled = false;
if (e.keyCode === KeyCodes.UP) {
this.moveHighlightBy_(0, -1);
handled = true;
} else if (e.keyCode === Blockly.utils.KeyCodes.DOWN) {
} else if (e.keyCode === KeyCodes.DOWN) {
this.moveHighlightBy_(0, 1);
handled = true;
} else if (e.keyCode === Blockly.utils.KeyCodes.LEFT) {
} else if (e.keyCode === KeyCodes.LEFT) {
this.moveHighlightBy_(-1, 0);
handled = true;
} else if (e.keyCode === Blockly.utils.KeyCodes.RIGHT) {
} else if (e.keyCode === KeyCodes.RIGHT) {
this.moveHighlightBy_(1, 0);
handled = true;
} else if (e.keyCode === Blockly.utils.KeyCodes.ENTER) {
} else if (e.keyCode === KeyCodes.ENTER) {
// Select the highlighted colour.
var highlighted = this.getHighlighted_();
const highlighted = this.getHighlighted_();
if (highlighted) {
var colour = highlighted && highlighted.label;
const colour = highlighted && highlighted.label;
if (colour !== null) {
this.setValue(colour);
}
}
Blockly.DropDownDiv.hideWithoutAnimation();
DropDownDiv.hideWithoutAnimation();
handled = true;
}
if (handled) {
@@ -390,13 +393,13 @@ Blockly.FieldColour.prototype.onKeyDown_ = function(e) {
* @param {number} dy Change of y
* @private
*/
Blockly.FieldColour.prototype.moveHighlightBy_ = function(dx, dy) {
var colours = this.colours_ || Blockly.FieldColour.COLOURS;
var columns = this.columns_ || Blockly.FieldColour.COLUMNS;
FieldColour.prototype.moveHighlightBy_ = function(dx, dy) {
const colours = this.colours_ || FieldColour.COLOURS;
const columns = this.columns_ || FieldColour.COLUMNS;
// Get the current x and y coordinates
var x = this.highlightedIndex_ % columns;
var y = Math.floor(this.highlightedIndex_ / columns);
let x = this.highlightedIndex_ % columns;
let y = Math.floor(this.highlightedIndex_ / columns);
// Add the offset
x += dx;
@@ -414,8 +417,7 @@ Blockly.FieldColour.prototype.moveHighlightBy_ = function(dx, dy) {
} else if (dx > 0) {
// Move right one grid cell, even in RTL.
// Loop to the start of the next row, if there's room.
if (x > columns - 1 &&
y < Math.floor(colours.length / columns) - 1) {
if (x > columns - 1 && y < Math.floor(colours.length / columns) - 1) {
x = 0;
y++;
} else if (x > columns - 1) {
@@ -434,8 +436,9 @@ Blockly.FieldColour.prototype.moveHighlightBy_ = function(dx, dy) {
}
// Move the highlight to the new coordinates.
var cell = /** @type {!Element} */ (this.picker_.childNodes[y].childNodes[x]);
var index = (y * columns) + x;
const cell =
/** @type {!Element} */ (this.picker_.childNodes[y].childNodes[x]);
const index = (y * columns) + x;
this.setHighlightedCell_(cell, index);
};
@@ -444,9 +447,9 @@ Blockly.FieldColour.prototype.moveHighlightBy_ = function(dx, dy) {
* @param {!MouseEvent} e Mouse event.
* @private
*/
Blockly.FieldColour.prototype.onMouseMove_ = function(e) {
var cell = /** @type {!Element} */ (e.target);
var index = cell && Number(cell.getAttribute('data-index'));
FieldColour.prototype.onMouseMove_ = function(e) {
const cell = /** @type {!Element} */ (e.target);
const index = cell && Number(cell.getAttribute('data-index'));
if (index !== null && index !== this.highlightedIndex_) {
this.setHighlightedCell_(cell, index);
}
@@ -456,8 +459,8 @@ Blockly.FieldColour.prototype.onMouseMove_ = function(e) {
* Handle a mouse enter event. Focus the picker.
* @private
*/
Blockly.FieldColour.prototype.onMouseEnter_ = function() {
this.picker_.focus({preventScroll:true});
FieldColour.prototype.onMouseEnter_ = function() {
this.picker_.focus({preventScroll: true});
};
/**
@@ -465,11 +468,11 @@ Blockly.FieldColour.prototype.onMouseEnter_ = function() {
* the currently highlighted colour.
* @private
*/
Blockly.FieldColour.prototype.onMouseLeave_ = function() {
FieldColour.prototype.onMouseLeave_ = function() {
this.picker_.blur();
var highlighted = this.getHighlighted_();
const highlighted = this.getHighlighted_();
if (highlighted) {
Blockly.utils.dom.removeClass(highlighted, 'blocklyColourHighlighted');
removeClass(highlighted, 'blocklyColourHighlighted');
}
};
@@ -478,15 +481,15 @@ Blockly.FieldColour.prototype.onMouseLeave_ = function() {
* @return {?HTMLElement} Highlighted item (null if none).
* @private
*/
Blockly.FieldColour.prototype.getHighlighted_ = function() {
var columns = this.columns_ || Blockly.FieldColour.COLUMNS;
var x = this.highlightedIndex_ % columns;
var y = Math.floor(this.highlightedIndex_ / columns);
var row = this.picker_.childNodes[y];
FieldColour.prototype.getHighlighted_ = function() {
const columns = this.columns_ || FieldColour.COLUMNS;
const x = this.highlightedIndex_ % columns;
const y = Math.floor(this.highlightedIndex_ / columns);
const row = this.picker_.childNodes[y];
if (!row) {
return null;
}
var col = /** @type {HTMLElement} */ (row.childNodes[x]);
const col = /** @type {HTMLElement} */ (row.childNodes[x]);
return col;
};
@@ -496,60 +499,58 @@ Blockly.FieldColour.prototype.getHighlighted_ = function() {
* @param {number} index the index of the new cell
* @private
*/
Blockly.FieldColour.prototype.setHighlightedCell_ = function(cell, index) {
FieldColour.prototype.setHighlightedCell_ = function(cell, index) {
// Unhighlight the current item.
var highlighted = this.getHighlighted_();
const highlighted = this.getHighlighted_();
if (highlighted) {
Blockly.utils.dom.removeClass(highlighted, 'blocklyColourHighlighted');
removeClass(highlighted, 'blocklyColourHighlighted');
}
// Highlight new item.
Blockly.utils.dom.addClass(cell, 'blocklyColourHighlighted');
addClass(cell, 'blocklyColourHighlighted');
// Set new highlighted index.
this.highlightedIndex_ = index;
// Update accessibility roles.
Blockly.utils.aria.setState(/** @type {!Element} */ (this.picker_),
Blockly.utils.aria.State.ACTIVEDESCENDANT, cell.getAttribute('id'));
aria.setState(
/** @type {!Element} */ (this.picker_), aria.State.ACTIVEDESCENDANT,
cell.getAttribute('id'));
};
/**
* Create a colour picker dropdown editor.
* @private
*/
Blockly.FieldColour.prototype.dropdownCreate_ = function() {
var columns = this.columns_ || Blockly.FieldColour.COLUMNS;
var colours = this.colours_ || Blockly.FieldColour.COLOURS;
var titles = this.titles_ || Blockly.FieldColour.TITLES;
var selectedColour = this.getValue();
FieldColour.prototype.dropdownCreate_ = function() {
const columns = this.columns_ || FieldColour.COLUMNS;
const colours = this.colours_ || FieldColour.COLOURS;
const titles = this.titles_ || FieldColour.TITLES;
const selectedColour = this.getValue();
// Create the palette.
var table = document.createElement('table');
const table = document.createElement('table');
table.className = 'blocklyColourTable';
table.tabIndex = 0;
table.dir = 'ltr';
Blockly.utils.aria.setRole(table, Blockly.utils.aria.Role.GRID);
Blockly.utils.aria.setState(table, Blockly.utils.aria.State.EXPANDED, true);
Blockly.utils.aria.setState(table, Blockly.utils.aria.State.ROWCOUNT,
Math.floor(colours.length / columns));
Blockly.utils.aria.setState(table, Blockly.utils.aria.State.COLCOUNT,
columns);
var row;
for (var i = 0; i < colours.length; i++) {
aria.setRole(table, aria.Role.GRID);
aria.setState(table, aria.State.EXPANDED, true);
aria.setState(
table, aria.State.ROWCOUNT, Math.floor(colours.length / columns));
aria.setState(table, aria.State.COLCOUNT, columns);
let row;
for (let i = 0; i < colours.length; i++) {
if (i % columns == 0) {
row = document.createElement('tr');
Blockly.utils.aria.setRole(row, Blockly.utils.aria.Role.ROW);
aria.setRole(row, aria.Role.ROW);
table.appendChild(row);
}
var cell = document.createElement('td');
const cell = document.createElement('td');
row.appendChild(cell);
cell.label = colours[i]; // This becomes the value, if clicked.
cell.title = titles[i] || colours[i];
cell.id = Blockly.utils.IdGenerator.getNextUniqueId();
cell.id = IdGenerator.getNextUniqueId();
cell.setAttribute('data-index', i);
Blockly.utils.aria.setRole(cell, Blockly.utils.aria.Role.GRIDCELL);
Blockly.utils.aria.setState(cell,
Blockly.utils.aria.State.LABEL, colours[i]);
Blockly.utils.aria.setState(cell,
Blockly.utils.aria.State.SELECTED, colours[i] == selectedColour);
aria.setRole(cell, aria.Role.GRIDCELL);
aria.setState(cell, aria.State.LABEL, colours[i]);
aria.setState(cell, aria.State.SELECTED, colours[i] == selectedColour);
cell.style.backgroundColor = colours[i];
if (colours[i] == selectedColour) {
cell.className = 'blocklyColourSelected';
@@ -558,16 +559,16 @@ Blockly.FieldColour.prototype.dropdownCreate_ = function() {
}
// Configure event handler on the table to listen for any event in a cell.
this.onClickWrapper_ = Blockly.browserEvents.conditionalBind(
table, 'click', this, this.onClick_, true);
this.onMouseMoveWrapper_ = Blockly.browserEvents.conditionalBind(
table, 'mousemove', this, this.onMouseMove_, true);
this.onMouseEnterWrapper_ = Blockly.browserEvents.conditionalBind(
table, 'mouseenter', this, this.onMouseEnter_, true);
this.onMouseLeaveWrapper_ = Blockly.browserEvents.conditionalBind(
table, 'mouseleave', this, this.onMouseLeave_, true);
this.onKeyDownWrapper_ = Blockly.browserEvents.conditionalBind(
table, 'keydown', this, this.onKeyDown_);
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);
this.onKeyDownWrapper_ =
conditionalBind(table, 'keydown', this, this.onKeyDown_);
this.picker_ = table;
};
@@ -576,25 +577,25 @@ Blockly.FieldColour.prototype.dropdownCreate_ = function() {
* Disposes of events and DOM-references belonging to the colour editor.
* @private
*/
Blockly.FieldColour.prototype.dropdownDispose_ = function() {
FieldColour.prototype.dropdownDispose_ = function() {
if (this.onClickWrapper_) {
Blockly.browserEvents.unbind(this.onClickWrapper_);
unbind(this.onClickWrapper_);
this.onClickWrapper_ = null;
}
if (this.onMouseMoveWrapper_) {
Blockly.browserEvents.unbind(this.onMouseMoveWrapper_);
unbind(this.onMouseMoveWrapper_);
this.onMouseMoveWrapper_ = null;
}
if (this.onMouseEnterWrapper_) {
Blockly.browserEvents.unbind(this.onMouseEnterWrapper_);
unbind(this.onMouseEnterWrapper_);
this.onMouseEnterWrapper_ = null;
}
if (this.onMouseLeaveWrapper_) {
Blockly.browserEvents.unbind(this.onMouseLeaveWrapper_);
unbind(this.onMouseLeaveWrapper_);
this.onMouseLeaveWrapper_ = null;
}
if (this.onKeyDownWrapper_) {
Blockly.browserEvents.unbind(this.onKeyDownWrapper_);
unbind(this.onKeyDownWrapper_);
this.onKeyDownWrapper_ = null;
}
this.picker_ = null;
@@ -604,7 +605,7 @@ Blockly.FieldColour.prototype.dropdownDispose_ = function() {
/**
* CSS for colour picker. See css.js for use.
*/
Blockly.Css.register([
Css.register([
/* eslint-disable indent */
'.blocklyColourTable {',
'border-collapse: collapse;',
@@ -637,4 +638,6 @@ Blockly.Css.register([
/* eslint-enable indent */
]);
Blockly.fieldRegistry.register('field_colour', Blockly.FieldColour);
fieldRegistry.register('field_colour', FieldColour);
exports = FieldColour;

View File

@@ -12,21 +12,22 @@
*/
'use strict';
goog.provide('Blockly.FieldDropdown');
goog.module('Blockly.FieldDropdown');
goog.module.declareLegacyNamespace();
goog.require('Blockly.DropDownDiv');
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.Menu');
goog.require('Blockly.MenuItem');
goog.require('Blockly.utils');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.string');
goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.userAgent');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const DropDownDiv = goog.require('Blockly.DropDownDiv');
const Field = goog.require('Blockly.Field');
const Menu = goog.require('Blockly.Menu');
const MenuItem = goog.require('Blockly.MenuItem');
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 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');
/**
@@ -38,22 +39,23 @@ goog.require('Blockly.utils.userAgent');
* option & returns a validated language-neutral dropdown option, or null to
* abort the change.
* @param {Object=} opt_config A map of options used to configure the field.
* See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/dropdown#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/dropdown#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.Field}
* @extends {Field}
* @constructor
* @throws {TypeError} If `menuGenerator` options are incorrectly structured.
*/
Blockly.FieldDropdown = function(menuGenerator, opt_validator, opt_config) {
const FieldDropdown = function(menuGenerator, opt_validator, opt_config) {
if (typeof menuGenerator != 'function') {
Blockly.FieldDropdown.validateOptions_(menuGenerator);
validateOptions(menuGenerator);
}
/**
* An array of options for a dropdown list,
* or a function which generates these options.
* @type {(!Array<!Array>|
* !function(this:Blockly.FieldDropdown): !Array<!Array>)}
* !function(this:FieldDropdown): !Array<!Array>)}
* @protected
*/
this.menuGenerator_ = menuGenerator;
@@ -90,19 +92,19 @@ Blockly.FieldDropdown = function(menuGenerator, opt_validator, opt_config) {
this.selectedOption_ = this.getOptions(false)[0];
// Call parent's constructor.
Blockly.FieldDropdown.superClass_.constructor.call(
FieldDropdown.superClass_.constructor.call(
this, this.selectedOption_[1], opt_validator, opt_config);
/**
* A reference to the currently selected menu item.
* @type {?Blockly.MenuItem}
* @type {?MenuItem}
* @private
*/
this.selectedMenuItem_ = null;
/**
* The dropdown menu.
* @type {?Blockly.Menu}
* @type {?Menu}
* @protected
*/
this.menu_ = null;
@@ -128,27 +130,27 @@ Blockly.FieldDropdown = function(menuGenerator, opt_validator, opt_config) {
*/
this.svgArrow_ = null;
};
Blockly.utils.object.inherits(Blockly.FieldDropdown, Blockly.Field);
inherits(FieldDropdown, Field);
/**
* Dropdown image properties.
* @typedef {{
* src:string,
* alt:string,
* width:number,
* height:number
* }}
*/
Blockly.FieldDropdown.ImageProperties;
* src:string,
* alt:string,
* width:number,
* height:number
* }}
*/
FieldDropdown.ImageProperties;
/**
* Construct a FieldDropdown from a JSON arg object.
* @param {!Object} options A JSON object with options (options).
* @return {!Blockly.FieldDropdown} The new field instance.
* @return {!FieldDropdown} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldDropdown.fromJson = function(options) {
FieldDropdown.fromJson = function(options) {
// `this` might be a subclass of FieldDropdown if that class doesn't override
// the static fromJson method.
return new this(options['options'], undefined, options);
@@ -161,7 +163,7 @@ Blockly.FieldDropdown.fromJson = function(options) {
* field's state.
* @package
*/
Blockly.FieldDropdown.prototype.fromXml = function(fieldElement) {
FieldDropdown.prototype.fromXml = function(fieldElement) {
if (this.isOptionListDynamic()) {
this.getOptions(false);
}
@@ -173,52 +175,48 @@ Blockly.FieldDropdown.prototype.fromXml = function(fieldElement) {
* are not. Editable fields should also be serializable.
* @type {boolean}
*/
Blockly.FieldDropdown.prototype.SERIALIZABLE = true;
FieldDropdown.prototype.SERIALIZABLE = true;
/**
* Horizontal distance that a checkmark overhangs the dropdown.
*/
Blockly.FieldDropdown.CHECKMARK_OVERHANG = 25;
FieldDropdown.CHECKMARK_OVERHANG = 25;
/**
* Maximum height of the dropdown menu, as a percentage of the viewport height.
*/
Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH = 0.45;
FieldDropdown.MAX_MENU_HEIGHT_VH = 0.45;
/**
* The y offset from the top of the field to the top of the image, if an image
* is selected.
* @type {number}
* @const
* @private
*/
Blockly.FieldDropdown.IMAGE_Y_OFFSET = 5;
const IMAGE_Y_OFFSET = 5;
/**
* The total vertical padding above and below an image.
* @type {number}
* @const
* @private
*/
Blockly.FieldDropdown.IMAGE_Y_PADDING =
Blockly.FieldDropdown.IMAGE_Y_OFFSET * 2;
const IMAGE_Y_PADDING = IMAGE_Y_OFFSET * 2;
/**
* Android can't (in 2014) display "▾", so use "▼" instead.
*/
Blockly.FieldDropdown.ARROW_CHAR =
Blockly.utils.userAgent.ANDROID ? '\u25BC' : '\u25BE';
FieldDropdown.ARROW_CHAR = userAgent.ANDROID ? '\u25BC' : '\u25BE';
/**
* Mouse cursor style when over the hotspot that initiates the editor.
*/
Blockly.FieldDropdown.prototype.CURSOR = 'default';
FieldDropdown.prototype.CURSOR = 'default';
/**
* Create the block UI for this dropdown.
* @package
*/
Blockly.FieldDropdown.prototype.initView = function() {
FieldDropdown.prototype.initView = function() {
if (this.shouldAddBorderRect_()) {
this.createBorderRect_();
} else {
@@ -226,8 +224,7 @@ Blockly.FieldDropdown.prototype.initView = function() {
}
this.createTextElement_();
this.imageElement_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.IMAGE, {}, this.fieldGroup_);
this.imageElement_ = dom.createSvgElement(Svg.IMAGE, {}, this.fieldGroup_);
if (this.getConstants().FIELD_DROPDOWN_SVG_ARROW) {
this.createSVGArrow_();
@@ -236,7 +233,7 @@ Blockly.FieldDropdown.prototype.initView = function() {
}
if (this.borderRect_) {
Blockly.utils.dom.addClass(this.borderRect_, 'blocklyDropdownRect');
dom.addClass(this.borderRect_, 'blocklyDropdownRect');
}
};
@@ -245,23 +242,21 @@ Blockly.FieldDropdown.prototype.initView = function() {
* @return {boolean} True if the dropdown field should add a border rect.
* @protected
*/
Blockly.FieldDropdown.prototype.shouldAddBorderRect_ = function() {
FieldDropdown.prototype.shouldAddBorderRect_ = function() {
return !this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW ||
(this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW &&
!this.sourceBlock_.isShadow());
!this.sourceBlock_.isShadow());
};
/**
* Create a tspan based arrow.
* @protected
*/
Blockly.FieldDropdown.prototype.createTextArrow_ = function() {
this.arrow_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.TSPAN, {}, this.textElement_);
FieldDropdown.prototype.createTextArrow_ = function() {
this.arrow_ = dom.createSvgElement(Svg.TSPAN, {}, this.textElement_);
this.arrow_.appendChild(document.createTextNode(
this.sourceBlock_.RTL ?
Blockly.FieldDropdown.ARROW_CHAR + ' ' :
' ' + Blockly.FieldDropdown.ARROW_CHAR));
this.sourceBlock_.RTL ? FieldDropdown.ARROW_CHAR + ' ' :
' ' + FieldDropdown.ARROW_CHAR));
if (this.sourceBlock_.RTL) {
this.textElement_.insertBefore(this.arrow_, this.textContent_);
} else {
@@ -273,13 +268,15 @@ Blockly.FieldDropdown.prototype.createTextArrow_ = function() {
* Create an SVG based arrow.
* @protected
*/
Blockly.FieldDropdown.prototype.createSVGArrow_ = function() {
this.svgArrow_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.IMAGE, {
FieldDropdown.prototype.createSVGArrow_ = function() {
this.svgArrow_ = dom.createSvgElement(
Svg.IMAGE, {
'height': this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px',
'width': this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px'
}, this.fieldGroup_);
this.svgArrow_.setAttributeNS(Blockly.utils.dom.XLINK_NS, 'xlink:href',
},
this.fieldGroup_);
this.svgArrow_.setAttributeNS(
dom.XLINK_NS, 'xlink:href',
this.getConstants().FIELD_DROPDOWN_SVG_ARROW_DATAURI);
};
@@ -289,31 +286,29 @@ Blockly.FieldDropdown.prototype.createSVGArrow_ = function() {
* or undefined if triggered programmatically.
* @protected
*/
Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) {
FieldDropdown.prototype.showEditor_ = function(opt_e) {
this.dropdownCreate_();
if (opt_e && typeof opt_e.clientX === 'number') {
this.menu_.openingCoords =
new Blockly.utils.Coordinate(opt_e.clientX, opt_e.clientY);
this.menu_.openingCoords = new Coordinate(opt_e.clientX, opt_e.clientY);
} else {
this.menu_.openingCoords = null;
}
// Element gets created in render.
this.menu_.render(Blockly.DropDownDiv.getContentDiv());
var menuElement = /** @type {!Element} */ (this.menu_.getElement());
Blockly.utils.dom.addClass(menuElement, 'blocklyDropdownMenu');
this.menu_.render(DropDownDiv.getContentDiv());
const menuElement = /** @type {!Element} */ (this.menu_.getElement());
dom.addClass(menuElement, 'blocklyDropdownMenu');
if (this.getConstants().FIELD_DROPDOWN_COLOURED_DIV) {
var primaryColour = (this.sourceBlock_.isShadow()) ?
const primaryColour = (this.sourceBlock_.isShadow()) ?
this.sourceBlock_.getParent().getColour() :
this.sourceBlock_.getColour();
var borderColour = (this.sourceBlock_.isShadow()) ?
const borderColour = (this.sourceBlock_.isShadow()) ?
this.sourceBlock_.getParent().style.colourTertiary :
this.sourceBlock_.style.colourTertiary;
Blockly.DropDownDiv.setColour(primaryColour, borderColour);
DropDownDiv.setColour(primaryColour, borderColour);
}
Blockly.DropDownDiv.showPositionedByField(
this, this.dropdownDispose_.bind(this));
DropDownDiv.showPositionedByField(this, this.dropdownDispose_.bind(this));
// Focusing needs to be handled after the menu is rendered and positioned.
// Otherwise it will cause a page scroll to get the misplaced menu in
@@ -331,25 +326,25 @@ Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) {
* Create the dropdown editor.
* @private
*/
Blockly.FieldDropdown.prototype.dropdownCreate_ = function() {
var menu = new Blockly.Menu();
menu.setRole(Blockly.utils.aria.Role.LISTBOX);
FieldDropdown.prototype.dropdownCreate_ = function() {
const menu = new Menu();
menu.setRole(aria.Role.LISTBOX);
this.menu_ = menu;
var options = this.getOptions(false);
const options = this.getOptions(false);
this.selectedMenuItem_ = null;
for (var i = 0; i < options.length; i++) {
var content = options[i][0]; // Human-readable text or image.
var value = options[i][1]; // Language-neutral value.
for (let i = 0; i < options.length; i++) {
let content = options[i][0]; // Human-readable text or image.
const value = options[i][1]; // Language-neutral value.
if (typeof content == 'object') {
// An image, not text.
var image = new Image(content['width'], content['height']);
const image = new Image(content['width'], content['height']);
image.src = content['src'];
image.alt = content['alt'] || '';
content = image;
}
var menuItem = new Blockly.MenuItem(content, value);
menuItem.setRole(Blockly.utils.aria.Role.OPTION);
const menuItem = new MenuItem(content, value);
menuItem.setRole(aria.Role.OPTION);
menuItem.setRightToLeft(this.sourceBlock_.RTL);
menuItem.setCheckable(true);
menu.addChild(menuItem);
@@ -365,7 +360,7 @@ Blockly.FieldDropdown.prototype.dropdownCreate_ = function() {
* Disposes of events and DOM-references belonging to the dropdown editor.
* @private
*/
Blockly.FieldDropdown.prototype.dropdownDispose_ = function() {
FieldDropdown.prototype.dropdownDispose_ = function() {
if (this.menu_) {
this.menu_.dispose();
}
@@ -376,21 +371,21 @@ Blockly.FieldDropdown.prototype.dropdownDispose_ = function() {
/**
* Handle an action in the dropdown menu.
* @param {!Blockly.MenuItem} menuItem The MenuItem selected within menu.
* @param {!MenuItem} menuItem The MenuItem selected within menu.
* @private
*/
Blockly.FieldDropdown.prototype.handleMenuActionEvent_ = function(menuItem) {
Blockly.DropDownDiv.hideIfOwner(this, true);
this.onItemSelected_(/** @type {!Blockly.Menu} */ (this.menu_), menuItem);
FieldDropdown.prototype.handleMenuActionEvent_ = function(menuItem) {
DropDownDiv.hideIfOwner(this, true);
this.onItemSelected_(/** @type {!Menu} */ (this.menu_), menuItem);
};
/**
* Handle the selection of an item in the dropdown menu.
* @param {!Blockly.Menu} menu The Menu component clicked.
* @param {!Blockly.MenuItem} menuItem The MenuItem selected within menu.
* @param {!Menu} menu The Menu component clicked.
* @param {!MenuItem} menuItem The MenuItem selected within menu.
* @protected
*/
Blockly.FieldDropdown.prototype.onItemSelected_ = function(menu, menuItem) {
FieldDropdown.prototype.onItemSelected_ = function(menu, menuItem) {
this.setValue(menuItem.getValue());
};
@@ -399,21 +394,21 @@ Blockly.FieldDropdown.prototype.onItemSelected_ = function(menu, menuItem) {
* Create prefix and/or suffix labels.
* @private
*/
Blockly.FieldDropdown.prototype.trimOptions_ = function() {
var options = this.menuGenerator_;
FieldDropdown.prototype.trimOptions_ = function() {
const options = this.menuGenerator_;
if (!Array.isArray(options)) {
return;
}
var hasImages = false;
let hasImages = false;
// Localize label text and image alt text.
for (var i = 0; i < options.length; i++) {
var label = options[i][0];
for (let i = 0; i < options.length; i++) {
const label = options[i][0];
if (typeof label == 'string') {
options[i][0] = Blockly.utils.replaceMessageReferences(label);
options[i][0] = replaceMessageReferences(label);
} else {
if (label.alt != null) {
options[i][0].alt = Blockly.utils.replaceMessageReferences(label.alt);
options[i][0].alt = replaceMessageReferences(label.alt);
}
hasImages = true;
}
@@ -421,13 +416,13 @@ Blockly.FieldDropdown.prototype.trimOptions_ = function() {
if (hasImages || options.length < 2) {
return; // Do nothing if too few items or at least one label is an image.
}
var strings = [];
for (var i = 0; i < options.length; i++) {
const strings = [];
for (let i = 0; i < options.length; i++) {
strings.push(options[i][0]);
}
var shortest = Blockly.utils.string.shortestStringLength(strings);
var prefixLength = Blockly.utils.string.commonWordPrefix(strings, shortest);
var suffixLength = Blockly.utils.string.commonWordSuffix(strings, shortest);
const shortest = shortestStringLength(strings);
const prefixLength = commonWordPrefix(strings, shortest);
const suffixLength = commonWordSuffix(strings, shortest);
if (!prefixLength && !suffixLength) {
return;
}
@@ -442,8 +437,8 @@ Blockly.FieldDropdown.prototype.trimOptions_ = function() {
this.suffixField = strings[0].substr(1 - suffixLength);
}
this.menuGenerator_ = Blockly.FieldDropdown.applyTrim_(options, prefixLength,
suffixLength);
this.menuGenerator_ =
FieldDropdown.applyTrim_(options, prefixLength, suffixLength);
};
/**
@@ -455,13 +450,12 @@ Blockly.FieldDropdown.prototype.trimOptions_ = function() {
* @param {number} suffixLength The length of the common suffix
* @return {!Array<!Array>} A new array with all of the option text trimmed.
*/
Blockly.FieldDropdown.applyTrim_ = function(options,
prefixLength, suffixLength) {
var newOptions = [];
FieldDropdown.applyTrim_ = function(options, prefixLength, suffixLength) {
const newOptions = [];
// Remove the prefix and suffix from the options.
for (var i = 0; i < options.length; i++) {
var text = options[i][0];
var value = options[i][1];
for (let i = 0; i < options.length; i++) {
let text = options[i][0];
const value = options[i][1];
text = text.substring(prefixLength, text.length - suffixLength);
newOptions[i] = [text, value];
}
@@ -472,7 +466,7 @@ Blockly.FieldDropdown.applyTrim_ = function(options,
* @return {boolean} True if the option list is generated by a function.
* Otherwise false.
*/
Blockly.FieldDropdown.prototype.isOptionListDynamic = function() {
FieldDropdown.prototype.isOptionListDynamic = function() {
return typeof this.menuGenerator_ == 'function';
};
@@ -484,11 +478,11 @@ Blockly.FieldDropdown.prototype.isOptionListDynamic = function() {
* (human-readable text or image, language-neutral name).
* @throws {TypeError} If generated options are incorrectly structured.
*/
Blockly.FieldDropdown.prototype.getOptions = function(opt_useCache) {
FieldDropdown.prototype.getOptions = function(opt_useCache) {
if (this.isOptionListDynamic()) {
if (!this.generatedOptions_ || !opt_useCache) {
this.generatedOptions_ = this.menuGenerator_.call(this);
Blockly.FieldDropdown.validateOptions_(this.generatedOptions_);
validateOptions(this.generatedOptions_);
}
return this.generatedOptions_;
}
@@ -501,10 +495,10 @@ Blockly.FieldDropdown.prototype.getOptions = function(opt_useCache) {
* @return {?string} A valid language-neutral option, or null if invalid.
* @protected
*/
Blockly.FieldDropdown.prototype.doClassValidation_ = function(opt_newValue) {
var isValueValid = false;
var options = this.getOptions(true);
for (var i = 0, option; (option = options[i]); i++) {
FieldDropdown.prototype.doClassValidation_ = function(opt_newValue) {
let isValueValid = false;
const options = this.getOptions(true);
for (let i = 0, option; (option = options[i]); i++) {
// Options are tuples of human-readable text and language-neutral values.
if (option[1] == opt_newValue) {
isValueValid = true;
@@ -513,9 +507,10 @@ Blockly.FieldDropdown.prototype.doClassValidation_ = function(opt_newValue) {
}
if (!isValueValid) {
if (this.sourceBlock_) {
console.warn('Cannot set the dropdown\'s value to an unavailable option.' +
' Block type: ' + this.sourceBlock_.type + ', Field name: ' + this.name +
', Value: ' + opt_newValue);
console.warn(
'Cannot set the dropdown\'s value to an unavailable option.' +
' Block type: ' + this.sourceBlock_.type +
', Field name: ' + this.name + ', Value: ' + opt_newValue);
}
return null;
}
@@ -528,10 +523,10 @@ Blockly.FieldDropdown.prototype.doClassValidation_ = function(opt_newValue) {
* that this is one of the valid dropdown options.
* @protected
*/
Blockly.FieldDropdown.prototype.doValueUpdate_ = function(newValue) {
Blockly.FieldDropdown.superClass_.doValueUpdate_.call(this, newValue);
var options = this.getOptions(true);
for (var i = 0, option; (option = options[i]); i++) {
FieldDropdown.prototype.doValueUpdate_ = function(newValue) {
FieldDropdown.superClass_.doValueUpdate_.call(this, newValue);
const options = this.getOptions(true);
for (let i = 0, option; (option = options[i]); i++) {
if (option[1] == this.value_) {
this.selectedOption_ = option;
}
@@ -542,13 +537,13 @@ Blockly.FieldDropdown.prototype.doValueUpdate_ = function(newValue) {
* Updates the dropdown arrow to match the colour/style of the block.
* @package
*/
Blockly.FieldDropdown.prototype.applyColour = function() {
FieldDropdown.prototype.applyColour = function() {
if (this.borderRect_) {
this.borderRect_.setAttribute('stroke',
this.sourceBlock_.style.colourTertiary);
this.borderRect_.setAttribute(
'stroke', this.sourceBlock_.style.colourTertiary);
if (this.menu_) {
this.borderRect_.setAttribute('fill',
this.sourceBlock_.style.colourTertiary);
this.borderRect_.setAttribute(
'fill', this.sourceBlock_.style.colourTertiary);
} else {
this.borderRect_.setAttribute('fill', 'transparent');
}
@@ -567,16 +562,16 @@ Blockly.FieldDropdown.prototype.applyColour = function() {
* Draws the border with the correct width.
* @protected
*/
Blockly.FieldDropdown.prototype.render_ = function() {
FieldDropdown.prototype.render_ = function() {
// Hide both elements.
this.textContent_.nodeValue = '';
this.imageElement_.style.display = 'none';
// Show correct element.
var option = this.selectedOption_ && this.selectedOption_[0];
const option = this.selectedOption_ && this.selectedOption_[0];
if (option && typeof option == 'object') {
this.renderSelectedImage_(
/** @type {!Blockly.FieldDropdown.ImageProperties} */ (option));
/** @type {!FieldDropdown.ImageProperties} */ (option));
} else {
this.renderSelectedText_();
}
@@ -586,32 +581,33 @@ Blockly.FieldDropdown.prototype.render_ = function() {
/**
* Renders the selected option, which must be an image.
* @param {!Blockly.FieldDropdown.ImageProperties} imageJson Selected
* @param {!FieldDropdown.ImageProperties} imageJson Selected
* option that must be an image.
* @private
*/
Blockly.FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) {
FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) {
this.imageElement_.style.display = '';
this.imageElement_.setAttributeNS(
Blockly.utils.dom.XLINK_NS, 'xlink:href', imageJson.src);
this.imageElement_.setAttributeNS(dom.XLINK_NS, 'xlink:href', imageJson.src);
this.imageElement_.setAttribute('height', imageJson.height);
this.imageElement_.setAttribute('width', imageJson.width);
var imageHeight = Number(imageJson.height);
var imageWidth = Number(imageJson.width);
const imageHeight = Number(imageJson.height);
const imageWidth = Number(imageJson.width);
// Height and width include the border rect.
var hasBorder = !!this.borderRect_;
var height = Math.max(
const hasBorder = !!this.borderRect_;
const height = Math.max(
hasBorder ? this.getConstants().FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0,
imageHeight + Blockly.FieldDropdown.IMAGE_Y_PADDING);
var xPadding = hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0;
var arrowWidth = 0;
imageHeight + IMAGE_Y_PADDING);
const xPadding =
hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0;
let arrowWidth = 0;
if (this.svgArrow_) {
arrowWidth = this.positionSVGArrow_(imageWidth + xPadding, height / 2 -
this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE / 2);
arrowWidth = this.positionSVGArrow_(
imageWidth + xPadding,
height / 2 - this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE / 2);
} else {
arrowWidth = Blockly.utils.dom.getFastTextWidth(
arrowWidth = dom.getFastTextWidth(
/** @type {!SVGTSpanElement} */ (this.arrow_),
this.getConstants().FIELD_TEXT_FONTSIZE,
this.getConstants().FIELD_TEXT_FONTWEIGHT,
@@ -620,9 +616,9 @@ Blockly.FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) {
this.size_.width = imageWidth + arrowWidth + xPadding * 2;
this.size_.height = height;
var arrowX = 0;
let arrowX = 0;
if (this.sourceBlock_.RTL) {
var imageX = xPadding + arrowWidth;
const imageX = xPadding + arrowWidth;
this.imageElement_.setAttribute('x', imageX);
} else {
arrowX = imageWidth + arrowWidth;
@@ -638,27 +634,29 @@ Blockly.FieldDropdown.prototype.renderSelectedImage_ = function(imageJson) {
* Renders the selected option, which must be text.
* @private
*/
Blockly.FieldDropdown.prototype.renderSelectedText_ = function() {
FieldDropdown.prototype.renderSelectedText_ = function() {
// Retrieves the selected option to display through getText_.
this.textContent_.nodeValue = this.getDisplayText_();
Blockly.utils.dom.addClass(/** @type {!Element} */ (this.textElement_),
'blocklyDropdownText');
dom.addClass(
/** @type {!Element} */ (this.textElement_), 'blocklyDropdownText');
this.textElement_.setAttribute('text-anchor', 'start');
// Height and width include the border rect.
var hasBorder = !!this.borderRect_;
var height = Math.max(
const hasBorder = !!this.borderRect_;
const height = Math.max(
hasBorder ? this.getConstants().FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0,
this.getConstants().FIELD_TEXT_HEIGHT);
var textWidth = Blockly.utils.dom.getFastTextWidth(this.textElement_,
this.getConstants().FIELD_TEXT_FONTSIZE,
const textWidth = dom.getFastTextWidth(
this.textElement_, this.getConstants().FIELD_TEXT_FONTSIZE,
this.getConstants().FIELD_TEXT_FONTWEIGHT,
this.getConstants().FIELD_TEXT_FONTFAMILY);
var xPadding = hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0;
var arrowWidth = 0;
const xPadding =
hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0;
let arrowWidth = 0;
if (this.svgArrow_) {
arrowWidth = this.positionSVGArrow_(textWidth + xPadding, height / 2 -
this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE / 2);
arrowWidth = this.positionSVGArrow_(
textWidth + xPadding,
height / 2 - this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE / 2);
}
this.size_.width = textWidth + arrowWidth + xPadding * 2;
this.size_.height = height;
@@ -673,17 +671,18 @@ Blockly.FieldDropdown.prototype.renderSelectedText_ = function() {
* @return {number} Amount of space the arrow is taking up, in px.
* @private
*/
Blockly.FieldDropdown.prototype.positionSVGArrow_ = function(x, y) {
FieldDropdown.prototype.positionSVGArrow_ = function(x, y) {
if (!this.svgArrow_) {
return 0;
}
var hasBorder = !!this.borderRect_;
var xPadding = hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0;
var textPadding = this.getConstants().FIELD_DROPDOWN_SVG_ARROW_PADDING;
var svgArrowSize = this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE;
var arrowX = this.sourceBlock_.RTL ? xPadding : x + textPadding;
this.svgArrow_.setAttribute('transform',
'translate(' + arrowX + ',' + y + ')');
const hasBorder = !!this.borderRect_;
const xPadding =
hasBorder ? this.getConstants().FIELD_BORDER_RECT_X_PADDING : 0;
const textPadding = this.getConstants().FIELD_DROPDOWN_SVG_ARROW_PADDING;
const svgArrowSize = this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE;
const arrowX = this.sourceBlock_.RTL ? xPadding : x + textPadding;
this.svgArrow_.setAttribute(
'transform', 'translate(' + arrowX + ',' + y + ')');
return svgArrowSize + textPadding;
};
@@ -695,11 +694,11 @@ Blockly.FieldDropdown.prototype.positionSVGArrow_ = function(x, y) {
* @protected
* @override
*/
Blockly.FieldDropdown.prototype.getText_ = function() {
FieldDropdown.prototype.getText_ = function() {
if (!this.selectedOption_) {
return null;
}
var option = this.selectedOption_[0];
const option = this.selectedOption_[0];
if (typeof option == 'object') {
return option['alt'];
}
@@ -710,35 +709,36 @@ Blockly.FieldDropdown.prototype.getText_ = function() {
* Validates the data structure to be processed as an options list.
* @param {?} options The proposed dropdown options.
* @throws {TypeError} If proposed options are incorrectly structured.
* @private
*/
Blockly.FieldDropdown.validateOptions_ = function(options) {
const validateOptions = function(options) {
if (!Array.isArray(options)) {
throw TypeError('FieldDropdown options must be an array.');
}
if (!options.length) {
throw TypeError('FieldDropdown options must not be an empty array.');
}
var foundError = false;
for (var i = 0; i < options.length; ++i) {
var tuple = options[i];
let foundError = false;
for (let i = 0; i < options.length; ++i) {
const tuple = options[i];
if (!Array.isArray(tuple)) {
foundError = true;
console.error(
'Invalid option[' + i + ']: Each FieldDropdown option must be an ' +
'array. Found: ', tuple);
'array. Found: ',
tuple);
} else if (typeof tuple[1] != 'string') {
foundError = true;
console.error(
'Invalid option[' + i + ']: Each FieldDropdown option id must be ' +
'a string. Found ' + tuple[1] + ' in: ', tuple);
} else if (tuple[0] &&
(typeof tuple[0] != 'string') &&
(typeof tuple[0].src != 'string')) {
'a string. Found ' + tuple[1] + ' in: ',
tuple);
} else if (
tuple[0] && (typeof tuple[0] != 'string') &&
(typeof tuple[0].src != 'string')) {
foundError = true;
console.error(
'Invalid option[' + i + ']: Each FieldDropdown option must have a ' +
'string label or image description. Found' + tuple[0] + ' in: ',
'string label or image description. Found' + tuple[0] + ' in: ',
tuple);
}
}
@@ -747,4 +747,6 @@ Blockly.FieldDropdown.validateOptions_ = function(options) {
}
};
Blockly.fieldRegistry.register('field_dropdown', Blockly.FieldDropdown);
fieldRegistry.register('field_dropdown', FieldDropdown);
exports = FieldDropdown;

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,12 +10,13 @@
*/
'use strict';
goog.provide('Blockly.FieldNumber');
goog.module('Blockly.FieldNumber');
goog.module.declareLegacyNamespace();
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.FieldTextInput');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.object');
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');
/**
@@ -29,14 +30,14 @@ goog.require('Blockly.utils.object');
* changes to the field's value. Takes in a number & returns a validated
* number, or null to abort the change.
* @param {Object=} opt_config A map of options used to configure the field.
* See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/number#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/number#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.FieldTextInput}
* @extends {FieldTextInput}
* @constructor
*/
Blockly.FieldNumber = function(opt_value, opt_min, opt_max, opt_precision,
opt_validator, opt_config) {
const FieldNumber = function(
opt_value, opt_min, opt_max, opt_precision, opt_validator, opt_config) {
/**
* The minimum value this number field can contain.
* @type {number}
@@ -66,35 +67,35 @@ Blockly.FieldNumber = function(opt_value, opt_min, opt_max, opt_precision,
*/
this.decimalPlaces_ = null;
Blockly.FieldNumber.superClass_.constructor.call(
FieldNumber.superClass_.constructor.call(
this, opt_value, opt_validator, opt_config);
if (!opt_config) { // Only do one kind of configuration or the other.
this.setConstraints(opt_min, opt_max, opt_precision);
}
};
Blockly.utils.object.inherits(Blockly.FieldNumber, Blockly.FieldTextInput);
inherits(FieldNumber, FieldTextInput);
/**
* The default value for this field.
* @type {*}
* @protected
*/
Blockly.FieldNumber.prototype.DEFAULT_VALUE = 0;
FieldNumber.prototype.DEFAULT_VALUE = 0;
/**
* Construct a FieldNumber from a JSON arg object.
* @param {!Object} options A JSON object with options (value, min, max, and
* precision).
* @return {!Blockly.FieldNumber} The new field instance.
* @return {!FieldNumber} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldNumber.fromJson = function(options) {
FieldNumber.fromJson = function(options) {
// `this` might be a subclass of FieldNumber if that class doesn't override
// the static fromJson method.
return new this(options['value'],
undefined, undefined, undefined, undefined, options);
return new this(
options['value'], undefined, undefined, undefined, undefined, options);
};
/**
@@ -102,7 +103,7 @@ Blockly.FieldNumber.fromJson = function(options) {
* are not. Editable fields should also be serializable.
* @type {boolean}
*/
Blockly.FieldNumber.prototype.SERIALIZABLE = true;
FieldNumber.prototype.SERIALIZABLE = true;
/**
* Configure the field based on the given map of options.
@@ -110,8 +111,8 @@ Blockly.FieldNumber.prototype.SERIALIZABLE = true;
* @protected
* @override
*/
Blockly.FieldNumber.prototype.configure_ = function(config) {
Blockly.FieldNumber.superClass_.configure_.call(this, config);
FieldNumber.prototype.configure_ = function(config) {
FieldNumber.superClass_.configure_.call(this, config);
this.setMinInternal_(config['min']);
this.setMaxInternal_(config['max']);
this.setPrecisionInternal_(config['precision']);
@@ -128,7 +129,7 @@ Blockly.FieldNumber.prototype.configure_ = function(config) {
* @param {?(number|string|undefined)} max Maximum value.
* @param {?(number|string|undefined)} precision Precision for value.
*/
Blockly.FieldNumber.prototype.setConstraints = function(min, max, precision) {
FieldNumber.prototype.setConstraints = function(min, max, precision) {
this.setMinInternal_(min);
this.setMaxInternal_(max);
this.setPrecisionInternal_(precision);
@@ -139,7 +140,7 @@ Blockly.FieldNumber.prototype.setConstraints = function(min, max, precision) {
* Sets the minimum value this field can contain. Updates the value to reflect.
* @param {?(number|string|undefined)} min Minimum value.
*/
Blockly.FieldNumber.prototype.setMin = function(min) {
FieldNumber.prototype.setMin = function(min) {
this.setMinInternal_(min);
this.setValue(this.getValue());
};
@@ -150,7 +151,7 @@ Blockly.FieldNumber.prototype.setMin = function(min) {
* @param {?(number|string|undefined)} min Minimum value.
* @private
*/
Blockly.FieldNumber.prototype.setMinInternal_ = function(min) {
FieldNumber.prototype.setMinInternal_ = function(min) {
if (min == null) {
this.min_ = -Infinity;
} else {
@@ -166,7 +167,7 @@ Blockly.FieldNumber.prototype.setMinInternal_ = function(min) {
* -Infinity.
* @return {number} The current minimum value this field can contain.
*/
Blockly.FieldNumber.prototype.getMin = function() {
FieldNumber.prototype.getMin = function() {
return this.min_;
};
@@ -174,7 +175,7 @@ Blockly.FieldNumber.prototype.getMin = function() {
* Sets the maximum value this field can contain. Updates the value to reflect.
* @param {?(number|string|undefined)} max Maximum value.
*/
Blockly.FieldNumber.prototype.setMax = function(max) {
FieldNumber.prototype.setMax = function(max) {
this.setMaxInternal_(max);
this.setValue(this.getValue());
};
@@ -185,7 +186,7 @@ Blockly.FieldNumber.prototype.setMax = function(max) {
* @param {?(number|string|undefined)} max Maximum value.
* @private
*/
Blockly.FieldNumber.prototype.setMaxInternal_ = function(max) {
FieldNumber.prototype.setMaxInternal_ = function(max) {
if (max == null) {
this.max_ = Infinity;
} else {
@@ -201,7 +202,7 @@ Blockly.FieldNumber.prototype.setMaxInternal_ = function(max) {
* Infinity.
* @return {number} The current maximum value this field can contain.
*/
Blockly.FieldNumber.prototype.getMax = function() {
FieldNumber.prototype.getMax = function() {
return this.max_;
};
@@ -211,7 +212,7 @@ Blockly.FieldNumber.prototype.getMax = function() {
* @param {?(number|string|undefined)} precision The number to which the
* field's value is rounded.
*/
Blockly.FieldNumber.prototype.setPrecision = function(precision) {
FieldNumber.prototype.setPrecision = function(precision) {
this.setPrecisionInternal_(precision);
this.setValue(this.getValue());
};
@@ -223,16 +224,16 @@ Blockly.FieldNumber.prototype.setPrecision = function(precision) {
* field's value is rounded.
* @private
*/
Blockly.FieldNumber.prototype.setPrecisionInternal_ = function(precision) {
FieldNumber.prototype.setPrecisionInternal_ = function(precision) {
this.precision_ = Number(precision) || 0;
var precisionString = String(this.precision_);
let precisionString = String(this.precision_);
if (precisionString.indexOf('e') != -1) {
// String() is fast. But it turns .0000001 into '1e-7'.
// Use the much slower toLocaleString to access all the digits.
precisionString =
this.precision_.toLocaleString('en-US', {maximumFractionDigits: 20});
}
var decimalIndex = precisionString.indexOf('.');
const decimalIndex = precisionString.indexOf('.');
if (decimalIndex == -1) {
// If the precision is 0 (float) allow any number of decimals,
// otherwise allow none.
@@ -248,7 +249,7 @@ Blockly.FieldNumber.prototype.setPrecisionInternal_ = function(precision) {
* the value is not rounded.
* @return {number} The number to which this field's value is rounded.
*/
Blockly.FieldNumber.prototype.getPrecision = function() {
FieldNumber.prototype.getPrecision = function() {
return this.precision_;
};
@@ -260,12 +261,12 @@ Blockly.FieldNumber.prototype.getPrecision = function() {
* @protected
* @override
*/
Blockly.FieldNumber.prototype.doClassValidation_ = function(opt_newValue) {
FieldNumber.prototype.doClassValidation_ = function(opt_newValue) {
if (opt_newValue === null) {
return null;
}
// Clean up text.
var newValue = String(opt_newValue);
let newValue = String(opt_newValue);
// TODO: Handle cases like 'ten', '1.203,14', etc.
// 'O' is sometimes mistaken for '0' by inexperienced users.
newValue = newValue.replace(/O/ig, '0');
@@ -275,7 +276,7 @@ Blockly.FieldNumber.prototype.doClassValidation_ = function(opt_newValue) {
newValue = newValue.replace(/infinity/i, 'Infinity');
// Clean up number.
var n = Number(newValue || 0);
let n = Number(newValue || 0);
if (isNaN(n)) {
// Invalid number.
return null;
@@ -299,19 +300,19 @@ Blockly.FieldNumber.prototype.doClassValidation_ = function(opt_newValue) {
* @protected
* @override
*/
Blockly.FieldNumber.prototype.widgetCreate_ = function() {
var htmlInput = Blockly.FieldNumber.superClass_.widgetCreate_.call(this);
FieldNumber.prototype.widgetCreate_ = function() {
const htmlInput = FieldNumber.superClass_.widgetCreate_.call(this);
// Set the accessibility state
if (this.min_ > -Infinity) {
Blockly.utils.aria.setState(htmlInput,
Blockly.utils.aria.State.VALUEMIN, this.min_);
aria.setState(htmlInput, aria.State.VALUEMIN, this.min_);
}
if (this.max_ < Infinity) {
Blockly.utils.aria.setState(htmlInput,
Blockly.utils.aria.State.VALUEMAX, this.max_);
aria.setState(htmlInput, aria.State.VALUEMAX, this.max_);
}
return htmlInput;
};
Blockly.fieldRegistry.register('field_number', Blockly.FieldNumber);
register('field_number', FieldNumber);
exports = FieldNumber;

View File

@@ -12,56 +12,62 @@
*/
'use strict';
goog.provide('Blockly.fieldRegistry');
goog.module('Blockly.fieldRegistry');
goog.module.declareLegacyNamespace();
goog.require('Blockly.registry');
goog.requireType('Blockly.Field');
goog.requireType('Blockly.IRegistrableField');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
/* eslint-disable-next-line no-unused-vars */
const IRegistrableField = goog.requireType('Blockly.IRegistrableField');
const registry = goog.require('Blockly.registry');
/**
* Registers a field type.
* Blockly.fieldRegistry.fromJson uses this registry to
* fieldRegistry.fromJson uses this registry to
* find the appropriate field type.
* @param {string} type The field type name as used in the JSON definition.
* @param {!Blockly.IRegistrableField} fieldClass The field class containing a
* @param {!IRegistrableField} fieldClass The field class containing a
* fromJson function that can construct an instance of the field.
* @throws {Error} if the type name is empty, the field is already
* registered, or the fieldClass is not an object containing a fromJson
* function.
*/
Blockly.fieldRegistry.register = function(type, fieldClass) {
Blockly.registry.register(Blockly.registry.Type.FIELD, type, fieldClass);
const register = function(type, fieldClass) {
registry.register(registry.Type.FIELD, type, fieldClass);
};
exports.register = register;
/**
* Unregisters the field registered with the given type.
* @param {string} type The field type name as used in the JSON definition.
*/
Blockly.fieldRegistry.unregister = function(type) {
Blockly.registry.unregister(Blockly.registry.Type.FIELD, type);
const unregister = function(type) {
registry.unregister(registry.Type.FIELD, type);
};
exports.unregister = unregister;
/**
* Construct a Field from a JSON arg object.
* Finds the appropriate registered field by the type name as registered using
* Blockly.fieldRegistry.register.
* fieldRegistry.register.
* @param {!Object} options A JSON object with a type and options specific
* to the field type.
* @return {?Blockly.Field} The new field instance or null if a field wasn't
* @return {?Field} The new field instance or null if a field wasn't
* found with the given type name
* @package
*/
Blockly.fieldRegistry.fromJson = function(options) {
var fieldObject = /** @type {?Blockly.IRegistrableField} */ (
Blockly.registry.getObject(Blockly.registry.Type.FIELD, options['type']));
const fromJson = function(options) {
const fieldObject = /** @type {?IRegistrableField} */ (
registry.getObject(registry.Type.FIELD, options['type']));
if (!fieldObject) {
console.warn('Blockly could not create a field of type ' + options['type'] +
'. The field is probably not being registered. This could be because' +
' the file is not loaded, the field does not register itself (Issue' +
' #1584), or the registration is not being reached.');
console.warn(
'Blockly could not create a field of type ' + options['type'] +
'. The field is probably not being registered. This could be because' +
' the file is not loaded, the field does not register itself (Issue' +
' #1584), or the registration is not being reached.');
return null;
}
return fieldObject.fromJson(options);
};
/** @package */
exports.fromJson = fromJson;

View File

@@ -10,27 +10,30 @@
*/
'use strict';
goog.provide('Blockly.FieldTextInput');
goog.module('Blockly.FieldTextInput');
goog.module.declareLegacyNamespace();
goog.require('Blockly.browserEvents');
goog.require('Blockly.DropDownDiv');
goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const DropDownDiv = goog.require('Blockly.DropDownDiv');
const Events = goog.require('Blockly.Events');
const Field = goog.require('Blockly.Field');
const KeyCodes = goog.require('Blockly.utils.KeyCodes');
const Msg = goog.require('Blockly.Msg');
const WidgetDiv = goog.require('Blockly.WidgetDiv');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
const aria = goog.require('Blockly.utils.aria');
const browserEvents = goog.require('Blockly.browserEvents');
const dom = goog.require('Blockly.utils.dom');
const fieldRegistry = goog.require('Blockly.fieldRegistry');
const userAgent = goog.require('Blockly.utils.userAgent');
const {inherits} = goog.require('Blockly.utils.object');
const {prompt: blocklyPrompt} = goog.require('Blockly');
const {replaceMessageReferences} = goog.require('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.Msg');
goog.require('Blockly.utils');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.KeyCodes');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.userAgent');
goog.require('Blockly.WidgetDiv');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.WorkspaceSvg');
/**
@@ -41,12 +44,13 @@ goog.requireType('Blockly.WorkspaceSvg');
* changes to the field's value. Takes in a string & returns a validated
* string, or null to abort the change.
* @param {Object=} opt_config A map of options used to configure the field.
* See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/text-input#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.Field}
* @extends {Field}
* @constructor
*/
Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) {
const FieldTextInput = function(opt_value, opt_validator, opt_config) {
/**
* Allow browser to spellcheck this field.
* @type {boolean}
@@ -54,8 +58,8 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) {
*/
this.spellcheck_ = true;
Blockly.FieldTextInput.superClass_.constructor.call(this,
opt_value, opt_validator, opt_config);
FieldTextInput.superClass_.constructor.call(
this, opt_value, opt_validator, opt_config);
/**
* The HTML input element.
@@ -65,14 +69,14 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) {
/**
* Key down event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.onKeyDownWrapper_ = null;
/**
* Key input event data.
* @type {?Blockly.browserEvents.Data}
* @type {?browserEvents.Data}
* @private
*/
this.onKeyInputWrapper_ = null;
@@ -86,30 +90,30 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) {
/**
* The workspace that this field belongs to.
* @type {?Blockly.WorkspaceSvg}
* @type {?WorkspaceSvg}
* @protected
*/
this.workspace_ = null;
};
Blockly.utils.object.inherits(Blockly.FieldTextInput, Blockly.Field);
inherits(FieldTextInput, Field);
/**
* The default value for this field.
* @type {*}
* @protected
*/
Blockly.FieldTextInput.prototype.DEFAULT_VALUE = '';
FieldTextInput.prototype.DEFAULT_VALUE = '';
/**
* Construct a FieldTextInput from a JSON arg object,
* dereferencing any string table references.
* @param {!Object} options A JSON object with options (text, and spellcheck).
* @return {!Blockly.FieldTextInput} The new field instance.
* @return {!FieldTextInput} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldTextInput.fromJson = function(options) {
var text = Blockly.utils.replaceMessageReferences(options['text']);
FieldTextInput.fromJson = function(options) {
const text = replaceMessageReferences(options['text']);
// `this` might be a subclass of FieldTextInput if that class doesn't override
// the static fromJson method.
return new this(text, undefined, options);
@@ -120,24 +124,24 @@ Blockly.FieldTextInput.fromJson = function(options) {
* are not. Editable fields should also be serializable.
* @type {boolean}
*/
Blockly.FieldTextInput.prototype.SERIALIZABLE = true;
FieldTextInput.prototype.SERIALIZABLE = true;
/**
* Pixel size of input border radius.
* Should match blocklyText's border-radius in CSS.
*/
Blockly.FieldTextInput.BORDERRADIUS = 4;
FieldTextInput.BORDERRADIUS = 4;
/**
* Mouse cursor style when over the hotspot that initiates the editor.
*/
Blockly.FieldTextInput.prototype.CURSOR = 'text';
FieldTextInput.prototype.CURSOR = 'text';
/**
* @override
*/
Blockly.FieldTextInput.prototype.configure_ = function(config) {
Blockly.FieldTextInput.superClass_.configure_.call(this, config);
FieldTextInput.prototype.configure_ = function(config) {
FieldTextInput.superClass_.configure_.call(this, config);
if (typeof config['spellcheck'] == 'boolean') {
this.spellcheck_ = config['spellcheck'];
}
@@ -146,17 +150,17 @@ Blockly.FieldTextInput.prototype.configure_ = function(config) {
/**
* @override
*/
Blockly.FieldTextInput.prototype.initView = function() {
FieldTextInput.prototype.initView = function() {
if (this.getConstants().FULL_BLOCK_FIELDS) {
// Step one: figure out if this is the only field on this block.
// Rendering is quite different in that case.
var nFields = 0;
var nConnections = 0;
let nFields = 0;
let nConnections = 0;
// Count the number of fields, excluding text fields
for (var i = 0, input; (input = this.sourceBlock_.inputList[i]); i++) {
for (var j = 0; (input.fieldRow[j]); j++) {
nFields ++;
for (let i = 0, input; (input = this.sourceBlock_.inputList[i]); i++) {
for (let j = 0; (input.fieldRow[j]); j++) {
nFields++;
}
if (input.connection) {
nConnections++;
@@ -184,7 +188,7 @@ Blockly.FieldTextInput.prototype.initView = function() {
* @return {*} A valid string, or null if invalid.
* @protected
*/
Blockly.FieldTextInput.prototype.doClassValidation_ = function(opt_newValue) {
FieldTextInput.prototype.doClassValidation_ = function(opt_newValue) {
if (opt_newValue === null || opt_newValue === undefined) {
return null;
}
@@ -200,15 +204,16 @@ Blockly.FieldTextInput.prototype.doClassValidation_ = function(opt_newValue) {
* the htmlInput_.
* @protected
*/
Blockly.FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) {
FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) {
if (this.isBeingEdited_) {
this.isTextValid_ = false;
var oldValue = this.value_;
const oldValue = this.value_;
// Revert value when the text becomes invalid.
this.value_ = this.htmlInput_.untypedDefaultValue_;
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
Blockly.Events.fire(new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))(
this.sourceBlock_, 'field', this.name || null, oldValue, this.value_));
if (this.sourceBlock_ && Events.isEnabled()) {
Events.fire(new (Events.get(Events.BLOCK_CHANGE))(
this.sourceBlock_, 'field', this.name || null, oldValue,
this.value_));
}
}
};
@@ -221,7 +226,7 @@ Blockly.FieldTextInput.prototype.doValueInvalid_ = function(_invalidValue) {
* that this is a string.
* @protected
*/
Blockly.FieldTextInput.prototype.doValueUpdate_ = function(newValue) {
FieldTextInput.prototype.doValueUpdate_ = function(newValue) {
this.isTextValid_ = true;
this.value_ = newValue;
if (!this.isBeingEdited_) {
@@ -234,14 +239,14 @@ Blockly.FieldTextInput.prototype.doValueUpdate_ = function(newValue) {
* Updates text field to match the colour/style of the block.
* @package
*/
Blockly.FieldTextInput.prototype.applyColour = function() {
FieldTextInput.prototype.applyColour = function() {
if (this.sourceBlock_ && this.getConstants().FULL_BLOCK_FIELDS) {
if (this.borderRect_) {
this.borderRect_.setAttribute('stroke',
this.sourceBlock_.style.colourTertiary);
this.borderRect_.setAttribute(
'stroke', this.sourceBlock_.style.colourTertiary);
} else {
this.sourceBlock_.pathObject.svgPath.setAttribute('fill',
this.getConstants().FIELD_BORDER_RECT_COLOUR);
this.sourceBlock_.pathObject.svgPath.setAttribute(
'fill', this.getConstants().FIELD_BORDER_RECT_COLOUR);
}
}
};
@@ -251,21 +256,19 @@ Blockly.FieldTextInput.prototype.applyColour = function() {
* field's value.
* @protected
*/
Blockly.FieldTextInput.prototype.render_ = function() {
Blockly.FieldTextInput.superClass_.render_.call(this);
FieldTextInput.prototype.render_ = function() {
FieldTextInput.superClass_.render_.call(this);
// This logic is done in render_ rather than doValueInvalid_ or
// doValueUpdate_ so that the code is more centralized.
if (this.isBeingEdited_) {
this.resizeEditor_();
var htmlInput = /** @type {!HTMLElement} */(this.htmlInput_);
const htmlInput = /** @type {!HTMLElement} */ (this.htmlInput_);
if (!this.isTextValid_) {
Blockly.utils.dom.addClass(htmlInput, 'blocklyInvalidInput');
Blockly.utils.aria.setState(htmlInput,
Blockly.utils.aria.State.INVALID, true);
dom.addClass(htmlInput, 'blocklyInvalidInput');
aria.setState(htmlInput, aria.State.INVALID, true);
} else {
Blockly.utils.dom.removeClass(htmlInput, 'blocklyInvalidInput');
Blockly.utils.aria.setState(htmlInput,
Blockly.utils.aria.State.INVALID, false);
dom.removeClass(htmlInput, 'blocklyInvalidInput');
aria.setState(htmlInput, aria.State.INVALID, false);
}
}
};
@@ -274,7 +277,7 @@ Blockly.FieldTextInput.prototype.render_ = function() {
* Set whether this field is spellchecked by the browser.
* @param {boolean} check True if checked.
*/
Blockly.FieldTextInput.prototype.setSpellcheck = function(check) {
FieldTextInput.prototype.setSpellcheck = function(check) {
if (check == this.spellcheck_) {
return;
}
@@ -292,14 +295,11 @@ Blockly.FieldTextInput.prototype.setSpellcheck = function(check) {
* focus. Defaults to false.
* @protected
*/
Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e,
opt_quietInput) {
this.workspace_ =
(/** @type {!Blockly.BlockSvg} */ (this.sourceBlock_)).workspace;
var quietInput = opt_quietInput || false;
if (!quietInput && (Blockly.utils.userAgent.MOBILE ||
Blockly.utils.userAgent.ANDROID ||
Blockly.utils.userAgent.IPAD)) {
FieldTextInput.prototype.showEditor_ = function(_opt_e, opt_quietInput) {
this.workspace_ = (/** @type {!BlockSvg} */ (this.sourceBlock_)).workspace;
const quietInput = opt_quietInput || false;
if (!quietInput &&
(userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD)) {
this.showPromptEditor_();
} else {
this.showInlineEditor_(quietInput);
@@ -311,11 +311,10 @@ Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e,
* Mobile browsers have issues with in-line textareas (focus and keyboards).
* @private
*/
Blockly.FieldTextInput.prototype.showPromptEditor_ = function() {
Blockly.prompt(Blockly.Msg['CHANGE_VALUE_TITLE'], this.getText(),
function(text) {
this.setValue(this.getValueFromEditorText_(text));
}.bind(this));
FieldTextInput.prototype.showPromptEditor_ = function() {
blocklyPrompt(Msg['CHANGE_VALUE_TITLE'], this.getText(), function(text) {
this.setValue(this.getValueFromEditorText_(text));
}.bind(this));
};
/**
@@ -324,14 +323,13 @@ Blockly.FieldTextInput.prototype.showPromptEditor_ = function() {
* focus.
* @private
*/
Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) {
Blockly.WidgetDiv.show(
this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));
FieldTextInput.prototype.showInlineEditor_ = function(quietInput) {
WidgetDiv.show(this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));
this.htmlInput_ = this.widgetCreate_();
this.isBeingEdited_ = true;
if (!quietInput) {
this.htmlInput_.focus({preventScroll:true});
this.htmlInput_.focus({preventScroll: true});
this.htmlInput_.select();
}
};
@@ -341,38 +339,37 @@ Blockly.FieldTextInput.prototype.showInlineEditor_ = function(quietInput) {
* @return {!HTMLElement} The newly created text input editor.
* @protected
*/
Blockly.FieldTextInput.prototype.widgetCreate_ = function() {
Blockly.Events.setGroup(true);
var div = Blockly.WidgetDiv.DIV;
FieldTextInput.prototype.widgetCreate_ = function() {
Events.setGroup(true);
const div = WidgetDiv.DIV;
Blockly.utils.dom.addClass(this.getClickTarget_(), 'editing');
dom.addClass(this.getClickTarget_(), 'editing');
var htmlInput = /** @type {HTMLInputElement} */ (document.createElement('input'));
const htmlInput =
/** @type {HTMLInputElement} */ (document.createElement('input'));
htmlInput.className = 'blocklyHtmlInput';
htmlInput.setAttribute('spellcheck', this.spellcheck_);
var scale = this.workspace_.getScale();
var fontSize =
(this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt';
const scale = this.workspace_.getScale();
const fontSize = (this.getConstants().FIELD_TEXT_FONTSIZE * scale) + 'pt';
div.style.fontSize = fontSize;
htmlInput.style.fontSize = fontSize;
var borderRadius =
(Blockly.FieldTextInput.BORDERRADIUS * scale) + 'px';
let borderRadius = (FieldTextInput.BORDERRADIUS * scale) + 'px';
if (this.fullBlockClickTarget_) {
var bBox = this.getScaledBBox();
const bBox = this.getScaledBBox();
// Override border radius.
borderRadius = (bBox.bottom - bBox.top) / 2 + 'px';
// Pull stroke colour from the existing shadow block
var strokeColour = this.sourceBlock_.getParent() ?
this.sourceBlock_.getParent().style.colourTertiary :
this.sourceBlock_.style.colourTertiary;
const strokeColour = this.sourceBlock_.getParent() ?
this.sourceBlock_.getParent().style.colourTertiary :
this.sourceBlock_.style.colourTertiary;
htmlInput.style.border = (1 * scale) + 'px solid ' + strokeColour;
div.style.borderRadius = borderRadius;
div.style.transition = 'box-shadow 0.25s ease 0s';
if (this.getConstants().FIELD_TEXTINPUT_BOX_SHADOW) {
div.style.boxShadow = 'rgba(255, 255, 255, 0.3) 0 0 0 ' +
(4 * scale) + 'px';
div.style.boxShadow =
'rgba(255, 255, 255, 0.3) 0 0 0 ' + (4 * scale) + 'px';
}
}
htmlInput.style.borderRadius = borderRadius;
@@ -395,7 +392,7 @@ Blockly.FieldTextInput.prototype.widgetCreate_ = function() {
* DOM-references belonging to the editor.
* @protected
*/
Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
FieldTextInput.prototype.widgetDispose_ = function() {
// Non-disposal related things that we do when the editor closes.
this.isBeingEdited_ = false;
this.isTextValid_ = true;
@@ -405,11 +402,11 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
if (this.onFinishEditing_) {
this.onFinishEditing_(this.value_);
}
Blockly.Events.setGroup(false);
Events.setGroup(false);
// Actual disposal.
this.unbindInputEvents_();
var style = Blockly.WidgetDiv.DIV.style;
const style = WidgetDiv.DIV.style;
style.width = 'auto';
style.height = 'auto';
style.fontSize = '';
@@ -417,7 +414,7 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
style.boxShadow = '';
this.htmlInput_ = null;
Blockly.utils.dom.removeClass(this.getClickTarget_(), 'editing');
dom.removeClass(this.getClickTarget_(), 'editing');
};
/**
@@ -426,12 +423,12 @@ Blockly.FieldTextInput.prototype.widgetDispose_ = function() {
* handlers will be bound.
* @protected
*/
Blockly.FieldTextInput.prototype.bindInputEvents_ = function(htmlInput) {
FieldTextInput.prototype.bindInputEvents_ = function(htmlInput) {
// Trap Enter without IME and Esc to hide.
this.onKeyDownWrapper_ = Blockly.browserEvents.conditionalBind(
this.onKeyDownWrapper_ = browserEvents.conditionalBind(
htmlInput, 'keydown', this, this.onHtmlInputKeyDown_);
// Resize after every input change.
this.onKeyInputWrapper_ = Blockly.browserEvents.conditionalBind(
this.onKeyInputWrapper_ = browserEvents.conditionalBind(
htmlInput, 'input', this, this.onHtmlInputChange_);
};
@@ -439,13 +436,13 @@ Blockly.FieldTextInput.prototype.bindInputEvents_ = function(htmlInput) {
* Unbind handlers for user input and workspace size changes.
* @protected
*/
Blockly.FieldTextInput.prototype.unbindInputEvents_ = function() {
FieldTextInput.prototype.unbindInputEvents_ = function() {
if (this.onKeyDownWrapper_) {
Blockly.browserEvents.unbind(this.onKeyDownWrapper_);
browserEvents.unbind(this.onKeyDownWrapper_);
this.onKeyDownWrapper_ = null;
}
if (this.onKeyInputWrapper_) {
Blockly.browserEvents.unbind(this.onKeyInputWrapper_);
browserEvents.unbind(this.onKeyInputWrapper_);
this.onKeyInputWrapper_ = null;
}
};
@@ -455,17 +452,17 @@ Blockly.FieldTextInput.prototype.unbindInputEvents_ = function() {
* @param {!Event} e Keyboard event.
* @protected
*/
Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
if (e.keyCode == Blockly.utils.KeyCodes.ENTER) {
Blockly.WidgetDiv.hide();
Blockly.DropDownDiv.hideWithoutAnimation();
} else if (e.keyCode == Blockly.utils.KeyCodes.ESC) {
FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
if (e.keyCode == KeyCodes.ENTER) {
WidgetDiv.hide();
DropDownDiv.hideWithoutAnimation();
} else if (e.keyCode == KeyCodes.ESC) {
this.setValue(this.htmlInput_.untypedDefaultValue_);
Blockly.WidgetDiv.hide();
Blockly.DropDownDiv.hideWithoutAnimation();
} else if (e.keyCode == Blockly.utils.KeyCodes.TAB) {
Blockly.WidgetDiv.hide();
Blockly.DropDownDiv.hideWithoutAnimation();
WidgetDiv.hide();
DropDownDiv.hideWithoutAnimation();
} else if (e.keyCode == KeyCodes.TAB) {
WidgetDiv.hide();
DropDownDiv.hideWithoutAnimation();
this.sourceBlock_.tab(this, !e.shiftKey);
e.preventDefault();
}
@@ -476,12 +473,12 @@ Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) {
* @param {!Event} _e Keyboard event.
* @private
*/
Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(_e) {
var text = this.htmlInput_.value;
FieldTextInput.prototype.onHtmlInputChange_ = function(_e) {
const text = this.htmlInput_.value;
if (text !== this.htmlInput_.oldValue_) {
this.htmlInput_.oldValue_ = text;
var value = this.getValueFromEditorText_(text);
const value = this.getValueFromEditorText_(text);
this.setValue(value);
this.forceRerender();
this.resizeEditor_();
@@ -495,7 +492,7 @@ Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(_e) {
* @param {*} newValue New value.
* @protected
*/
Blockly.FieldTextInput.prototype.setEditorValue_ = function(newValue) {
FieldTextInput.prototype.setEditorValue_ = function(newValue) {
this.isDirty_ = true;
if (this.isBeingEdited_) {
// In the case this method is passed an invalid value, we still
@@ -511,16 +508,16 @@ Blockly.FieldTextInput.prototype.setEditorValue_ = function(newValue) {
* Resize the editor to fit the text.
* @protected
*/
Blockly.FieldTextInput.prototype.resizeEditor_ = function() {
var div = Blockly.WidgetDiv.DIV;
var bBox = this.getScaledBBox();
FieldTextInput.prototype.resizeEditor_ = function() {
const div = WidgetDiv.DIV;
const bBox = this.getScaledBBox();
div.style.width = bBox.right - bBox.left + 'px';
div.style.height = bBox.bottom - bBox.top + 'px';
// In RTL mode block fields and LTR input fields the left edge moves,
// whereas the right edge is fixed. Reposition the editor.
var x = this.sourceBlock_.RTL ? bBox.right - div.offsetWidth : bBox.left;
var xy = new Blockly.utils.Coordinate(x, bBox.top);
const x = this.sourceBlock_.RTL ? bBox.right - div.offsetWidth : bBox.left;
const xy = new Coordinate(x, bBox.top);
div.style.left = xy.x + 'px';
div.style.top = xy.y + 'px';
@@ -531,20 +528,20 @@ Blockly.FieldTextInput.prototype.resizeEditor_ = function() {
* @return {boolean} True if the field is tab navigable.
* @override
*/
Blockly.FieldTextInput.prototype.isTabNavigable = function() {
FieldTextInput.prototype.isTabNavigable = function() {
return true;
};
/**
* Use the `getText_` developer hook to override the field's text representation.
* When we're currently editing, return the current HTML value instead.
* Otherwise, return null which tells the field to use the default behaviour
* (which is a string cast of the field's value).
* Use the `getText_` developer hook to override the field's text
* representation. When we're currently editing, return the current HTML value
* instead. Otherwise, return null which tells the field to use the default
* behaviour (which is a string cast of the field's value).
* @return {?string} The HTML value if we're editing, otherwise null.
* @protected
* @override
*/
Blockly.FieldTextInput.prototype.getText_ = function() {
FieldTextInput.prototype.getText_ = function() {
if (this.isBeingEdited_ && this.htmlInput_) {
// We are currently editing, return the HTML input value instead.
return this.htmlInput_.value;
@@ -561,7 +558,7 @@ Blockly.FieldTextInput.prototype.getText_ = function() {
* @return {string} The text to show on the HTML input.
* @protected
*/
Blockly.FieldTextInput.prototype.getEditorText_ = function(value) {
FieldTextInput.prototype.getEditorText_ = function(value) {
return String(value);
};
@@ -575,8 +572,10 @@ Blockly.FieldTextInput.prototype.getEditorText_ = function(value) {
* @return {*} The value to store.
* @protected
*/
Blockly.FieldTextInput.prototype.getValueFromEditorText_ = function(text) {
FieldTextInput.prototype.getValueFromEditorText_ = function(text) {
return text;
};
Blockly.fieldRegistry.register('field_input', Blockly.FieldTextInput);
fieldRegistry.register('field_input', FieldTextInput);
exports = FieldTextInput;

View File

@@ -10,26 +10,24 @@
*/
'use strict';
goog.provide('Blockly.FieldVariable');
goog.module('Blockly.FieldVariable');
goog.module.declareLegacyNamespace();
/** @suppress {extraRequire} */
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
const Block = goog.requireType('Blockly.Block');
const FieldDropdown = goog.require('Blockly.FieldDropdown');
const Menu = goog.requireType('Blockly.Menu');
const MenuItem = goog.requireType('Blockly.MenuItem');
const Msg = goog.require('Blockly.Msg');
const Size = goog.require('Blockly.utils.Size');
const VariableModel = goog.require('Blockly.VariableModel');
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');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.FieldDropdown');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.Msg');
goog.require('Blockly.utils');
goog.require('Blockly.utils.object');
goog.require('Blockly.utils.Size');
goog.require('Blockly.VariableModel');
goog.require('Blockly.Variables');
goog.require('Blockly.Xml');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.Menu');
goog.requireType('Blockly.MenuItem');
/**
@@ -44,13 +42,14 @@ goog.requireType('Blockly.MenuItem');
* @param {string=} opt_defaultType The type of variable to create if this
* field's value is not explicitly set. Defaults to ''.
* @param {Object=} opt_config A map of options used to configure the field.
* See the [field creation documentation]{@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/variable#creation}
* See the [field creation documentation]{@link
* https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/variable#creation}
* for a list of properties this parameter supports.
* @extends {Blockly.FieldDropdown}
* @extends {FieldDropdown}
* @constructor
*/
Blockly.FieldVariable = function(varName, opt_validator, opt_variableTypes,
opt_defaultType, opt_config) {
const FieldVariable = function(
varName, opt_validator, opt_variableTypes, opt_defaultType, opt_config) {
// The FieldDropdown constructor expects the field's initial value to be
// the first entry in the menu generator, which it may or may not be.
// Just do the relevant parts of the constructor.
@@ -59,10 +58,10 @@ Blockly.FieldVariable = function(varName, opt_validator, opt_variableTypes,
* An array of options for a dropdown list,
* or a function which generates these options.
* @type {(!Array<!Array>|
* !function(this:Blockly.FieldDropdown): !Array<!Array>)}
* !function(this:FieldDropdown): !Array<!Array>)}
* @protected
*/
this.menuGenerator_ = Blockly.FieldVariable.dropdownCreate;
this.menuGenerator_ = FieldVariable.dropdownCreate;
/**
* The initial variable name passed to this field's constructor, or an
@@ -74,11 +73,11 @@ Blockly.FieldVariable = function(varName, opt_validator, opt_variableTypes,
/**
* The size of the area rendered by the field.
* @type {Blockly.utils.Size}
* @type {Size}
* @protected
* @override
*/
this.size_ = new Blockly.utils.Size(0, 0);
this.size_ = new Size(0, 0);
opt_config && this.configure_(opt_config);
opt_validator && this.setValidator(opt_validator);
@@ -87,19 +86,19 @@ Blockly.FieldVariable = function(varName, opt_validator, opt_variableTypes,
this.setTypes_(opt_variableTypes, opt_defaultType);
}
};
Blockly.utils.object.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);
inherits(FieldVariable, FieldDropdown);
/**
* Construct a FieldVariable from a JSON arg object,
* dereferencing any string table references.
* @param {!Object} options A JSON object with options (variable,
* variableTypes, and defaultType).
* @return {!Blockly.FieldVariable} The new field instance.
* @return {!FieldVariable} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldVariable.fromJson = function(options) {
var varName = Blockly.utils.replaceMessageReferences(options['variable']);
FieldVariable.fromJson = function(options) {
const varName = 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);
@@ -110,15 +109,15 @@ Blockly.FieldVariable.fromJson = function(options) {
* are not. Editable fields should also be serializable.
* @type {boolean}
*/
Blockly.FieldVariable.prototype.SERIALIZABLE = true;
FieldVariable.prototype.SERIALIZABLE = true;
/**
* Configure the field based on the given map of options.
* @param {!Object} config A map of options to configure the field based on.
* @protected
*/
Blockly.FieldVariable.prototype.configure_ = function(config) {
Blockly.FieldVariable.superClass_.configure_.call(this, config);
FieldVariable.prototype.configure_ = function(config) {
FieldVariable.superClass_.configure_.call(this, config);
this.setTypes_(config['variableTypes'], config['defaultType']);
};
@@ -128,13 +127,13 @@ Blockly.FieldVariable.prototype.configure_ = function(config) {
* variable rather than let the value be invalid.
* @package
*/
Blockly.FieldVariable.prototype.initModel = function() {
FieldVariable.prototype.initModel = function() {
if (this.variable_) {
return; // Initialization already happened.
}
var variable = Blockly.Variables.getOrCreateVariablePackage(
this.sourceBlock_.workspace, null,
this.defaultVariableName, this.defaultType_);
const variable = Variables.getOrCreateVariablePackage(
this.sourceBlock_.workspace, null, this.defaultVariableName,
this.defaultType_);
// Don't call setValue because we don't want to cause a rerender.
this.doValueUpdate_(variable.getId());
@@ -143,10 +142,10 @@ Blockly.FieldVariable.prototype.initModel = function() {
/**
* @override
*/
Blockly.FieldVariable.prototype.shouldAddBorderRect_ = function() {
return Blockly.FieldVariable.superClass_.shouldAddBorderRect_.call(this) &&
(!this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW ||
this.sourceBlock_.type != 'variables_get');
FieldVariable.prototype.shouldAddBorderRect_ = function() {
return FieldVariable.superClass_.shouldAddBorderRect_.call(this) &&
(!this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW ||
this.sourceBlock_.type != 'variables_get');
};
/**
@@ -154,23 +153,24 @@ Blockly.FieldVariable.prototype.shouldAddBorderRect_ = function() {
* @param {!Element} fieldElement The element containing information about the
* variable field's state.
*/
Blockly.FieldVariable.prototype.fromXml = function(fieldElement) {
var id = fieldElement.getAttribute('id');
var variableName = fieldElement.textContent;
FieldVariable.prototype.fromXml = function(fieldElement) {
const id = fieldElement.getAttribute('id');
const variableName = fieldElement.textContent;
// 'variabletype' should be lowercase, but until July 2019 it was sometimes
// recorded as 'variableType'. Thus we need to check for both.
var variableType = fieldElement.getAttribute('variabletype') ||
const variableType = fieldElement.getAttribute('variabletype') ||
fieldElement.getAttribute('variableType') || '';
var variable = Blockly.Variables.getOrCreateVariablePackage(
const variable = Variables.getOrCreateVariablePackage(
this.sourceBlock_.workspace, id, variableName, variableType);
// This should never happen :)
if (variableType != null && variableType !== variable.type) {
throw Error('Serialized variable type with id \'' +
variable.getId() + '\' had type ' + variable.type + ', and ' +
'does not match variable field that references it: ' +
Blockly.Xml.domToText(fieldElement) + '.');
throw Error(
'Serialized variable type with id \'' + variable.getId() +
'\' had type ' + variable.type + ', and ' +
'does not match variable field that references it: ' +
Xml.domToText(fieldElement) + '.');
}
this.setValue(variable.getId());
@@ -182,7 +182,7 @@ Blockly.FieldVariable.prototype.fromXml = function(fieldElement) {
* field's state.
* @return {!Element} The element containing info about the field's state.
*/
Blockly.FieldVariable.prototype.toXml = function(fieldElement) {
FieldVariable.prototype.toXml = function(fieldElement) {
// Make sure the variable is initialized.
this.initModel();
@@ -196,20 +196,20 @@ Blockly.FieldVariable.prototype.toXml = function(fieldElement) {
/**
* Attach this field to a block.
* @param {!Blockly.Block} block The block containing this field.
* @param {!Block} block The block containing this field.
*/
Blockly.FieldVariable.prototype.setSourceBlock = function(block) {
FieldVariable.prototype.setSourceBlock = function(block) {
if (block.isShadow()) {
throw Error('Variable fields are not allowed to exist on shadow blocks.');
}
Blockly.FieldVariable.superClass_.setSourceBlock.call(this, block);
FieldVariable.superClass_.setSourceBlock.call(this, block);
};
/**
* Get the variable's ID.
* @return {string} Current variable's ID.
*/
Blockly.FieldVariable.prototype.getValue = function() {
FieldVariable.prototype.getValue = function() {
return this.variable_ ? this.variable_.getId() : null;
};
@@ -218,7 +218,7 @@ Blockly.FieldVariable.prototype.getValue = function() {
* @return {string} The selected variable's name, or the empty string if no
* variable is selected.
*/
Blockly.FieldVariable.prototype.getText = function() {
FieldVariable.prototype.getText = function() {
return this.variable_ ? this.variable_.name : '';
};
@@ -226,11 +226,11 @@ Blockly.FieldVariable.prototype.getText = function() {
* Get the variable model for the selected variable.
* Not guaranteed to be in the variable map on the workspace (e.g. if accessed
* after the variable has been deleted).
* @return {?Blockly.VariableModel} The selected variable, or null if none was
* @return {?VariableModel} The selected variable, or null if none was
* selected.
* @package
*/
Blockly.FieldVariable.prototype.getVariable = function() {
FieldVariable.prototype.getVariable = function() {
return this.variable_;
};
@@ -241,7 +241,7 @@ Blockly.FieldVariable.prototype.getVariable = function() {
* a block and workspace at that point.
* @return {?Function} Validation function, or null.
*/
Blockly.FieldVariable.prototype.getValidator = function() {
FieldVariable.prototype.getValidator = function() {
// Validators shouldn't operate on the initial setValue call.
// Normally this is achieved by calling setValidator after setValue, but
// this is not a possibility with variable fields.
@@ -257,20 +257,20 @@ Blockly.FieldVariable.prototype.getValidator = function() {
* @return {?string} The validated ID, or null if invalid.
* @protected
*/
Blockly.FieldVariable.prototype.doClassValidation_ = function(opt_newValue) {
FieldVariable.prototype.doClassValidation_ = function(opt_newValue) {
if (opt_newValue === null) {
return null;
}
var newId = /** @type {string} */ (opt_newValue);
var variable = Blockly.Variables.getVariable(
this.sourceBlock_.workspace, newId);
const newId = /** @type {string} */ (opt_newValue);
const variable = Variables.getVariable(this.sourceBlock_.workspace, newId);
if (!variable) {
console.warn('Variable id doesn\'t point to a real variable! ' +
console.warn(
'Variable id doesn\'t point to a real variable! ' +
'ID was ' + newId);
return null;
}
// Type Checks.
var type = variable.type;
const type = variable.type;
if (!this.typeIsAllowed_(type)) {
console.warn('Variable type doesn\'t match this field! Type was ' + type);
return null;
@@ -286,10 +286,10 @@ Blockly.FieldVariable.prototype.doClassValidation_ = function(opt_newValue) {
* @param {*} newId The value to be saved.
* @protected
*/
Blockly.FieldVariable.prototype.doValueUpdate_ = function(newId) {
this.variable_ = Blockly.Variables.getVariable(
FieldVariable.prototype.doValueUpdate_ = function(newId) {
this.variable_ = Variables.getVariable(
this.sourceBlock_.workspace, /** @type {string} */ (newId));
Blockly.FieldVariable.superClass_.doValueUpdate_.call(this, newId);
FieldVariable.superClass_.doValueUpdate_.call(this, newId);
};
/**
@@ -298,12 +298,12 @@ Blockly.FieldVariable.prototype.doValueUpdate_ = function(newId) {
* @return {boolean} True if the type is in the list of allowed types.
* @private
*/
Blockly.FieldVariable.prototype.typeIsAllowed_ = function(type) {
var typeList = this.getVariableTypes_();
FieldVariable.prototype.typeIsAllowed_ = function(type) {
const typeList = this.getVariableTypes_();
if (!typeList) {
return true; // If it's null, all types are valid.
}
for (var i = 0; i < typeList.length; i++) {
for (let i = 0; i < typeList.length; i++) {
if (type == typeList[i]) {
return true;
}
@@ -317,9 +317,9 @@ Blockly.FieldVariable.prototype.typeIsAllowed_ = function(type) {
* @throws {Error} if variableTypes is an empty array.
* @private
*/
Blockly.FieldVariable.prototype.getVariableTypes_ = function() {
FieldVariable.prototype.getVariableTypes_ = function() {
// TODO (#1513): Try to avoid calling this every time the field is edited.
var variableTypes = this.variableTypes;
let variableTypes = this.variableTypes;
if (variableTypes === null) {
// If variableTypes is null, return all variable types.
if (this.sourceBlock_ && this.sourceBlock_.workspace) {
@@ -329,9 +329,9 @@ Blockly.FieldVariable.prototype.getVariableTypes_ = function() {
variableTypes = variableTypes || [''];
if (variableTypes.length == 0) {
// Throw an error if variableTypes is an empty list.
var name = this.getText();
throw Error('\'variableTypes\' of field variable ' +
name + ' was an empty list');
const name = this.getText();
throw Error(
'\'variableTypes\' of field variable ' + name + ' was an empty list');
}
return variableTypes;
};
@@ -346,29 +346,32 @@ Blockly.FieldVariable.prototype.getVariableTypes_ = function() {
* field's value is not explicitly set. Defaults to ''.
* @private
*/
Blockly.FieldVariable.prototype.setTypes_ = function(opt_variableTypes,
opt_defaultType) {
FieldVariable.prototype.setTypes_ = function(
opt_variableTypes, opt_defaultType) {
// If you expected that the default type would be the same as the only entry
// in the variable types array, tell the Blockly team by commenting on #1499.
var defaultType = opt_defaultType || '';
const defaultType = opt_defaultType || '';
let variableTypes;
// Set the allowable variable types. Null means all types on the workspace.
if (opt_variableTypes == null || opt_variableTypes == undefined) {
var variableTypes = null;
variableTypes = null;
} else if (Array.isArray(opt_variableTypes)) {
var variableTypes = opt_variableTypes;
variableTypes = opt_variableTypes;
// Make sure the default type is valid.
var isInArray = false;
for (var i = 0; i < variableTypes.length; i++) {
let isInArray = false;
for (let i = 0; i < variableTypes.length; i++) {
if (variableTypes[i] == defaultType) {
isInArray = true;
}
}
if (!isInArray) {
throw Error('Invalid default type \'' + defaultType + '\' in ' +
throw Error(
'Invalid default type \'' + defaultType + '\' in ' +
'the definition of a FieldVariable');
}
} else {
throw Error('\'variableTypes\' was not an array in the definition of ' +
throw Error(
'\'variableTypes\' was not an array in the definition of ' +
'a FieldVariable');
}
// Only update the field once all checks pass.
@@ -382,7 +385,7 @@ Blockly.FieldVariable.prototype.setTypes_ = function(opt_variableTypes,
* be called by the block.
* @package
*/
Blockly.FieldVariable.prototype.refreshVariableName = function() {
FieldVariable.prototype.refreshVariableName = function() {
this.forceRerender();
};
@@ -390,41 +393,40 @@ Blockly.FieldVariable.prototype.refreshVariableName = function() {
* Return a sorted list of variable names for variable dropdown menus.
* Include a special option at the end for creating a new variable name.
* @return {!Array<!Array>} Array of variable names/id tuples.
* @this {Blockly.FieldVariable}
* @this {FieldVariable}
*/
Blockly.FieldVariable.dropdownCreate = function() {
FieldVariable.dropdownCreate = function() {
if (!this.variable_) {
throw Error('Tried to call dropdownCreate on a variable field with no' +
throw Error(
'Tried to call dropdownCreate on a variable field with no' +
' variable selected.');
}
var name = this.getText();
var variableModelList = [];
const name = this.getText();
let variableModelList = [];
if (this.sourceBlock_ && this.sourceBlock_.workspace) {
var variableTypes = this.getVariableTypes_();
const variableTypes = this.getVariableTypes_();
// Get a copy of the list, so that adding rename and new variable options
// doesn't modify the workspace's list.
for (var i = 0; i < variableTypes.length; i++) {
var variableType = variableTypes[i];
var variables =
this.sourceBlock_.workspace.getVariablesOfType(variableType);
for (let i = 0; i < variableTypes.length; i++) {
const variableType = variableTypes[i];
const variables =
this.sourceBlock_.workspace.getVariablesOfType(variableType);
variableModelList = variableModelList.concat(variables);
}
}
variableModelList.sort(Blockly.VariableModel.compareByName);
variableModelList.sort(VariableModel.compareByName);
var options = [];
for (var i = 0; i < variableModelList.length; i++) {
const options = [];
for (let i = 0; i < variableModelList.length; i++) {
// Set the UUID as the internal representation of the variable.
options[i] = [variableModelList[i].name, variableModelList[i].getId()];
}
options.push([Blockly.Msg['RENAME_VARIABLE'], Blockly.RENAME_VARIABLE_ID]);
if (Blockly.Msg['DELETE_VARIABLE']) {
options.push(
[
Blockly.Msg['DELETE_VARIABLE'].replace('%1', name),
Blockly.DELETE_VARIABLE_ID
]
);
options.push([Msg['RENAME_VARIABLE'], internalConstants.RENAME_VARIABLE_ID]);
if (Msg['DELETE_VARIABLE']) {
options.push([
Msg['DELETE_VARIABLE'].replace('%1', name),
internalConstants.DELETE_VARIABLE_ID
]);
}
return options;
@@ -434,20 +436,19 @@ Blockly.FieldVariable.dropdownCreate = function() {
* Handle the selection of an item in the variable dropdown menu.
* Special case the 'Rename variable...' and 'Delete variable...' options.
* In the rename case, prompt the user for a new name.
* @param {!Blockly.Menu} menu The Menu component clicked.
* @param {!Blockly.MenuItem} menuItem The MenuItem selected within menu.
* @param {!Menu} menu The Menu component clicked.
* @param {!MenuItem} menuItem The MenuItem selected within menu.
* @protected
*/
Blockly.FieldVariable.prototype.onItemSelected_ = function(menu, menuItem) {
var id = menuItem.getValue();
FieldVariable.prototype.onItemSelected_ = function(menu, menuItem) {
const id = menuItem.getValue();
// Handle special cases.
if (this.sourceBlock_ && this.sourceBlock_.workspace) {
if (id == Blockly.RENAME_VARIABLE_ID) {
if (id == internalConstants.RENAME_VARIABLE_ID) {
// Rename variable.
Blockly.Variables.renameVariable(
this.sourceBlock_.workspace, this.variable_);
Variables.renameVariable(this.sourceBlock_.workspace, this.variable_);
return;
} else if (id == Blockly.DELETE_VARIABLE_ID) {
} else if (id == internalConstants.DELETE_VARIABLE_ID) {
// Delete variable.
this.sourceBlock_.workspace.deleteVariableById(this.variable_.getId());
return;
@@ -463,8 +464,10 @@ Blockly.FieldVariable.prototype.onItemSelected_ = function(menu, menuItem) {
* @package
* @override
*/
Blockly.FieldVariable.prototype.referencesVariables = function() {
FieldVariable.prototype.referencesVariables = function() {
return true;
};
Blockly.fieldRegistry.register('field_variable', Blockly.FieldVariable);
fieldRegistry.register('field_variable', FieldVariable);
exports = FieldVariable;

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,9 +15,8 @@ goog.provide('Blockly.InsertionMarkerManager');
goog.require('Blockly.blockAnimations');
goog.require('Blockly.ComponentManager');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.Events');
goog.require('Blockly.internalConstants');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.RenderedConnection');
@@ -378,8 +377,10 @@ Blockly.InsertionMarkerManager.prototype.shouldUpdatePreviews_ = function(
var yDiff = this.localConnection_.y + dxy.y - this.closestConnection_.y;
var curDistance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
// Slightly prefer the existing preview over a new preview.
return !(candidateClosest && radius > curDistance -
Blockly.CURRENT_CONNECTION_PREFERENCE);
return !(
candidateClosest &&
radius > curDistance -
Blockly.internalConstants.CURRENT_CONNECTION_PREFERENCE);
} else if (!this.localConnection_ && !this.closestConnection_) {
// We weren't showing a preview before, but we should now.
return true;
@@ -439,9 +440,9 @@ Blockly.InsertionMarkerManager.prototype.getStartRadius_ = function() {
// By increasing radiusConnection when a connection already exists,
// we never "lose" the connection from the offset.
if (this.closestConnection_ && this.localConnection_) {
return Blockly.CONNECTING_SNAP_RADIUS;
return Blockly.internalConstants.CONNECTING_SNAP_RADIUS;
}
return Blockly.SNAP_RADIUS;
return Blockly.internalConstants.SNAP_RADIUS;
};
/**

View File

@@ -1,75 +0,0 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview AST Node and keyboard navigation interfaces.
* @author samelh@google.com (Sam El-Husseini)
*/
'use strict';
goog.provide('Blockly.IASTNodeLocation');
goog.provide('Blockly.IASTNodeLocationSvg');
goog.provide('Blockly.IASTNodeLocationWithBlock');
goog.provide('Blockly.IKeyboardAccessible');
goog.requireType('Blockly.Block');
goog.requireType('Blockly.ShortcutRegistry');
/**
* An AST node location interface.
* @interface
*/
Blockly.IASTNodeLocation = function() {};
/**
* An AST node location SVG interface.
* @interface
* @extends {Blockly.IASTNodeLocation}
*/
Blockly.IASTNodeLocationSvg = function() {};
/**
* Add the marker SVG to this node's SVG group.
* @param {SVGElement} markerSvg The SVG root of the marker to be added to the
* SVG group.
*/
Blockly.IASTNodeLocationSvg.prototype.setMarkerSvg;
/**
* Add the cursor SVG to this node's SVG group.
* @param {SVGElement} cursorSvg The SVG root of the cursor to be added to the
* SVG group.
*/
Blockly.IASTNodeLocationSvg.prototype.setCursorSvg;
/**
* An AST node location that has an associated block.
* @interface
* @extends {Blockly.IASTNodeLocation}
*/
Blockly.IASTNodeLocationWithBlock = function() {};
/**
* Get the source block associated with this node.
* @return {Blockly.Block} The source block.
*/
Blockly.IASTNodeLocationWithBlock.prototype.getSourceBlock;
/**
* An interface for an object that handles keyboard shortcuts.
* @interface
*/
Blockly.IKeyboardAccessible = function() {};
/**
* Handles the given keyboard shortcut.
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} shortcut The shortcut to be handled.
* @return {boolean} True if the shortcut has been handled, false otherwise.
*/
Blockly.IKeyboardAccessible.prototype.onShortcut;

View File

@@ -0,0 +1,23 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for an AST node location.
* @author samelh@google.com (Sam El-Husseini)
*/
'use strict';
goog.module('Blockly.IASTNodeLocation');
goog.module.declareLegacyNamespace();
/**
* An AST node location interface.
* @interface
*/
const IASTNodeLocation = function() {};
exports = IASTNodeLocation;

View File

@@ -0,0 +1,42 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for an AST node location SVG.
* @author samelh@google.com (Sam El-Husseini)
*/
'use strict';
goog.module('Blockly.IASTNodeLocationSvg');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IASTNodeLocation = goog.require('Blockly.IASTNodeLocation');
/**
* An AST node location SVG interface.
* @interface
* @extends {IASTNodeLocation}
*/
const IASTNodeLocationSvg = function() {};
/**
* Add the marker SVG to this node's SVG group.
* @param {SVGElement} markerSvg The SVG root of the marker to be added to the
* SVG group.
*/
IASTNodeLocationSvg.prototype.setMarkerSvg;
/**
* Add the cursor SVG to this node's SVG group.
* @param {SVGElement} cursorSvg The SVG root of the cursor to be added to the
* SVG group.
*/
IASTNodeLocationSvg.prototype.setCursorSvg;
exports = IASTNodeLocationSvg;

View File

@@ -0,0 +1,37 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for an AST node location that has an associated
* block.
* @author samelh@google.com (Sam El-Husseini)
*/
'use strict';
goog.module('Blockly.IASTNodeLocationWithBlock');
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');
/**
* An AST node location that has an associated block.
* @interface
* @extends {IASTNodeLocation}
*/
const IASTNodeLocationWithBlock = function() {};
/**
* Get the source block associated with this node.
* @return {Block} The source block.
*/
IASTNodeLocationWithBlock.prototype.getSourceBlock;
exports = IASTNodeLocationWithBlock;

View File

@@ -12,21 +12,25 @@
'use strict';
goog.provide('Blockly.IAutoHideable');
goog.module('Blockly.IAutoHideable');
goog.module.declareLegacyNamespace();
goog.require('Blockly.IComponent');
/* eslint-disable-next-line no-unused-vars */
const IComponent = goog.require('Blockly.IComponent');
/**
* Interface for a component that can be automatically hidden.
* @extends {Blockly.IComponent}
* @extends {IComponent}
* @interface
*/
Blockly.IAutoHideable = function() {};
const IAutoHideable = function() {};
/**
* Hides the component. Called in Blockly.hideChaff.
* @param {boolean} onlyClosePopups Whether only popups should be closed.
* Flyouts should not be closed if this is true.
*/
Blockly.IAutoHideable.prototype.autoHide;
IAutoHideable.prototype.autoHide;
exports = IAutoHideable;

View File

@@ -11,48 +11,53 @@
'use strict';
goog.provide('Blockly.IBlockDragger');
goog.module('Blockly.IBlockDragger');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const Coordinate = goog.requireType('Blockly.utils.Coordinate');
/**
* A block dragger interface.
* @interface
*/
Blockly.IBlockDragger = function() {};
const IBlockDragger = function() {};
/**
* Start dragging a block. This includes moving it to the drag surface.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at mouse down, in pixel units.
* @param {boolean} healStack Whether or not to heal the stack after
* disconnecting.
*/
Blockly.IBlockDragger.prototype.startDrag;
IBlockDragger.prototype.startDrag;
/**
* Execute a step of block dragging, based on the given event. Update the
* display accordingly.
* @param {!Event} e The most recent move event.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at the start of the drag, in pixel units.
*/
Blockly.IBlockDragger.prototype.drag;
IBlockDragger.prototype.drag;
/**
* Finish a block drag and put the block back on the workspace.
* @param {!Event} e The mouseup/touchend event.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at the start of the drag, in pixel units.
*/
Blockly.IBlockDragger.prototype.endDrag;
IBlockDragger.prototype.endDrag;
/**
* 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.
*/
Blockly.IBlockDragger.prototype.getInsertionMarkers;
IBlockDragger.prototype.getInsertionMarkers;
exports = IBlockDragger;

View File

@@ -11,28 +11,32 @@
'use strict';
goog.provide('Blockly.IBoundedElement');
goog.module('Blockly.IBoundedElement');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.utils.Rect');
/* eslint-disable-next-line no-unused-vars */
const Rect = goog.requireType('Blockly.utils.Rect');
/**
* A bounded element interface.
* @interface
*/
Blockly.IBoundedElement = function() {};
const IBoundedElement = function() {};
/**
* Returns the coordinates of a bounded element describing the dimensions of the
* element.
* Coordinate system: workspace coordinates.
* @return {!Blockly.utils.Rect} Object with coordinates of the bounded element.
* @return {!Rect} Object with coordinates of the bounded element.
*/
Blockly.IBoundedElement.prototype.getBoundingRectangle;
IBoundedElement.prototype.getBoundingRectangle;
/**
* Move the element by a relative offset.
* @param {number} dx Horizontal offset in workspace units.
* @param {number} dy Vertical offset in workspace units.
*/
Blockly.IBoundedElement.prototype.moveBy;
IBoundedElement.prototype.moveBy;
exports = IBoundedElement;

View File

@@ -11,35 +11,39 @@
'use strict';
goog.provide('Blockly.IBubble');
goog.module('Blockly.IBubble');
goog.module.declareLegacyNamespace();
goog.require('Blockly.IContextMenu');
goog.require('Blockly.IDraggable');
goog.requireType('Blockly.BlockDragSurfaceSvg');
goog.requireType('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
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');
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.require('Blockly.IDraggable');
/**
* A bubble interface.
* @interface
* @extends {Blockly.IDraggable}
* @extends {Blockly.IContextMenu}
* @extends {IDraggable}
* @extends {IContextMenu}
*/
Blockly.IBubble = function() {};
const IBubble = function() {};
/**
* 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.IBubble.prototype.getRelativeToSurfaceXY;
IBubble.prototype.getRelativeToSurfaceXY;
/**
* Return the root node of the bubble's SVG group.
* @return {!SVGElement} The root SVG node of the bubble's group.
*/
Blockly.IBubble.prototype.getSvgRoot;
IBubble.prototype.getSvgRoot;
/**
* Set whether auto-layout of this bubble is enabled. The first time a bubble
@@ -48,39 +52,41 @@ Blockly.IBubble.prototype.getSvgRoot;
* @param {boolean} enable True if auto-layout should be enabled, false
* otherwise.
*/
Blockly.IBubble.prototype.setAutoLayout;
IBubble.prototype.setAutoLayout;
/**
* Triggers a move callback if one exists at the end of a drag.
* @param {boolean} adding True if adding, false if removing.
*/
Blockly.IBubble.prototype.setDragging;
IBubble.prototype.setDragging;
/**
* 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.
*/
Blockly.IBubble.prototype.moveDuringDrag;
IBubble.prototype.moveDuringDrag;
/**
* Move the bubble to the specified location in workspace coordinates.
* @param {number} x The x position to move to.
* @param {number} y The y position to move to.
*/
Blockly.IBubble.prototype.moveTo;
IBubble.prototype.moveTo;
/**
* Update the style of this bubble when it is dragged over a delete area.
* @param {boolean} enable True if the bubble is about to be deleted, false
* otherwise.
*/
Blockly.IBubble.prototype.setDeleteStyle;
IBubble.prototype.setDeleteStyle;
/**
* Dispose of this bubble.
*/
Blockly.IBubble.prototype.dispose;
IBubble.prototype.dispose;
exports = IBubble;

View File

@@ -0,0 +1,50 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for a collapsible toolbox item.
* @author kozbial@google.com (Monica Kozbial)
*/
'use strict';
goog.module('Blockly.ICollapsibleToolboxItem');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const ISelectableToolboxItem = goog.require('Blockly.ISelectableToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const IToolboxItem = goog.requireType('Blockly.IToolboxItem');
/**
* Interface for an item in the toolbox that can be collapsed.
* @extends {ISelectableToolboxItem}
* @interface
*/
const ICollapsibleToolboxItem = function() {};
/**
* Gets any children toolbox items. (ex. Gets the subcategories)
* @return {!Array<!IToolboxItem>} The child toolbox items.
*/
ICollapsibleToolboxItem.prototype.getChildToolboxItems;
/**
* Whether the toolbox item is expanded to show its child subcategories.
* @return {boolean} True if the toolbox item shows its children, false if it
* is collapsed.
* @public
*/
ICollapsibleToolboxItem.prototype.isExpanded;
/**
* Toggles whether or not the toolbox item is expanded.
* @public
*/
ICollapsibleToolboxItem.prototype.toggleExpanded;
exports = ICollapsibleToolboxItem;

View File

@@ -12,7 +12,8 @@
'use strict';
goog.provide('Blockly.IComponent');
goog.module('Blockly.IComponent');
goog.module.declareLegacyNamespace();
/**
@@ -20,11 +21,13 @@ goog.provide('Blockly.IComponent');
* ComponentManager.
* @interface
*/
Blockly.IComponent = function() {};
const IComponent = function() {};
/**
* The unique id for this component that is used to register with the
* ComponentManager.
* @type {string}
*/
Blockly.IComponent.id;
IComponent.id;
exports = IComponent;

View File

@@ -11,23 +11,26 @@
*/
'use strict';
goog.provide('Blockly.IConnectionChecker');
goog.module('Blockly.IConnectionChecker');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.Connection');
goog.requireType('Blockly.RenderedConnection');
/* eslint-disable-next-line no-unused-vars */
const Connection = goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
/**
* Class for connection type checking logic.
* @interface
*/
Blockly.IConnectionChecker = function() {};
const IConnectionChecker = function() {};
/**
* Check whether the current connection can connect with the target
* connection.
* @param {Blockly.Connection} a Connection to check compatibility with.
* @param {Blockly.Connection} b Connection to check compatibility with.
* @param {Connection} a Connection to check compatibility with.
* @param {Connection} b Connection to check compatibility with.
* @param {boolean} isDragging True if the connection is being made by dragging
* a block.
* @param {number=} opt_distance The max allowable distance between the
@@ -35,61 +38,63 @@ Blockly.IConnectionChecker = function() {};
* @return {boolean} Whether the connection is legal.
* @public
*/
Blockly.IConnectionChecker.prototype.canConnect;
IConnectionChecker.prototype.canConnect;
/**
* Checks whether the current connection can connect with the target
* connection, and return an error code if there are problems.
* @param {Blockly.Connection} a Connection to check compatibility with.
* @param {Blockly.Connection} b Connection to check compatibility with.
* @param {Connection} a Connection to check compatibility with.
* @param {Connection} b Connection to check compatibility with.
* @param {boolean} isDragging True if the connection is being made by dragging
* a block.
* @param {number=} opt_distance The max allowable distance between the
* connections for drag checks.
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
* @return {number} Connection.CAN_CONNECT if the connection is legal,
* an error code otherwise.
* @public
*/
Blockly.IConnectionChecker.prototype.canConnectWithReason;
IConnectionChecker.prototype.canConnectWithReason;
/**
* Helper method that translates a connection error code into a string.
* @param {number} errorCode The error code.
* @param {Blockly.Connection} a One of the two connections being checked.
* @param {Blockly.Connection} b The second of the two connections being
* @param {Connection} a One of the two connections being checked.
* @param {Connection} b The second of the two connections being
* checked.
* @return {string} A developer-readable error string.
* @public
*/
Blockly.IConnectionChecker.prototype.getErrorMessage;
IConnectionChecker.prototype.getErrorMessage;
/**
* Check that connecting the given connections is safe, meaning that it would
* not break any of Blockly's basic assumptions (e.g. no self connections).
* @param {Blockly.Connection} a The first of the connections to check.
* @param {Blockly.Connection} b The second of the connections to check.
* @param {Connection} a The first of the connections to check.
* @param {Connection} b The second of the connections to check.
* @return {number} An enum with the reason this connection is safe or unsafe.
* @public
*/
Blockly.IConnectionChecker.prototype.doSafetyChecks;
IConnectionChecker.prototype.doSafetyChecks;
/**
* Check whether this connection is compatible with another connection with
* respect to the value type system. E.g. square_root("Hello") is not
* compatible.
* @param {!Blockly.Connection} a Connection to compare.
* @param {!Blockly.Connection} b Connection to compare against.
* @param {!Connection} a Connection to compare.
* @param {!Connection} b Connection to compare against.
* @return {boolean} True if the connections share a type.
* @public
*/
Blockly.IConnectionChecker.prototype.doTypeChecks;
IConnectionChecker.prototype.doTypeChecks;
/**
* Check whether this connection can be made by dragging.
* @param {!Blockly.RenderedConnection} a Connection to compare.
* @param {!Blockly.RenderedConnection} b Connection to compare against.
* @param {!RenderedConnection} a Connection to compare.
* @param {!RenderedConnection} b Connection to compare against.
* @param {number} distance The maximum allowable distance between connections.
* @return {boolean} True if the connection is allowed during a drag.
* @public
*/
Blockly.IConnectionChecker.prototype.doDragChecks;
IConnectionChecker.prototype.doDragChecks;
exports = IConnectionChecker;

View File

@@ -11,16 +11,19 @@
'use strict';
goog.provide('Blockly.IContextMenu');
goog.module('Blockly.IContextMenu');
goog.module.declareLegacyNamespace();
/**
* @interface
*/
Blockly.IContextMenu = function() {};
const IContextMenu = function() {};
/**
* Show the context menu for this object.
* @param {!Event} e Mouse event.
*/
Blockly.IContextMenu.prototype.showContextMenu;
IContextMenu.prototype.showContextMenu;
exports = IContextMenu;

View File

@@ -11,30 +11,35 @@
'use strict';
goog.provide('Blockly.ICopyable');
goog.module('Blockly.ICopyable');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.ISelectable');
goog.requireType('Blockly.WorkspaceSvg');
/* eslint-disable-next-line no-unused-vars */
const ISelectable = goog.requireType('Blockly.ISelectable');
/* eslint-disable-next-line no-unused-vars */
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
/**
* @extends {Blockly.ISelectable}
* @extends {ISelectable}
* @interface
*/
Blockly.ICopyable = function() {};
const ICopyable = function() {};
/**
* Encode for copying.
* @return {?Blockly.ICopyable.CopyData} Copy metadata.
* @return {?ICopyable.CopyData} Copy metadata.
*/
Blockly.ICopyable.prototype.toCopyData;
ICopyable.prototype.toCopyData;
/**
* Copy Metadata.
* @typedef {{
* xml:!Element,
* source:Blockly.WorkspaceSvg,
* source:WorkspaceSvg,
* typeCounts:?Object
* }}
*/
Blockly.ICopyable.CopyData;
ICopyable.CopyData;
exports = ICopyable;

View File

@@ -11,17 +11,20 @@
'use strict';
goog.provide('Blockly.IDeletable');
goog.module('Blockly.IDeletable');
goog.module.declareLegacyNamespace();
/**
* The interface for an object that can be deleted.
* @interface
*/
Blockly.IDeletable = function() {};
const IDeletable = function() {};
/**
* Get whether this object is deletable or not.
* @return {boolean} True if deletable.
*/
Blockly.IDeletable.prototype.isDeletable;
IDeletable.prototype.isDeletable;
exports = IDeletable;

View File

@@ -12,31 +12,35 @@
'use strict';
goog.provide('Blockly.IDeleteArea');
goog.module('Blockly.IDeleteArea');
goog.module.declareLegacyNamespace();
goog.require('Blockly.IDragTarget');
goog.requireType('Blockly.IDraggable');
/* 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');
/**
* Interface for a component that can delete a block or bubble that is dropped
* on top of it.
* @extends {Blockly.IDragTarget}
* @extends {IDragTarget}
* @interface
*/
Blockly.IDeleteArea = function() {};
const IDeleteArea = function() {};
/**
* Returns whether the provided block or bubble would be deleted if dropped on
* this area.
* This method should check if the element is deletable and is always called
* before onDragEnter/onDragOver/onDragExit.
* @param {!Blockly.IDraggable} element The block or bubble currently being
* @param {!IDraggable} element The block or bubble currently being
* dragged.
* @param {boolean} couldConnect Whether the element could could connect to
* another.
* @return {boolean} Whether the element provided would be deleted if dropped on
* this area.
*/
Blockly.IDeleteArea.prototype.wouldDelete;
IDeleteArea.prototype.wouldDelete;
exports = IDeleteArea;

View File

@@ -12,67 +12,73 @@
'use strict';
goog.provide('Blockly.IDragTarget');
goog.module('Blockly.IDragTarget');
goog.module.declareLegacyNamespace();
goog.require('Blockly.IComponent');
/* eslint-disable-next-line no-unused-vars */
const IComponent = goog.require('Blockly.IComponent');
/* eslint-disable-next-line no-unused-vars */
const IDraggable = goog.requireType('Blockly.IDraggable');
/* eslint-disable-next-line no-unused-vars */
const Rect = goog.requireType('Blockly.utils.Rect');
goog.requireType('Blockly.IDraggable');
goog.requireType('Blockly.utils.Rect');
/**
* Interface for a component with custom behaviour when a block or bubble is
* dragged over or dropped on top of it.
* @extends {Blockly.IComponent}
* @extends {IComponent}
* @interface
*/
Blockly.IDragTarget = function() {};
const IDragTarget = function() {};
/**
* Returns the bounding rectangle of the drag target area in pixel units
* relative to viewport.
* @return {?Blockly.utils.Rect} The component's bounding box. Null if drag
* @return {?Rect} The component's bounding box. Null if drag
* target area should be ignored.
*/
Blockly.IDragTarget.prototype.getClientRect;
IDragTarget.prototype.getClientRect;
/**
* Handles when a cursor with a block or bubble enters this drag target.
* @param {!Blockly.IDraggable} dragElement The block or bubble currently being
* @param {!IDraggable} dragElement The block or bubble currently being
* dragged.
*/
Blockly.IDragTarget.prototype.onDragEnter;
IDragTarget.prototype.onDragEnter;
/**
* Handles when a cursor with a block or bubble is dragged over this drag
* target.
* @param {!Blockly.IDraggable} dragElement The block or bubble currently being
* @param {!IDraggable} dragElement The block or bubble currently being
* dragged.
*/
Blockly.IDragTarget.prototype.onDragOver;
IDragTarget.prototype.onDragOver;
/**
* Handles when a cursor with a block or bubble exits this drag target.
* @param {!Blockly.IDraggable} dragElement The block or bubble currently being
* @param {!IDraggable} dragElement The block or bubble currently being
* dragged.
*/
Blockly.IDragTarget.prototype.onDragExit;
IDragTarget.prototype.onDragExit;
/**
* Handles when a block or bubble is dropped on this component.
* Should not handle delete here.
* @param {!Blockly.IDraggable} dragElement The block or bubble currently being
* @param {!IDraggable} dragElement The block or bubble currently being
* dragged.
*/
Blockly.IDragTarget.prototype.onDrop;
IDragTarget.prototype.onDrop;
/**
* Returns whether the provided block or bubble should not be moved after being
* dropped on this component. If true, the element will return to where it was
* when the drag started.
* @param {!Blockly.IDraggable} dragElement The block or bubble currently being
* @param {!IDraggable} dragElement The block or bubble currently being
* dragged.
* @return {boolean} Whether the block or bubble provided should be returned to
* drag start.
*/
Blockly.IDragTarget.prototype.shouldPreventMove;
IDragTarget.prototype.shouldPreventMove;
exports = IDragTarget;

View File

@@ -11,14 +11,18 @@
'use strict';
goog.provide('Blockly.IDraggable');
goog.module('Blockly.IDraggable');
goog.module.declareLegacyNamespace();
goog.require('Blockly.IDeletable');
/* eslint-disable-next-line no-unused-vars */
const IDeletable = goog.require('Blockly.IDeletable');
/**
* The interface for an object that can be dragged.
* @extends {Blockly.IDeletable}
* @extends {IDeletable}
* @interface
*/
Blockly.IDraggable = function() {};
const IDraggable = function() {};
exports = IDraggable;

View File

@@ -11,179 +11,188 @@
'use strict';
goog.provide('Blockly.IFlyout');
goog.module('Blockly.IFlyout');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.IRegistrable');
goog.requireType('Blockly.utils.Coordinate');
goog.requireType('Blockly.utils.Svg');
goog.requireType('Blockly.utils.toolbox');
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 Coordinate = goog.requireType('Blockly.utils.Coordinate');
/* eslint-disable-next-line no-unused-vars */
const IRegistrable = goog.require('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');
/**
* Interface for a flyout.
* @extends {Blockly.IRegistrable}
* @extends {IRegistrable}
* @interface
*/
Blockly.IFlyout = function() {};
const IFlyout = function() {};
/**
* Whether the flyout is laid out horizontally or not.
* @type {boolean}
*/
Blockly.IFlyout.prototype.horizontalLayout;
IFlyout.prototype.horizontalLayout;
/**
* Is RTL vs LTR.
* @type {boolean}
*/
Blockly.IFlyout.prototype.RTL;
IFlyout.prototype.RTL;
/**
* The target workspace
* @type {?Blockly.WorkspaceSvg}
* @type {?WorkspaceSvg}
*/
Blockly.IFlyout.prototype.targetWorkspace;
IFlyout.prototype.targetWorkspace;
/**
* Margin around the edges of the blocks in the flyout.
* @type {number}
* @const
*/
Blockly.IFlyout.prototype.MARGIN;
IFlyout.prototype.MARGIN;
/**
* Does the flyout automatically close when a block is created?
* @type {boolean}
*/
Blockly.IFlyout.prototype.autoClose;
IFlyout.prototype.autoClose;
/**
* Corner radius of the flyout background.
* @type {number}
* @const
*/
Blockly.IFlyout.prototype.CORNER_RADIUS;
IFlyout.prototype.CORNER_RADIUS;
/**
* Creates the flyout's DOM. Only needs to be called once. The flyout can
* either exist as its own svg element or be a g element nested inside a
* separate svg element.
* @param {string|
* !Blockly.utils.Svg<!SVGSVGElement>|
* !Blockly.utils.Svg<!SVGGElement>} tagName The type of tag to
* !Svg<!SVGSVGElement>|
* !Svg<!SVGGElement>} tagName The type of tag to
* put the flyout in. This should be <svg> or <g>.
* @return {!SVGElement} The flyout's SVG group.
*/
Blockly.IFlyout.prototype.createDom;
IFlyout.prototype.createDom;
/**
* Initializes the flyout.
* @param {!Blockly.WorkspaceSvg} targetWorkspace The workspace in which to
* @param {!WorkspaceSvg} targetWorkspace The workspace in which to
* create new blocks.
*/
Blockly.IFlyout.prototype.init;
IFlyout.prototype.init;
/**
* Dispose of this flyout.
* Unlink from all DOM elements to prevent memory leaks.
*/
Blockly.IFlyout.prototype.dispose;
IFlyout.prototype.dispose;
/**
* Get the width of the flyout.
* @return {number} The width of the flyout.
*/
Blockly.IFlyout.prototype.getWidth;
IFlyout.prototype.getWidth;
/**
* Get the height of the flyout.
* @return {number} The width of the flyout.
*/
Blockly.IFlyout.prototype.getHeight;
IFlyout.prototype.getHeight;
/**
* Get the workspace inside the flyout.
* @return {!Blockly.WorkspaceSvg} The workspace inside the flyout.
* @return {!WorkspaceSvg} The workspace inside the flyout.
*/
Blockly.IFlyout.prototype.getWorkspace;
IFlyout.prototype.getWorkspace;
/**
* Is the flyout visible?
* @return {boolean} True if visible.
*/
Blockly.IFlyout.prototype.isVisible;
IFlyout.prototype.isVisible;
/**
* Set whether the flyout is visible. A value of true does not necessarily mean
* that the flyout is shown. It could be hidden because its container is hidden.
* @param {boolean} visible True if visible.
*/
Blockly.IFlyout.prototype.setVisible;
IFlyout.prototype.setVisible;
/**
* Set whether this flyout's container is visible.
* @param {boolean} visible Whether the container is visible.
*/
Blockly.IFlyout.prototype.setContainerVisible;
IFlyout.prototype.setContainerVisible;
/**
* Hide and empty the flyout.
*/
Blockly.IFlyout.prototype.hide;
IFlyout.prototype.hide;
/**
* Show and populate the flyout.
* @param {!Blockly.utils.toolbox.FlyoutDefinition|string} flyoutDef Contents to
* @param {!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.
*/
Blockly.IFlyout.prototype.show;
IFlyout.prototype.show;
/**
* Create a copy of this block on the workspace.
* @param {!Blockly.BlockSvg} originalBlock The block to copy from the flyout.
* @return {!Blockly.BlockSvg} The newly created block.
* @param {!BlockSvg} originalBlock The block to copy from the flyout.
* @return {!BlockSvg} The newly created block.
* @throws {Error} if something went wrong with deserialization.
*/
Blockly.IFlyout.prototype.createBlock;
IFlyout.prototype.createBlock;
/**
* Reflow blocks and their mats.
*/
Blockly.IFlyout.prototype.reflow;
IFlyout.prototype.reflow;
/**
* @return {boolean} True if this flyout may be scrolled with a scrollbar or by
* dragging.
*/
Blockly.IFlyout.prototype.isScrollable;
IFlyout.prototype.isScrollable;
/**
* Calculates the x coordinate for the flyout position.
* @return {number} X coordinate.
*/
Blockly.IFlyout.prototype.getX;
IFlyout.prototype.getX;
/**
* Calculates the y coordinate for the flyout position.
* @return {number} Y coordinate.
*/
Blockly.IFlyout.prototype.getY;
IFlyout.prototype.getY;
/**
* Position the flyout.
* @return {void}
*/
Blockly.IFlyout.prototype.position;
IFlyout.prototype.position;
/**
* Determine if a drag delta is toward the workspace, based on the position
* and orientation of the flyout. This is used in determineDragIntention_ to
* determine if a new block should be created or if the flyout should scroll.
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
* @param {!Coordinate} currentDragDeltaXY How far the pointer has
* moved from the position at mouse down, in pixel units.
* @return {boolean} True if the drag is toward the workspace.
*/
Blockly.IFlyout.prototype.isDragTowardWorkspace;
IFlyout.prototype.isDragTowardWorkspace;
exports = IFlyout;

View File

@@ -0,0 +1,35 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for objects that handle keyboard shortcuts.
* @author samelh@google.com (Sam El-Husseini)
*/
'use strict';
goog.module('Blockly.IKeyboardAccessible');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const ShortcutRegistry = goog.requireType('Blockly.ShortcutRegistry');
/**
* An interface for an object that handles keyboard shortcuts.
* @interface
*/
const IKeyboardAccessible = function() {};
/**
* Handles the given keyboard shortcut.
* @param {!ShortcutRegistry.KeyboardShortcut} shortcut The shortcut to be
* handled.
* @return {boolean} True if the shortcut has been handled, false otherwise.
*/
IKeyboardAccessible.prototype.onShortcut;
exports = IKeyboardAccessible;

View File

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

View File

@@ -11,17 +11,20 @@
'use strict';
goog.provide('Blockly.IMovable');
goog.module('Blockly.IMovable');
goog.module.declareLegacyNamespace();
/**
* The interface for an object that is movable.
* @interface
*/
Blockly.IMovable = function() {};
const IMovable = function() {};
/**
* Get whether this is movable or not.
* @return {boolean} True if movable.
*/
Blockly.IMovable.prototype.isMovable;
IMovable.prototype.isMovable;
exports = IMovable;

View File

@@ -11,33 +11,38 @@
'use strict';
goog.provide('Blockly.IPositionable');
goog.module('Blockly.IPositionable');
goog.module.declareLegacyNamespace();
goog.require('Blockly.IComponent');
goog.requireType('Blockly.MetricsManager');
goog.requireType('Blockly.utils.Rect');
/* eslint-disable-next-line no-unused-vars */
const IComponent = goog.require('Blockly.IComponent');
/* 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');
/**
* Interface for a component that is positioned on top of the workspace.
* @extends {Blockly.IComponent}
* @extends {IComponent}
* @interface
*/
Blockly.IPositionable = function() {};
const IPositionable = function() {};
/**
* Positions the element. Called when the window is resized.
* @param {!Blockly.MetricsManager.UiMetrics} metrics The workspace metrics.
* @param {!Array<!Blockly.utils.Rect>} savedPositions List of rectangles that
* @param {!UiMetrics} metrics The workspace metrics.
* @param {!Array<!Rect>} savedPositions List of rectangles that
* are already on the workspace.
*/
Blockly.IPositionable.prototype.position;
IPositionable.prototype.position;
/**
* Returns the bounding rectangle of the UI element in pixel units relative to
* the Blockly injection div.
* @return {?Blockly.utils.Rect} The UI elementss bounding box. Null if
* @return {?Rect} The UI elementss bounding box. Null if
* bounding box should be ignored by other UI elements.
*/
Blockly.IPositionable.prototype.getBoundingRectangle;
IPositionable.prototype.getBoundingRectangle;
exports = IPositionable;

View File

@@ -12,11 +12,14 @@
'use strict';
goog.provide('Blockly.IRegistrable');
goog.module('Blockly.IRegistrable');
goog.module.declareLegacyNamespace();
/**
* The interface for a Blockly component that can be registered.
* @interface
*/
Blockly.IRegistrable = function() {};
const IRegistrable = function() {};
exports = IRegistrable;

View File

@@ -11,21 +11,26 @@
'use strict';
goog.provide('Blockly.IRegistrableField');
goog.module('Blockly.IRegistrableField');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.Field');
goog.requireType('Blockly.Field');
/**
* A registrable field.
* Note: We are not using an interface here as we are interested in defining the
* static methods of a field rather than the instance methods.
* @typedef {{
* fromJson:Blockly.IRegistrableField.fromJson
* fromJson:IRegistrableField.fromJson
* }}
*/
Blockly.IRegistrableField;
let IRegistrableField;
/**
* @typedef {function(!Object): Blockly.Field}
* @typedef {function(!Object): Field}
*/
Blockly.IRegistrableField.fromJson;
IRegistrableField.fromJson;
exports = IRegistrableField;

View File

@@ -11,7 +11,8 @@
'use strict';
goog.provide('Blockly.ISelectable');
goog.module('Blockly.ISelectable');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.IDeletable');
goog.requireType('Blockly.IMovable');
@@ -23,21 +24,23 @@ goog.requireType('Blockly.IMovable');
* @extends {Blockly.IMovable}
* @interface
*/
Blockly.ISelectable = function() {};
const ISelectable = function() {};
/**
* @type {string}
*/
Blockly.ISelectable.prototype.id;
ISelectable.prototype.id;
/**
* Select this. Highlight it visually.
* @return {void}
*/
Blockly.ISelectable.prototype.select;
ISelectable.prototype.select;
/**
* Unselect this. Unhighlight it visually.
* @return {void}
*/
Blockly.ISelectable.prototype.unselect;
ISelectable.prototype.unselect;
exports = ISelectable;

View File

@@ -0,0 +1,70 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for a selectable toolbox item.
* @author aschmiedt@google.com (Abby Schmiedt)
*/
'use strict';
goog.module('Blockly.ISelectableToolboxItem');
goog.module.declareLegacyNamespace();
/* eslint-disable-next-line no-unused-vars */
const IToolboxItem = goog.require('Blockly.IToolboxItem');
/* eslint-disable-next-line no-unused-vars */
const {FlyoutItemInfoArray} = goog.requireType('Blockly.utils.toolbox');
/**
* Interface for an item in the toolbox that can be selected.
* @extends {IToolboxItem}
* @interface
*/
const ISelectableToolboxItem = function() {};
/**
* Gets the name of the toolbox item. Used for emitting events.
* @return {string} The name of the toolbox item.
* @public
*/
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
* of items to be displayed in the flyout.
* @public
*/
ISelectableToolboxItem.prototype.getContents;
/**
* Sets the current toolbox item as selected.
* @param {boolean} _isSelected True if this category is selected, false
* otherwise.
* @public
*/
ISelectableToolboxItem.prototype.setSelected;
/**
* Gets the HTML element that is clickable.
* The parent toolbox element receives clicks. The parent toolbox will add an ID
* to this element so it can pass the onClick event to the correct toolboxItem.
* @return {!Element} The HTML element that receives clicks.
* @public
*/
ISelectableToolboxItem.prototype.getClickTarget;
/**
* Handles when the toolbox item is clicked.
* @param {!Event} _e Click event to handle.
* @public
*/
ISelectableToolboxItem.prototype.onClick;
exports = ISelectableToolboxItem;

View File

@@ -11,23 +11,26 @@
'use strict';
goog.provide('Blockly.IStyleable');
goog.module('Blockly.IStyleable');
goog.module.declareLegacyNamespace();
/**
* Interface for an object that a style can be added to.
* @interface
*/
Blockly.IStyleable = function() {};
const IStyleable = function() {};
/**
* Adds a style on the toolbox. Usually used to change the cursor.
* @param {string} style The name of the class to add.
*/
Blockly.IStyleable.prototype.addStyle;
IStyleable.prototype.addStyle;
/**
* Removes a style from the toolbox. Usually used to change the cursor.
* @param {string} style The name of the class to remove.
*/
Blockly.IStyleable.prototype.removeStyle;
IStyleable.prototype.removeStyle;
exports = IStyleable;

View File

@@ -11,90 +11,96 @@
'use strict';
goog.provide('Blockly.IToolbox');
goog.module('Blockly.IToolbox');
goog.module.declareLegacyNamespace();
goog.requireType('Blockly.IFlyout');
goog.requireType('Blockly.IRegistrable');
goog.requireType('Blockly.IToolboxItem');
goog.requireType('Blockly.utils.toolbox');
goog.requireType('Blockly.WorkspaceSvg');
/* 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');
/* 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');
/**
* Interface for a toolbox.
* @extends {Blockly.IRegistrable}
* @extends {IRegistrable}
* @interface
*/
Blockly.IToolbox = function() {};
const IToolbox = function() {};
/**
* Initializes the toolbox.
* @return {void}
*/
Blockly.IToolbox.prototype.init;
IToolbox.prototype.init;
/**
* Fills the toolbox with new toolbox items and removes any old contents.
* @param {!Blockly.utils.toolbox.ToolboxInfo} toolboxDef Object holding information
* @param {!ToolboxInfo} toolboxDef Object holding information
* for creating a toolbox.
*/
Blockly.IToolbox.prototype.render;
IToolbox.prototype.render;
/**
* Gets the width of the toolbox.
* @return {number} The width of the toolbox.
*/
Blockly.IToolbox.prototype.getWidth;
IToolbox.prototype.getWidth;
/**
* Gets the height of the toolbox.
* @return {number} The width of the toolbox.
*/
Blockly.IToolbox.prototype.getHeight;
IToolbox.prototype.getHeight;
/**
* Gets the toolbox flyout.
* @return {?Blockly.IFlyout} The toolbox flyout.
* @return {?IFlyout} The toolbox flyout.
*/
Blockly.IToolbox.prototype.getFlyout;
IToolbox.prototype.getFlyout;
/**
* Gets the workspace for the toolbox.
* @return {!Blockly.WorkspaceSvg} The parent workspace for the toolbox.
* @return {!WorkspaceSvg} The parent workspace for the toolbox.
*/
Blockly.IToolbox.prototype.getWorkspace;
IToolbox.prototype.getWorkspace;
/**
* Gets whether or not the toolbox is horizontal.
* @return {boolean} True if the toolbox is horizontal, false if the toolbox is
* vertical.
*/
Blockly.IToolbox.prototype.isHorizontal;
IToolbox.prototype.isHorizontal;
/**
* Positions the toolbox based on whether it is a horizontal toolbox and whether
* the workspace is in rtl.
* @return {void}
*/
Blockly.IToolbox.prototype.position;
IToolbox.prototype.position;
/**
* Handles resizing the toolbox when a toolbox item resizes.
* @return {void}
*/
Blockly.IToolbox.prototype.handleToolboxItemResize;
IToolbox.prototype.handleToolboxItemResize;
/**
* Unhighlights any previously selected item.
* @return {void}
*/
Blockly.IToolbox.prototype.clearSelection;
IToolbox.prototype.clearSelection;
/**
* Updates the category colours and background colour of selected categories.
* @return {void}
*/
Blockly.IToolbox.prototype.refreshTheme;
IToolbox.prototype.refreshTheme;
/**
* Updates the flyout's content without closing it. Should be used in response
@@ -102,30 +108,32 @@ Blockly.IToolbox.prototype.refreshTheme;
* procedures.
* @return {void}
*/
Blockly.IToolbox.prototype.refreshSelection;
IToolbox.prototype.refreshSelection;
/**
* Sets the visibility of the toolbox.
* @param {boolean} isVisible True if toolbox should be visible.
*/
Blockly.IToolbox.prototype.setVisible;
IToolbox.prototype.setVisible;
/**
* Selects the toolbox item by it's position in the list of toolbox items.
* @param {number} position The position of the item to select.
* @return {void}
*/
Blockly.IToolbox.prototype.selectItemByPosition;
IToolbox.prototype.selectItemByPosition;
/**
* Gets the selected item.
* @return {?Blockly.IToolboxItem} The selected item, or null if no item is
* @return {?IToolboxItem} The selected item, or null if no item is
* currently selected.
*/
Blockly.IToolbox.prototype.getSelectedItem;
IToolbox.prototype.getSelectedItem;
/**
* Disposes of this toolbox.
* @return {void}
*/
Blockly.IToolbox.prototype.dispose;
IToolbox.prototype.dispose;
exports = IToolbox;

View File

@@ -11,18 +11,15 @@
'use strict';
goog.provide('Blockly.ICollapsibleToolboxItem');
goog.provide('Blockly.ISelectableToolboxItem');
goog.provide('Blockly.IToolboxItem');
goog.requireType('Blockly.utils.toolbox');
goog.module('Blockly.IToolboxItem');
goog.module.declareLegacyNamespace();
/**
* Interface for an item in the toolbox.
* @interface
*/
Blockly.IToolboxItem = function() {};
const IToolboxItem = function() {};
/**
* Initializes the toolbox item.
@@ -31,21 +28,21 @@ Blockly.IToolboxItem = function() {};
* @return {void}
* @public
*/
Blockly.IToolboxItem.prototype.init;
IToolboxItem.prototype.init;
/**
* Gets the div for the toolbox item.
* @return {?Element} The div for the toolbox item.
* @public
*/
Blockly.IToolboxItem.prototype.getDiv;
IToolboxItem.prototype.getDiv;
/**
* Gets a unique identifier for this toolbox item.
* @return {string} The ID for the toolbox item.
* @public
*/
Blockly.IToolboxItem.prototype.getId;
IToolboxItem.prototype.getId;
/**
* Gets the parent if the toolbox item is nested.
@@ -53,105 +50,33 @@ Blockly.IToolboxItem.prototype.getId;
* this toolbox item is not nested.
* @public
*/
Blockly.IToolboxItem.prototype.getParent;
IToolboxItem.prototype.getParent;
/**
* Gets the nested level of the category.
* @return {number} The nested level of the category.
* @package
*/
Blockly.IToolboxItem.prototype.getLevel;
IToolboxItem.prototype.getLevel;
/**
* Whether the toolbox item is selectable.
* @return {boolean} True if the toolbox item can be selected.
* @public
*/
Blockly.IToolboxItem.prototype.isSelectable;
IToolboxItem.prototype.isSelectable;
/**
* Whether the toolbox item is collapsible.
* @return {boolean} True if the toolbox item is collapsible.
* @public
*/
Blockly.IToolboxItem.prototype.isCollapsible;
IToolboxItem.prototype.isCollapsible;
/**
* Dispose of this toolbox item. No-op by default.
* @public
*/
Blockly.IToolboxItem.prototype.dispose;
IToolboxItem.prototype.dispose;
/**
* Interface for an item in the toolbox that can be selected.
* @extends {Blockly.IToolboxItem}
* @interface
*/
Blockly.ISelectableToolboxItem = function() {};
/**
* Gets the name of the toolbox item. Used for emitting events.
* @return {string} The name of the toolbox item.
* @public
*/
Blockly.ISelectableToolboxItem.prototype.getName;
/**
* Gets the contents of the toolbox item. These are items that are meant to be
* displayed in the flyout.
* @return {!Blockly.utils.toolbox.FlyoutItemInfoArray|string} The definition
* of items to be displayed in the flyout.
* @public
*/
Blockly.ISelectableToolboxItem.prototype.getContents;
/**
* Sets the current toolbox item as selected.
* @param {boolean} _isSelected True if this category is selected, false
* otherwise.
* @public
*/
Blockly.ISelectableToolboxItem.prototype.setSelected;
/**
* Gets the HTML element that is clickable.
* The parent toolbox element receives clicks. The parent toolbox will add an ID
* to this element so it can pass the onClick event to the correct toolboxItem.
* @return {!Element} The HTML element that receives clicks.
* @public
*/
Blockly.ISelectableToolboxItem.prototype.getClickTarget;
/**
* Handles when the toolbox item is clicked.
* @param {!Event} _e Click event to handle.
* @public
*/
Blockly.ISelectableToolboxItem.prototype.onClick;
/**
* Interface for an item in the toolbox that can be collapsed.
* @extends {Blockly.ISelectableToolboxItem}
* @interface
*/
Blockly.ICollapsibleToolboxItem = function() {};
/**
* Gets any children toolbox items. (ex. Gets the subcategories)
* @return {!Array<!Blockly.IToolboxItem>} The child toolbox items.
*/
Blockly.ICollapsibleToolboxItem.prototype.getChildToolboxItems;
/**
* Whether the toolbox item is expanded to show its child subcategories.
* @return {boolean} True if the toolbox item shows its children, false if it
* is collapsed.
* @public
*/
Blockly.ICollapsibleToolboxItem.prototype.isExpanded;
/**
* Toggles whether or not the toolbox item is expanded.
* @public
*/
Blockly.ICollapsibleToolboxItem.prototype.toggleExpanded;
exports = IToolboxItem;

218
core/internal_constants.js Normal file
View File

@@ -0,0 +1,218 @@
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Module that provides constants for use inside Blockly. Do not
* use these constants outside of the core library.
* @author fenichel@google.com (Rachel Fenichel)
* @package
*/
'use strict';
goog.module('Blockly.internalConstants');
goog.module.declareLegacyNamespace();
const connectionTypes = goog.require('Blockly.connectionTypes');
/**
* The multiplier for scroll wheel deltas using the line delta mode.
* @type {number}
*/
const LINE_MODE_MULTIPLIER = 40;
exports.LINE_MODE_MULTIPLIER = LINE_MODE_MULTIPLIER;
/**
* The multiplier for scroll wheel deltas using the page delta mode.
* @type {number}
*/
const PAGE_MODE_MULTIPLIER = 125;
exports.PAGE_MODE_MULTIPLIER = PAGE_MODE_MULTIPLIER;
/**
* Number of pixels the mouse must move before a drag starts.
*/
const DRAG_RADIUS = 5;
exports.DRAG_RADIUS = DRAG_RADIUS;
/**
* Number of pixels the mouse must move before a drag/scroll starts from the
* flyout. Because the drag-intention is determined when this is reached, it is
* larger than DRAG_RADIUS so that the drag-direction is clearer.
*/
const FLYOUT_DRAG_RADIUS = 10;
exports.FLYOUT_DRAG_RADIUS = FLYOUT_DRAG_RADIUS;
/**
* Maximum misalignment between connections for them to snap together.
*/
const SNAP_RADIUS = 28;
exports.SNAP_RADIUS = SNAP_RADIUS;
/**
* Maximum misalignment between connections for them to snap together,
* when a connection is already highlighted.
*/
const CONNECTING_SNAP_RADIUS = SNAP_RADIUS;
exports.CONNECTING_SNAP_RADIUS = CONNECTING_SNAP_RADIUS;
/**
* How much to prefer staying connected to the current connection over moving to
* a new connection. The current previewed connection is considered to be this
* much closer to the matching connection on the block than it actually is.
*/
const CURRENT_CONNECTION_PREFERENCE = 8;
exports.CURRENT_CONNECTION_PREFERENCE = CURRENT_CONNECTION_PREFERENCE;
/**
* Delay in ms between trigger and bumping unconnected block out of alignment.
*/
const BUMP_DELAY = 250;
exports.BUMP_DELAY = BUMP_DELAY;
/**
* Maximum randomness in workspace units for bumping a block.
*/
const BUMP_RANDOMNESS = 10;
exports.BUMP_RANDOMNESS = BUMP_RANDOMNESS;
/**
* Number of characters to truncate a collapsed block to.
*/
const COLLAPSE_CHARS = 30;
exports.COLLAPSE_CHARS = COLLAPSE_CHARS;
/**
* Length in ms for a touch to become a long press.
*/
const LONGPRESS = 750;
exports.LONGPRESS = LONGPRESS;
/**
* Prevent a sound from playing if another sound preceded it within this many
* milliseconds.
*/
const SOUND_LIMIT = 100;
exports.SOUND_LIMIT = SOUND_LIMIT;
/**
* When dragging a block out of a stack, split the stack in two (true), or drag
* out the block healing the stack (false).
*/
const DRAG_STACK = true;
exports.DRAG_STACK = DRAG_STACK;
/**
* The richness of block colours, regardless of the hue.
* Must be in the range of 0 (inclusive) to 1 (exclusive).
*/
const HSV_SATURATION = 0.45;
exports.HSV_SATURATION = HSV_SATURATION;
/**
* The intensity of block colours, regardless of the hue.
* Must be in the range of 0 (inclusive) to 1 (exclusive).
*/
const HSV_VALUE = 0.65;
exports.HSV_VALUE = HSV_VALUE;
/**
* Sprited icons and images.
*/
const SPRITE = {
width: 96,
height: 124,
url: 'sprites.png'
};
exports.SPRITE = SPRITE;
/**
* ENUM for no drag operation.
* @const
*/
const DRAG_NONE = 0;
exports.DRAG_NONE = DRAG_NONE;
/**
* ENUM for inside the sticky DRAG_RADIUS.
* @const
*/
const DRAG_STICKY = 1;
exports.DRAG_STICKY = DRAG_STICKY;
/**
* ENUM for inside the non-sticky DRAG_RADIUS, for differentiating between
* clicks and drags.
* @const
*/
const DRAG_BEGIN = 1;
exports.DRAG_BEGIN = DRAG_BEGIN;
/**
* ENUM for freely draggable (outside the DRAG_RADIUS, if one applies).
* @const
*/
const DRAG_FREE = 2;
exports.DRAG_FREE = DRAG_FREE;
/**
* Lookup table for determining the opposite type of a connection.
* @const
*/
const OPPOSITE_TYPE = [];
OPPOSITE_TYPE[connectionTypes.INPUT_VALUE] = connectionTypes.OUTPUT_VALUE;
OPPOSITE_TYPE[connectionTypes.OUTPUT_VALUE] = connectionTypes.INPUT_VALUE;
OPPOSITE_TYPE[connectionTypes.NEXT_STATEMENT] =
connectionTypes.PREVIOUS_STATEMENT;
OPPOSITE_TYPE[connectionTypes.PREVIOUS_STATEMENT] =
connectionTypes.NEXT_STATEMENT;
exports.OPPOSITE_TYPE = OPPOSITE_TYPE;
/**
* String for use in the "custom" attribute of a category in toolbox XML.
* This string indicates that the category should be dynamically populated with
* variable blocks.
* @const {string}
*/
const VARIABLE_CATEGORY_NAME = 'VARIABLE';
exports.VARIABLE_CATEGORY_NAME = VARIABLE_CATEGORY_NAME;
/**
* String for use in the "custom" attribute of a category in toolbox XML.
* This string indicates that the category should be dynamically populated with
* variable blocks.
* @const {string}
*/
const VARIABLE_DYNAMIC_CATEGORY_NAME = 'VARIABLE_DYNAMIC';
exports.VARIABLE_DYNAMIC_CATEGORY_NAME = VARIABLE_DYNAMIC_CATEGORY_NAME;
/**
* String for use in the "custom" attribute of a category in toolbox XML.
* This string indicates that the category should be dynamically populated with
* procedure blocks.
* @const {string}
*/
const PROCEDURE_CATEGORY_NAME = 'PROCEDURE';
exports.PROCEDURE_CATEGORY_NAME = PROCEDURE_CATEGORY_NAME;
/**
* String for use in the dropdown created in field_variable.
* This string indicates that this option in the dropdown is 'Rename
* variable...' and if selected, should trigger the prompt to rename a variable.
* @const {string}
*/
const RENAME_VARIABLE_ID = 'RENAME_VARIABLE_ID';
exports.RENAME_VARIABLE_ID = RENAME_VARIABLE_ID;
/**
* String for use in the dropdown created in field_variable.
* This string indicates that this option in the dropdown is 'Delete the "%1"
* variable' and if selected, should trigger the prompt to delete a variable.
* @const {string}
*/
const DELETE_VARIABLE_ID = 'DELETE_VARIABLE_ID';
exports.DELETE_VARIABLE_ID = DELETE_VARIABLE_ID;

View File

@@ -13,8 +13,6 @@
goog.provide('Blockly.ASTNode');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.utils.Coordinate');
goog.requireType('Blockly.Block');
@@ -420,7 +418,7 @@ Blockly.ASTNode.prototype.findPrevForField_ = function() {
*/
Blockly.ASTNode.prototype.navigateBetweenStacks_ = function(forward) {
var curLocation = this.getLocation();
if (!(curLocation instanceof Blockly.Block)) {
if (curLocation.getSourceBlock) {
curLocation = /** @type {!Blockly.IASTNodeLocationWithBlock} */ (
curLocation).getSourceBlock();
}

View File

@@ -11,11 +11,13 @@
*/
'use strict';
goog.provide('Blockly.BasicCursor');
goog.module('Blockly.BasicCursor');
goog.module.declareLegacyNamespace();
goog.require('Blockly.ASTNode');
goog.require('Blockly.Cursor');
goog.require('Blockly.registry');
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');
/**
@@ -23,31 +25,31 @@ goog.require('Blockly.registry');
* This will allow the user to get to all nodes in the AST by hitting next or
* previous.
* @constructor
* @extends {Blockly.Cursor}
* @extends {Cursor}
*/
Blockly.BasicCursor = function() {
Blockly.BasicCursor.superClass_.constructor.call(this);
const BasicCursor = function() {
BasicCursor.superClass_.constructor.call(this);
};
Blockly.utils.object.inherits(Blockly.BasicCursor, Blockly.Cursor);
inherits(BasicCursor, Cursor);
/**
* Name used for registering a basic cursor.
* @const {string}
*/
Blockly.BasicCursor.registrationName = 'basicCursor';
BasicCursor.registrationName = 'basicCursor';
/**
* Find the next node in the pre order traversal.
* @return {Blockly.ASTNode} The next node, or null if the current node is
* @return {?ASTNode} The next node, or null if the current node is
* not set or there is no next value.
* @override
*/
Blockly.BasicCursor.prototype.next = function() {
var curNode = this.getCurNode();
BasicCursor.prototype.next = function() {
const curNode = this.getCurNode();
if (!curNode) {
return null;
}
var newNode = this.getNextNode_(curNode, this.validNode_);
const newNode = this.getNextNode_(curNode, this.validNode_);
if (newNode) {
this.setCurNode(newNode);
@@ -59,26 +61,26 @@ Blockly.BasicCursor.prototype.next = function() {
* For a basic cursor we only have the ability to go next and previous, so
* in will also allow the user to get to the next node in the pre order
* traversal.
* @return {Blockly.ASTNode} The next node, or null if the current node is
* @return {?ASTNode} The next node, or null if the current node is
* not set or there is no next value.
* @override
*/
Blockly.BasicCursor.prototype.in = function() {
BasicCursor.prototype.in = function() {
return this.next();
};
/**
* Find the previous node in the pre order traversal.
* @return {Blockly.ASTNode} The previous node, or null if the current node
* @return {?ASTNode} The previous node, or null if the current node
* is not set or there is no previous value.
* @override
*/
Blockly.BasicCursor.prototype.prev = function() {
var curNode = this.getCurNode();
BasicCursor.prototype.prev = function() {
const curNode = this.getCurNode();
if (!curNode) {
return null;
}
var newNode = this.getPreviousNode_(curNode, this.validNode_);
const newNode = this.getPreviousNode_(curNode, this.validNode_);
if (newNode) {
this.setCurNode(newNode);
@@ -90,11 +92,11 @@ Blockly.BasicCursor.prototype.prev = function() {
* For a basic cursor we only have the ability to go next and previous, so
* out will allow the user to get to the previous node in the pre order
* traversal.
* @return {Blockly.ASTNode} The previous node, or null if the current node is
* @return {?ASTNode} The previous node, or null if the current node is
* not set or there is no previous value.
* @override
*/
Blockly.BasicCursor.prototype.out = function() {
BasicCursor.prototype.out = function() {
return this.prev();
};
@@ -102,23 +104,23 @@ Blockly.BasicCursor.prototype.out = function() {
* Uses pre order traversal to navigate the Blockly AST. This will allow
* a user to easily navigate the entire Blockly AST without having to go in
* and out levels on the tree.
* @param {Blockly.ASTNode} node The current position in the AST.
* @param {!function(Blockly.ASTNode) : boolean} isValid A function true/false
* @param {?ASTNode} node The current position in the AST.
* @param {!function(ASTNode) : boolean} isValid A function true/false
* depending on whether the given node should be traversed.
* @return {Blockly.ASTNode} The next node in the traversal.
* @return {?ASTNode} The next node in the traversal.
* @protected
*/
Blockly.BasicCursor.prototype.getNextNode_ = function(node, isValid) {
BasicCursor.prototype.getNextNode_ = function(node, isValid) {
if (!node) {
return null;
}
var newNode = node.in() || node.next();
const newNode = node.in() || node.next();
if (isValid(newNode)) {
return newNode;
} else if (newNode) {
return this.getNextNode_(newNode, isValid);
}
var siblingOrParent = this.findSiblingOrParent_(node.out());
const siblingOrParent = this.findSiblingOrParent_(node.out());
if (isValid(siblingOrParent)) {
return siblingOrParent;
} else if (siblingOrParent) {
@@ -131,18 +133,18 @@ Blockly.BasicCursor.prototype.getNextNode_ = function(node, isValid) {
* Reverses the pre order traversal in order to find the previous node. This
* will allow a user to easily navigate the entire Blockly AST without having to
* go in and out levels on the tree.
* @param {Blockly.ASTNode} node The current position in the AST.
* @param {!function(Blockly.ASTNode) : boolean} isValid A function true/false
* @param {?ASTNode} node The current position in the AST.
* @param {!function(ASTNode) : boolean} isValid A function true/false
* depending on whether the given node should be traversed.
* @return {Blockly.ASTNode} The previous node in the traversal or null if no
* @return {?ASTNode} The previous node in the traversal or null if no
* previous node exists.
* @protected
*/
Blockly.BasicCursor.prototype.getPreviousNode_ = function(node, isValid) {
BasicCursor.prototype.getPreviousNode_ = function(node, isValid) {
if (!node) {
return null;
}
var newNode = node.prev();
let newNode = node.prev();
if (newNode) {
newNode = this.getRightMostChild_(newNode);
@@ -160,19 +162,16 @@ Blockly.BasicCursor.prototype.getPreviousNode_ = function(node, isValid) {
/**
* Decides what nodes to traverse and which ones to skip. Currently, it
* skips output, stack and workspace nodes.
* @param {Blockly.ASTNode} node The AST node to check whether it is valid.
* @param {?ASTNode} node The AST node to check whether it is valid.
* @return {boolean} True if the node should be visited, false otherwise.
* @protected
*/
Blockly.BasicCursor.prototype.validNode_ = function(node) {
var isValid = false;
var type = node && node.getType();
if (type == Blockly.ASTNode.types.OUTPUT ||
type == Blockly.ASTNode.types.INPUT ||
type == Blockly.ASTNode.types.FIELD ||
type == Blockly.ASTNode.types.NEXT ||
type == Blockly.ASTNode.types.PREVIOUS ||
type == Blockly.ASTNode.types.WORKSPACE) {
BasicCursor.prototype.validNode_ = function(node) {
let isValid = false;
const type = node && node.getType();
if (type == ASTNode.types.OUTPUT || type == ASTNode.types.INPUT ||
type == ASTNode.types.FIELD || type == ASTNode.types.NEXT ||
type == ASTNode.types.PREVIOUS || type == ASTNode.types.WORKSPACE) {
isValid = true;
}
return isValid;
@@ -180,16 +179,16 @@ Blockly.BasicCursor.prototype.validNode_ = function(node) {
/**
* From the given node find either the next valid sibling or parent.
* @param {Blockly.ASTNode} node The current position in the AST.
* @return {Blockly.ASTNode} The parent AST node or null if there are no
* @param {?ASTNode} node The current position in the AST.
* @return {?ASTNode} The parent AST node or null if there are no
* valid parents.
* @private
*/
Blockly.BasicCursor.prototype.findSiblingOrParent_ = function(node) {
BasicCursor.prototype.findSiblingOrParent_ = function(node) {
if (!node) {
return null;
}
var nextNode = node.next();
const nextNode = node.next();
if (nextNode) {
return nextNode;
}
@@ -199,22 +198,22 @@ Blockly.BasicCursor.prototype.findSiblingOrParent_ = function(node) {
/**
* Get the right most child of a node.
* @param {Blockly.ASTNode} node The node to find the right most child of.
* @return {Blockly.ASTNode} The right most child of the given node, or the node
* @param {?ASTNode} node The node to find the right most child of.
* @return {?ASTNode} The right most child of the given node, or the node
* if no child exists.
* @private
*/
Blockly.BasicCursor.prototype.getRightMostChild_ = function(node) {
BasicCursor.prototype.getRightMostChild_ = function(node) {
if (!node.in()) {
return node;
}
var newNode = node.in();
let newNode = node.in();
while (newNode.next()) {
newNode = newNode.next();
}
return this.getRightMostChild_(newNode);
};
Blockly.registry.register(
Blockly.registry.Type.CURSOR, Blockly.BasicCursor.registrationName,
Blockly.BasicCursor);
register(Type.CURSOR, BasicCursor.registrationName, BasicCursor);
exports = BasicCursor;

View File

@@ -11,46 +11,47 @@
*/
'use strict';
goog.provide('Blockly.Cursor');
goog.module('Blockly.Cursor');
goog.module.declareLegacyNamespace();
goog.require('Blockly.ASTNode');
goog.require('Blockly.Marker');
goog.require('Blockly.registry');
goog.require('Blockly.utils.object');
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');
/**
* Class for a cursor.
* A cursor controls how a user navigates the Blockly AST.
* @constructor
* @extends {Blockly.Marker}
* @extends {Marker}
*/
Blockly.Cursor = function() {
Blockly.Cursor.superClass_.constructor.call(this);
const Cursor = function() {
Cursor.superClass_.constructor.call(this);
/**
* @override
*/
this.type = 'cursor';
};
Blockly.utils.object.inherits(Blockly.Cursor, Blockly.Marker);
inherits(Cursor, Marker);
/**
* Find the next connection, field, or block.
* @return {Blockly.ASTNode} The next element, or null if the current node is
* @return {ASTNode} The next element, or null if the current node is
* not set or there is no next value.
* @public
*/
Blockly.Cursor.prototype.next = function() {
var curNode = this.getCurNode();
Cursor.prototype.next = function() {
const curNode = this.getCurNode();
if (!curNode) {
return null;
}
var newNode = curNode.next();
let newNode = curNode.next();
while (newNode && newNode.next() &&
(newNode.getType() == Blockly.ASTNode.types.NEXT ||
newNode.getType() == Blockly.ASTNode.types.BLOCK)) {
(newNode.getType() == ASTNode.types.NEXT ||
newNode.getType() == ASTNode.types.BLOCK)) {
newNode = newNode.next();
}
@@ -62,22 +63,22 @@ Blockly.Cursor.prototype.next = function() {
/**
* Find the in connection or field.
* @return {Blockly.ASTNode} The in element, or null if the current node is
* @return {ASTNode} The in element, or null if the current node is
* not set or there is no in value.
* @public
*/
Blockly.Cursor.prototype.in = function() {
var curNode = this.getCurNode();
Cursor.prototype.in = function() {
let curNode = this.getCurNode();
if (!curNode) {
return null;
}
// If we are on a previous or output connection, go to the block level before
// performing next operation.
if (curNode.getType() == Blockly.ASTNode.types.PREVIOUS ||
curNode.getType() == Blockly.ASTNode.types.OUTPUT) {
if (curNode.getType() == ASTNode.types.PREVIOUS ||
curNode.getType() == ASTNode.types.OUTPUT) {
curNode = curNode.next();
}
var newNode = curNode.in();
const newNode = curNode.in();
if (newNode) {
this.setCurNode(newNode);
@@ -87,20 +88,20 @@ Blockly.Cursor.prototype.in = function() {
/**
* Find the previous connection, field, or block.
* @return {Blockly.ASTNode} The previous element, or null if the current node
* @return {ASTNode} The previous element, or null if the current node
* is not set or there is no previous value.
* @public
*/
Blockly.Cursor.prototype.prev = function() {
var curNode = this.getCurNode();
Cursor.prototype.prev = function() {
const curNode = this.getCurNode();
if (!curNode) {
return null;
}
var newNode = curNode.prev();
let newNode = curNode.prev();
while (newNode && newNode.prev() &&
(newNode.getType() == Blockly.ASTNode.types.NEXT ||
newNode.getType() == Blockly.ASTNode.types.BLOCK)) {
(newNode.getType() == ASTNode.types.NEXT ||
newNode.getType() == ASTNode.types.BLOCK)) {
newNode = newNode.prev();
}
@@ -112,18 +113,18 @@ Blockly.Cursor.prototype.prev = function() {
/**
* Find the out connection, field, or block.
* @return {Blockly.ASTNode} The out element, or null if the current node is
* @return {ASTNode} The out element, or null if the current node is
* not set or there is no out value.
* @public
*/
Blockly.Cursor.prototype.out = function() {
var curNode = this.getCurNode();
Cursor.prototype.out = function() {
const curNode = this.getCurNode();
if (!curNode) {
return null;
}
var newNode = curNode.out();
let newNode = curNode.out();
if (newNode && newNode.getType() == Blockly.ASTNode.types.BLOCK) {
if (newNode && newNode.getType() == ASTNode.types.BLOCK) {
newNode = newNode.prev() || newNode;
}
@@ -133,5 +134,6 @@ Blockly.Cursor.prototype.out = function() {
return newNode;
};
Blockly.registry.register(
Blockly.registry.Type.CURSOR, Blockly.registry.DEFAULT, Blockly.Cursor);
register(Type.CURSOR, DEFAULT, Cursor);
exports = Cursor;

View File

@@ -11,40 +11,43 @@
*/
'use strict';
goog.provide('Blockly.TabNavigateCursor');
goog.module('Blockly.TabNavigateCursor');
goog.module.declareLegacyNamespace();
goog.require('Blockly.ASTNode');
goog.require('Blockly.BasicCursor');
goog.require('Blockly.utils.object');
goog.requireType('Blockly.Field');
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');
/**
* A cursor for navigating between tab navigable fields.
* @constructor
* @extends {Blockly.BasicCursor}
* @extends {BasicCursor}
*/
Blockly.TabNavigateCursor = function() {
Blockly.TabNavigateCursor.superClass_.constructor.call(this);
const TabNavigateCursor = function() {
TabNavigateCursor.superClass_.constructor.call(this);
};
Blockly.utils.object.inherits(Blockly.TabNavigateCursor, Blockly.BasicCursor);
inherits(TabNavigateCursor, BasicCursor);
/**
* Skip all nodes except for tab navigable fields.
* @param {Blockly.ASTNode} node The AST node to check whether it is valid.
* @param {?ASTNode} node The AST node to check whether it is valid.
* @return {boolean} True if the node should be visited, false otherwise.
* @override
*/
Blockly.TabNavigateCursor.prototype.validNode_ = function(node) {
var isValid = false;
var type = node && node.getType();
TabNavigateCursor.prototype.validNode_ = function(node) {
let isValid = false;
const type = node && node.getType();
if (node) {
var location = /** @type {Blockly.Field} */ (node.getLocation());
if (type == Blockly.ASTNode.types.FIELD &&
location && location.isTabNavigable() && location.isClickable()) {
const location = /** @type {Field} */ (node.getLocation());
if (type == ASTNode.types.FIELD && location && location.isTabNavigable() &&
location.isClickable()) {
isValid = true;
}
}
return isValid;
};
exports = TabNavigateCursor;

View File

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

View File

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

View File

@@ -20,6 +20,7 @@ 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');
@@ -444,7 +445,7 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
Blockly.Events.setGroup(group);
block.bumpNeighbours();
Blockly.Events.setGroup(false);
}, Blockly.BUMP_DELAY);
}, Blockly.internalConstants.BUMP_DELAY);
}
// Don't update the bubble until the drag has ended, to avoid moving blocks

View File

@@ -12,8 +12,7 @@
goog.provide('Blockly.Names');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Msg');
goog.requireType('Blockly.VariableMap');
@@ -77,7 +76,7 @@ Blockly.Names.prototype.setVariableMap = function(map) {
/**
* Get the name for a user-defined variable, based on its ID.
* This should only be used for variables of realm
* Blockly.VARIABLE_CATEGORY_NAME.
* Blockly.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.
@@ -106,7 +105,8 @@ Blockly.Names.prototype.getNameForUserVariable_ = function(id) {
Blockly.Names.prototype.populateVariables = function(workspace) {
var variables = Blockly.Variables.allUsedVarModels(workspace);
for (var i = 0; i < variables.length; i++) {
this.getName(variables[i].getId(), Blockly.VARIABLE_CATEGORY_NAME);
this.getName(
variables[i].getId(), Blockly.internalConstants.VARIABLE_CATEGORY_NAME);
}
};
@@ -119,7 +119,8 @@ Blockly.Names.prototype.populateProcedures = function(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.PROCEDURE_CATEGORY_NAME);
this.getName(
procedures[i][0], Blockly.internalConstants.PROCEDURE_CATEGORY_NAME);
}
};
@@ -133,7 +134,7 @@ Blockly.Names.prototype.populateProcedures = function(workspace) {
*/
Blockly.Names.prototype.getName = function(nameOrId, realm) {
var name = nameOrId;
if (realm == Blockly.VARIABLE_CATEGORY_NAME) {
if (realm == Blockly.internalConstants.VARIABLE_CATEGORY_NAME) {
var varName = this.getNameForUserVariable_(nameOrId);
if (varName) {
// Successful ID lookup.
@@ -142,7 +143,7 @@ Blockly.Names.prototype.getName = function(nameOrId, realm) {
}
var normalizedName = name.toLowerCase();
var isVar = realm == Blockly.VARIABLE_CATEGORY_NAME ||
var isVar = realm == Blockly.internalConstants.VARIABLE_CATEGORY_NAME ||
realm == Blockly.Names.DEVELOPER_VARIABLE_TYPE;
var prefix = isVar ? this.variablePrefix_ : '';
@@ -189,7 +190,7 @@ Blockly.Names.prototype.getDistinctName = function(name, realm) {
}
safeName += i;
this.dbReverse_[safeName] = true;
var isVar = realm == Blockly.VARIABLE_CATEGORY_NAME ||
var isVar = realm == Blockly.internalConstants.VARIABLE_CATEGORY_NAME ||
realm == Blockly.Names.DEVELOPER_VARIABLE_TYPE;
var prefix = isVar ? this.variablePrefix_ : '';
return prefix + safeName;

View File

@@ -17,12 +17,11 @@
goog.provide('Blockly.Procedures');
goog.require('Blockly.Blocks');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.Events');
/** @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');
@@ -37,9 +36,10 @@ goog.requireType('Blockly.WorkspaceSvg');
/**
* Constant to separate procedure names from variables and generated functions
* when running generators.
* @deprecated Use Blockly.PROCEDURE_CATEGORY_NAME
* @deprecated Use Blockly.internalConstants.PROCEDURE_CATEGORY_NAME
*/
Blockly.Procedures.NAME_TYPE = Blockly.PROCEDURE_CATEGORY_NAME;
Blockly.Procedures.NAME_TYPE =
Blockly.internalConstants.PROCEDURE_CATEGORY_NAME;
/**
* The default argument for a procedures_mutatorarg block.

View File

@@ -14,8 +14,7 @@ goog.provide('Blockly.RenderedConnection');
goog.require('Blockly.Connection');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.internalConstants');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.utils.deprecation');
@@ -51,8 +50,9 @@ Blockly.RenderedConnection = function(source, type) {
* @const {!Blockly.ConnectionDB}
* @private
*/
this.dbOpposite_ = source.workspace
.connectionDBList[Blockly.OPPOSITE_TYPE[type]];
this.dbOpposite_ =
source.workspace
.connectionDBList[Blockly.internalConstants.OPPOSITE_TYPE[type]];
/**
* Workspace units, (0, 0) is top left of block.
@@ -173,17 +173,23 @@ Blockly.RenderedConnection.prototype.bumpAwayFrom = function(staticConnection) {
// Raise it to the top for extra visibility.
var selected = Blockly.selected == rootBlock;
selected || rootBlock.addSelect();
var dx = (staticConnection.x + Blockly.SNAP_RADIUS +
Math.floor(Math.random() * Blockly.BUMP_RANDOMNESS)) - this.x;
var dy = (staticConnection.y + Blockly.SNAP_RADIUS +
Math.floor(Math.random() * Blockly.BUMP_RANDOMNESS)) - this.y;
var dx =
(staticConnection.x + Blockly.internalConstants.SNAP_RADIUS +
Math.floor(Math.random() * Blockly.internalConstants.BUMP_RANDOMNESS)) -
this.x;
var dy =
(staticConnection.y + Blockly.internalConstants.SNAP_RADIUS +
Math.floor(Math.random() * Blockly.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.SNAP_RADIUS -
Math.floor(Math.random() * Blockly.BUMP_RANDOMNESS)) - this.x;
dx = (staticConnection.x - Blockly.internalConstants.SNAP_RADIUS -
Math.floor(
Math.random() * Blockly.internalConstants.BUMP_RANDOMNESS)) -
this.x;
}
rootBlock.moveBy(dx, dy);
selected || rootBlock.removeSelect();
@@ -466,7 +472,7 @@ Blockly.RenderedConnection.prototype.onFailedConnect =
this.bumpAwayFrom(otherConnection);
Blockly.Events.setGroup(false);
}
}.bind(this), Blockly.BUMP_DELAY);
}.bind(this), Blockly.internalConstants.BUMP_DELAY);
}
};

View File

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

View File

@@ -17,8 +17,6 @@ goog.require('Blockly.blockRendering.RenderInfo');
goog.require('Blockly.blockRendering.Row');
goog.require('Blockly.blockRendering.Types');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Svg');

View File

@@ -10,29 +10,36 @@
*/
'use strict';
goog.provide('Blockly.blockRendering.Drawer');
goog.module('Blockly.blockRendering.Drawer');
goog.module.declareLegacyNamespace();
goog.require('Blockly.blockRendering.RenderInfo');
goog.require('Blockly.blockRendering.Row');
goog.require('Blockly.blockRendering.Types');
goog.require('Blockly.utils.svgPaths');
goog.requireType('Blockly.blockRendering.ConstantProvider');
goog.requireType('Blockly.blockRendering.Field');
goog.requireType('Blockly.blockRendering.Icon');
goog.requireType('Blockly.blockRendering.InlineInput');
goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const ConstantProvider = goog.requireType('Blockly.blockRendering.ConstantProvider');
/* eslint-disable-next-line no-unused-vars */
const Field = goog.requireType('Blockly.blockRendering.Field');
/* eslint-disable-next-line no-unused-vars */
const Icon = goog.requireType('Blockly.blockRendering.Icon');
/* eslint-disable-next-line no-unused-vars */
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 Types = goog.require('Blockly.blockRendering.Types');
const svgPaths = goog.require('Blockly.utils.svgPaths');
/**
* An object that draws a block based on the given rendering information.
* @param {!Blockly.BlockSvg} block The block to render.
* @param {!Blockly.blockRendering.RenderInfo} info An object containing all
* @param {!BlockSvg} block The block to render.
* @param {!RenderInfo} info An object containing all
* information needed to render this block.
* @package
* @constructor
*/
Blockly.blockRendering.Drawer = function(block, info) {
const Drawer = function(block, info) {
this.block_ = block;
this.info_ = info;
this.topLeft_ = block.getRelativeToSurfaceXY();
@@ -41,7 +48,7 @@ Blockly.blockRendering.Drawer = function(block, info) {
/**
* The renderer's constant provider.
* @type {!Blockly.blockRendering.ConstantProvider}
* @type {!ConstantProvider}
* @protected
*/
this.constants_ = info.getRenderer().getConstants();
@@ -57,7 +64,7 @@ Blockly.blockRendering.Drawer = function(block, info) {
* required.
* @package
*/
Blockly.blockRendering.Drawer.prototype.draw = function() {
Drawer.prototype.draw = function() {
this.hideHiddenIcons_();
this.drawOutline_();
this.drawInternals_();
@@ -78,7 +85,7 @@ Blockly.blockRendering.Drawer.prototype.draw = function() {
* render. Anything that needs to be kept around should be set in this function.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.recordSizeOnBlock_ = function() {
Drawer.prototype.recordSizeOnBlock_ = function() {
// This is used when the block is reporting its size to anyone else.
// The dark path adds to the size of the block in both X and Y.
this.block_.height = this.info_.height;
@@ -89,8 +96,8 @@ Blockly.blockRendering.Drawer.prototype.recordSizeOnBlock_ = function() {
* Hide icons that were marked as hidden.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.hideHiddenIcons_ = function() {
for (var i = 0, iconInfo; (iconInfo = this.info_.hiddenIcons[i]); i++) {
Drawer.prototype.hideHiddenIcons_ = function() {
for (let i = 0, iconInfo; (iconInfo = this.info_.hiddenIcons[i]); i++) {
iconInfo.icon.iconGroup_.setAttribute('display', 'none');
}
};
@@ -99,10 +106,10 @@ Blockly.blockRendering.Drawer.prototype.hideHiddenIcons_ = function() {
* Create the outline of the block. This is a single continuous path.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawOutline_ = function() {
Drawer.prototype.drawOutline_ = function() {
this.drawTop_();
for (var r = 1; r < this.info_.rows.length - 1; r++) {
var row = this.info_.rows[r];
for (let r = 1; r < this.info_.rows.length - 1; r++) {
const row = this.info_.rows[r];
if (row.hasJaggedEdge) {
this.drawJaggedEdge_(row);
} else if (row.hasStatement) {
@@ -123,91 +130,80 @@ Blockly.blockRendering.Drawer.prototype.drawOutline_ = function() {
* details such as hats and rounded corners.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawTop_ = function() {
var topRow = this.info_.topRow;
var elements = topRow.elements;
Drawer.prototype.drawTop_ = function() {
const topRow = this.info_.topRow;
const elements = topRow.elements;
this.positionPreviousConnection_();
this.outlinePath_ +=
Blockly.utils.svgPaths.moveBy(topRow.xPos, this.info_.startY);
for (var i = 0, elem; (elem = elements[i]); i++) {
if (Blockly.blockRendering.Types.isLeftRoundedCorner(elem)) {
this.outlinePath_ +=
this.constants_.OUTSIDE_CORNERS.topLeft;
} else if (Blockly.blockRendering.Types.isRightRoundedCorner(elem)) {
this.outlinePath_ +=
this.constants_.OUTSIDE_CORNERS.topRight;
} else if (Blockly.blockRendering.Types.isPreviousConnection(elem)) {
this.outlinePath_ += svgPaths.moveBy(topRow.xPos, this.info_.startY);
for (let i = 0, elem; (elem = elements[i]); i++) {
if (Types.isLeftRoundedCorner(elem)) {
this.outlinePath_ += this.constants_.OUTSIDE_CORNERS.topLeft;
} else if (Types.isRightRoundedCorner(elem)) {
this.outlinePath_ += this.constants_.OUTSIDE_CORNERS.topRight;
} else if (Types.isPreviousConnection(elem)) {
this.outlinePath_ += elem.shape.pathLeft;
} else if (Blockly.blockRendering.Types.isHat(elem)) {
} else if (Types.isHat(elem)) {
this.outlinePath_ += this.constants_.START_HAT.path;
} else if (Blockly.blockRendering.Types.isSpacer(elem)) {
this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis('h', elem.width);
} else if (Types.isSpacer(elem)) {
this.outlinePath_ += svgPaths.lineOnAxis('h', elem.width);
}
// No branch for a square corner, because it's a no-op.
}
this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis('v', topRow.height);
this.outlinePath_ += svgPaths.lineOnAxis('v', topRow.height);
};
/**
* Add steps for the jagged edge of a row on a collapsed block.
* @param {!Blockly.blockRendering.Row} row The row to draw the side of.
* @param {!Row} row The row to draw the side of.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawJaggedEdge_ = function(row) {
var remainder =
row.height - this.constants_.JAGGED_TEETH.height;
this.outlinePath_ += this.constants_.JAGGED_TEETH.path +
Blockly.utils.svgPaths.lineOnAxis('v', remainder);
Drawer.prototype.drawJaggedEdge_ = function(row) {
const remainder = row.height - this.constants_.JAGGED_TEETH.height;
this.outlinePath_ +=
this.constants_.JAGGED_TEETH.path + svgPaths.lineOnAxis('v', remainder);
};
/**
* Add steps for an external value input, rendered as a notch in the side
* of the block.
* @param {!Blockly.blockRendering.Row} row The row that this input
* belongs to.
* @param {!Row} row The row that this input belongs to.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawValueInput_ = function(row) {
var input = row.getLastInput();
Drawer.prototype.drawValueInput_ = function(row) {
const input = row.getLastInput();
this.positionExternalValueConnection_(row);
var pathDown = (typeof input.shape.pathDown == "function") ?
const pathDown = (typeof input.shape.pathDown == 'function') ?
input.shape.pathDown(input.height) :
input.shape.pathDown;
this.outlinePath_ +=
Blockly.utils.svgPaths.lineOnAxis('H', input.xPos + input.width) +
pathDown +
Blockly.utils.svgPaths.lineOnAxis('v', row.height - input.connectionHeight);
this.outlinePath_ += svgPaths.lineOnAxis('H', input.xPos + input.width) +
pathDown + svgPaths.lineOnAxis('v', row.height - input.connectionHeight);
};
/**
* Add steps for a statement input.
* @param {!Blockly.blockRendering.Row} row The row that this input
* belongs to.
* @param {!Row} row The row that this input belongs to.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawStatementInput_ = function(row) {
var input = row.getLastInput();
Drawer.prototype.drawStatementInput_ = function(row) {
const input = row.getLastInput();
// Where to start drawing the notch, which is on the right side in LTR.
var x = input.xPos + input.notchOffset + input.shape.width;
const x = input.xPos + input.notchOffset + input.shape.width;
var innerTopLeftCorner =
input.shape.pathRight +
Blockly.utils.svgPaths.lineOnAxis('h',
-(input.notchOffset - this.constants_.INSIDE_CORNERS.width)) +
const innerTopLeftCorner = input.shape.pathRight +
svgPaths.lineOnAxis(
'h', -(input.notchOffset - this.constants_.INSIDE_CORNERS.width)) +
this.constants_.INSIDE_CORNERS.pathTop;
var innerHeight =
row.height - (2 * this.constants_.INSIDE_CORNERS.height);
const innerHeight = row.height - (2 * this.constants_.INSIDE_CORNERS.height);
this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis('H', x) +
innerTopLeftCorner +
Blockly.utils.svgPaths.lineOnAxis('v', innerHeight) +
this.outlinePath_ += svgPaths.lineOnAxis('H', x) + innerTopLeftCorner +
svgPaths.lineOnAxis('v', innerHeight) +
this.constants_.INSIDE_CORNERS.pathBottom +
Blockly.utils.svgPaths.lineOnAxis('H', row.xPos + row.width);
svgPaths.lineOnAxis('H', row.xPos + row.width);
this.positionStatementInputConnection_(row);
};
@@ -215,13 +211,11 @@ Blockly.blockRendering.Drawer.prototype.drawStatementInput_ = function(row) {
/**
* Add steps for the right side of a row that does not have value or
* statement input connections.
* @param {!Blockly.blockRendering.Row} row The row to draw the
* side of.
* @param {!Row} row The row to draw the side of.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawRightSideRow_ = function(row) {
this.outlinePath_ +=
Blockly.utils.svgPaths.lineOnAxis('V', row.yPos + row.height);
Drawer.prototype.drawRightSideRow_ = function(row) {
this.outlinePath_ += svgPaths.lineOnAxis('V', row.yPos + row.height);
};
@@ -230,30 +224,30 @@ Blockly.blockRendering.Drawer.prototype.drawRightSideRow_ = function(row) {
* for the next connection.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawBottom_ = function() {
var bottomRow = this.info_.bottomRow;
var elems = bottomRow.elements;
Drawer.prototype.drawBottom_ = function() {
const bottomRow = this.info_.bottomRow;
const elems = bottomRow.elements;
this.positionNextConnection_();
var rightCornerYOffset = 0;
var outlinePath = '';
for (var i = elems.length - 1, elem; (elem = elems[i]); i--) {
if (Blockly.blockRendering.Types.isNextConnection(elem)) {
let rightCornerYOffset = 0;
let outlinePath = '';
for (let i = elems.length - 1, elem; (elem = elems[i]); i--) {
if (Types.isNextConnection(elem)) {
outlinePath += elem.shape.pathRight;
} else if (Blockly.blockRendering.Types.isLeftSquareCorner(elem)) {
outlinePath += Blockly.utils.svgPaths.lineOnAxis('H', bottomRow.xPos);
} else if (Blockly.blockRendering.Types.isLeftRoundedCorner(elem)) {
} else if (Types.isLeftSquareCorner(elem)) {
outlinePath += svgPaths.lineOnAxis('H', bottomRow.xPos);
} else if (Types.isLeftRoundedCorner(elem)) {
outlinePath += this.constants_.OUTSIDE_CORNERS.bottomLeft;
} else if (Blockly.blockRendering.Types.isRightRoundedCorner(elem)) {
} else if (Types.isRightRoundedCorner(elem)) {
outlinePath += this.constants_.OUTSIDE_CORNERS.bottomRight;
rightCornerYOffset = this.constants_.OUTSIDE_CORNERS.rightHeight;
} else if (Blockly.blockRendering.Types.isSpacer(elem)) {
outlinePath += Blockly.utils.svgPaths.lineOnAxis('h', elem.width * -1);
} else if (Types.isSpacer(elem)) {
outlinePath += svgPaths.lineOnAxis('h', elem.width * -1);
}
}
this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis('V',
bottomRow.baseline - rightCornerYOffset);
this.outlinePath_ +=
svgPaths.lineOnAxis('V', bottomRow.baseline - rightCornerYOffset);
this.outlinePath_ += outlinePath;
};
@@ -262,21 +256,19 @@ Blockly.blockRendering.Drawer.prototype.drawBottom_ = function() {
* connection
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawLeft_ = function() {
var outputConnection = this.info_.outputConnection;
Drawer.prototype.drawLeft_ = function() {
const outputConnection = this.info_.outputConnection;
this.positionOutputConnection_();
if (outputConnection) {
var tabBottom = outputConnection.connectionOffsetY +
outputConnection.height;
var pathUp = (typeof outputConnection.shape.pathUp == "function") ?
const tabBottom =
outputConnection.connectionOffsetY + outputConnection.height;
const pathUp = (typeof outputConnection.shape.pathUp == 'function') ?
outputConnection.shape.pathUp(outputConnection.height) :
outputConnection.shape.pathUp;
// Draw a line up to the bottom of the tab.
this.outlinePath_ +=
Blockly.utils.svgPaths.lineOnAxis('V', tabBottom) +
pathUp;
this.outlinePath_ += svgPaths.lineOnAxis('V', tabBottom) + pathUp;
}
// Close off the path. This draws a vertical line up to the start of the
// block's path, which may be either a rounded or a sharp corner.
@@ -288,16 +280,15 @@ Blockly.blockRendering.Drawer.prototype.drawLeft_ = function() {
* not depend on the outer path for placement.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawInternals_ = function() {
for (var i = 0, row; (row = this.info_.rows[i]); i++) {
for (var j = 0, elem; (elem = row.elements[j]); j++) {
if (Blockly.blockRendering.Types.isInlineInput(elem)) {
Drawer.prototype.drawInternals_ = function() {
for (let i = 0, row; (row = this.info_.rows[i]); i++) {
for (let j = 0, elem; (elem = row.elements[j]); j++) {
if (Types.isInlineInput(elem)) {
this.drawInlineInput_(
/** @type {!Blockly.blockRendering.InlineInput} */ (elem));
} else if (Blockly.blockRendering.Types.isIcon(elem) ||
Blockly.blockRendering.Types.isField(elem)) {
/** @type {!InlineInput} */ (elem));
} else if (Types.isIcon(elem) || Types.isField(elem)) {
this.layoutField_(
/** @type {!Blockly.blockRendering.Field|!Blockly.blockRendering.Icon} */
/** @type {!Field|!Icon} */
(elem));
}
}
@@ -306,20 +297,21 @@ Blockly.blockRendering.Drawer.prototype.drawInternals_ = function() {
/**
* Push a field or icon's new position to its SVG root.
* @param {!Blockly.blockRendering.Icon|!Blockly.blockRendering.Field} fieldInfo
* @param {!Icon|!Field} fieldInfo
* The rendering information for the field or icon.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.layoutField_ = function(fieldInfo) {
if (Blockly.blockRendering.Types.isField(fieldInfo)) {
var svgGroup = fieldInfo.field.getSvgRoot();
} else if (Blockly.blockRendering.Types.isIcon(fieldInfo)) {
var svgGroup = fieldInfo.icon.iconGroup_;
Drawer.prototype.layoutField_ = function(fieldInfo) {
let svgGroup;
if (Types.isField(fieldInfo)) {
svgGroup = fieldInfo.field.getSvgRoot();
} else if (Types.isIcon(fieldInfo)) {
svgGroup = fieldInfo.icon.iconGroup_;
}
var yPos = fieldInfo.centerline - fieldInfo.height / 2;
var xPos = fieldInfo.xPos;
var scale = '';
const yPos = fieldInfo.centerline - fieldInfo.height / 2;
let xPos = fieldInfo.xPos;
let scale = '';
if (this.info_.RTL) {
xPos = -(xPos + fieldInfo.width);
if (fieldInfo.flipRtl) {
@@ -327,7 +319,7 @@ Blockly.blockRendering.Drawer.prototype.layoutField_ = function(fieldInfo) {
scale = 'scale(-1 1)';
}
}
if (Blockly.blockRendering.Types.isIcon(fieldInfo)) {
if (Types.isIcon(fieldInfo)) {
svgGroup.setAttribute('display', 'block');
svgGroup.setAttribute('transform', 'translate(' + xPos + ',' + yPos + ')');
fieldInfo.icon.computeIconLocation();
@@ -345,26 +337,24 @@ Blockly.blockRendering.Drawer.prototype.layoutField_ = function(fieldInfo) {
/**
* Add steps for an inline input.
* @param {!Blockly.blockRendering.InlineInput} input The information about the
* @param {!InlineInput} input The information about the
* input to render.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawInlineInput_ = function(input) {
var width = input.width;
var height = input.height;
var yPos = input.centerline - height / 2;
Drawer.prototype.drawInlineInput_ = function(input) {
const width = input.width;
const height = input.height;
const yPos = input.centerline - height / 2;
var connectionTop = input.connectionOffsetY;
var connectionBottom = input.connectionHeight + connectionTop;
var connectionRight = input.xPos + input.connectionWidth;
const connectionTop = input.connectionOffsetY;
const connectionBottom = input.connectionHeight + connectionTop;
const connectionRight = input.xPos + input.connectionWidth;
this.inlinePath_ += Blockly.utils.svgPaths.moveTo(connectionRight, yPos) +
Blockly.utils.svgPaths.lineOnAxis('v', connectionTop) +
input.shape.pathDown +
Blockly.utils.svgPaths.lineOnAxis('v', height - connectionBottom) +
Blockly.utils.svgPaths.lineOnAxis('h', width - input.connectionWidth) +
Blockly.utils.svgPaths.lineOnAxis('v', -height) +
'z';
this.inlinePath_ += svgPaths.moveTo(connectionRight, yPos) +
svgPaths.lineOnAxis('v', connectionTop) + input.shape.pathDown +
svgPaths.lineOnAxis('v', height - connectionBottom) +
svgPaths.lineOnAxis('h', width - input.connectionWidth) +
svgPaths.lineOnAxis('v', -height) + 'z';
this.positionInlineInputConnection_(input);
};
@@ -373,21 +363,21 @@ Blockly.blockRendering.Drawer.prototype.drawInlineInput_ = function(input) {
* Position the connection on an inline value input, taking into account
* RTL and the small gap between the parent block and child block which lets the
* parent block's dark path show through.
* @param {Blockly.blockRendering.InlineInput} input The information about
* @param {InlineInput} input The information about
* the input that the connection is on.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.positionInlineInputConnection_ = function(input) {
var yPos = input.centerline - input.height / 2;
Drawer.prototype.positionInlineInputConnection_ = function(input) {
const yPos = input.centerline - input.height / 2;
// Move the connection.
if (input.connectionModel) {
// xPos already contains info about startX
var connX = input.xPos + input.connectionWidth + input.connectionOffsetX;
let connX = input.xPos + input.connectionWidth + input.connectionOffsetX;
if (this.info_.RTL) {
connX *= -1;
}
input.connectionModel.setOffsetInBlock(connX,
yPos + input.connectionOffsetY);
input.connectionModel.setOffsetInBlock(
connX, yPos + input.connectionOffsetY);
}
};
@@ -395,13 +385,13 @@ Blockly.blockRendering.Drawer.prototype.positionInlineInputConnection_ = functio
* Position the connection on a statement input, taking into account
* RTL and the small gap between the parent block and child block which lets the
* parent block's dark path show through.
* @param {!Blockly.blockRendering.Row} row The row that the connection is on.
* @param {!Row} row The row that the connection is on.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.positionStatementInputConnection_ = function(row) {
var input = row.getLastInput();
Drawer.prototype.positionStatementInputConnection_ = function(row) {
const input = row.getLastInput();
if (input.connectionModel) {
var connX = row.xPos + row.statementEdge + input.notchOffset;
let connX = row.xPos + row.statementEdge + input.notchOffset;
if (this.info_.RTL) {
connX *= -1;
}
@@ -413,13 +403,13 @@ Blockly.blockRendering.Drawer.prototype.positionStatementInputConnection_ = func
* Position the connection on an external value input, taking into account
* RTL and the small gap between the parent block and child block which lets the
* parent block's dark path show through.
* @param {!Blockly.blockRendering.Row} row The row that the connection is on.
* @param {!Row} row The row that the connection is on.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.positionExternalValueConnection_ = function(row) {
var input = row.getLastInput();
Drawer.prototype.positionExternalValueConnection_ = function(row) {
const input = row.getLastInput();
if (input.connectionModel) {
var connX = row.xPos + row.width;
let connX = row.xPos + row.width;
if (this.info_.RTL) {
connX *= -1;
}
@@ -431,11 +421,11 @@ Blockly.blockRendering.Drawer.prototype.positionExternalValueConnection_ = funct
* Position the previous connection on a block.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.positionPreviousConnection_ = function() {
var topRow = this.info_.topRow;
Drawer.prototype.positionPreviousConnection_ = function() {
const topRow = this.info_.topRow;
if (topRow.connection) {
var x = topRow.xPos + topRow.notchOffset;
var connX = (this.info_.RTL ? -x : x);
const x = topRow.xPos + topRow.notchOffset;
const connX = (this.info_.RTL ? -x : x);
topRow.connection.connectionModel.setOffsetInBlock(connX, 0);
}
};
@@ -444,13 +434,13 @@ Blockly.blockRendering.Drawer.prototype.positionPreviousConnection_ = function()
* Position the next connection on a block.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.positionNextConnection_ = function() {
var bottomRow = this.info_.bottomRow;
Drawer.prototype.positionNextConnection_ = function() {
const bottomRow = this.info_.bottomRow;
if (bottomRow.connection) {
var connInfo = bottomRow.connection;
var x = connInfo.xPos; // Already contains info about startX.
var connX = (this.info_.RTL ? -x : x);
const connInfo = bottomRow.connection;
const x = connInfo.xPos; // Already contains info about startX.
const connX = (this.info_.RTL ? -x : x);
connInfo.connectionModel.setOffsetInBlock(connX, bottomRow.baseline);
}
};
@@ -459,11 +449,13 @@ Blockly.blockRendering.Drawer.prototype.positionNextConnection_ = function() {
* Position the output connection on a block.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.positionOutputConnection_ = function() {
Drawer.prototype.positionOutputConnection_ = function() {
if (this.info_.outputConnection) {
var x = this.info_.startX + this.info_.outputConnection.connectionOffsetX;
var connX = this.info_.RTL ? -x : x;
this.block_.outputConnection.setOffsetInBlock(connX,
this.info_.outputConnection.connectionOffsetY);
const x = this.info_.startX + this.info_.outputConnection.connectionOffsetX;
const connX = this.info_.RTL ? -x : x;
this.block_.outputConnection.setOffsetInBlock(
connX, this.info_.outputConnection.connectionOffsetY);
}
};
exports = Drawer;

View File

@@ -10,37 +10,45 @@
*/
'use strict';
goog.provide('Blockly.blockRendering.RenderInfo');
goog.module('Blockly.blockRendering.RenderInfo');
goog.module.declareLegacyNamespace();
goog.require('Blockly.blockRendering.BottomRow');
goog.require('Blockly.blockRendering.ExternalValueInput');
goog.require('Blockly.blockRendering.Field');
goog.require('Blockly.blockRendering.Hat');
goog.require('Blockly.blockRendering.InlineInput');
goog.require('Blockly.blockRendering.InputRow');
goog.require('Blockly.blockRendering.InRowSpacer');
goog.require('Blockly.blockRendering.JaggedEdge');
goog.require('Blockly.blockRendering.Measurable');
goog.require('Blockly.blockRendering.NextConnection');
goog.require('Blockly.blockRendering.OutputConnection');
goog.require('Blockly.blockRendering.PreviousConnection');
goog.require('Blockly.blockRendering.RoundCorner');
goog.require('Blockly.blockRendering.Row');
goog.require('Blockly.blockRendering.SpacerRow');
goog.require('Blockly.blockRendering.SquareCorner');
goog.require('Blockly.blockRendering.StatementInput');
goog.require('Blockly.blockRendering.TopRow');
goog.require('Blockly.blockRendering.Types');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.inputTypes');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const BottomRow = goog.require('Blockly.blockRendering.BottomRow');
/* eslint-disable-next-line no-unused-vars */
const ConstantProvider = goog.requireType('Blockly.blockRendering.ConstantProvider');
const ExternalValueInput = goog.require('Blockly.blockRendering.ExternalValueInput');
const Field = goog.require('Blockly.blockRendering.Field');
const Hat = goog.require('Blockly.blockRendering.Hat');
const Icon = goog.require('Blockly.blockRendering.Icon');
const InlineInput = goog.require('Blockly.blockRendering.InlineInput');
/* eslint-disable-next-line no-unused-vars */
const Input = goog.requireType('Blockly.Input');
const InputRow = goog.require('Blockly.blockRendering.InputRow');
const InRowSpacer = goog.require('Blockly.blockRendering.InRowSpacer');
const JaggedEdge = goog.require('Blockly.blockRendering.JaggedEdge');
/* eslint-disable-next-line no-unused-vars */
const Measurable = goog.require('Blockly.blockRendering.Measurable');
const NextConnection = goog.require('Blockly.blockRendering.NextConnection');
const OutputConnection = goog.require('Blockly.blockRendering.OutputConnection');
const PreviousConnection = goog.require('Blockly.blockRendering.PreviousConnection');
/* eslint-disable-next-line no-unused-vars */
const RenderedConnection = goog.requireType('Blockly.RenderedConnection');
/* eslint-disable-next-line no-unused-vars */
const Renderer = goog.requireType('Blockly.blockRendering.Renderer');
/* eslint-disable-next-line no-unused-vars */
const RoundCorner = goog.require('Blockly.blockRendering.RoundCorner');
/* eslint-disable-next-line no-unused-vars */
const Row = goog.require('Blockly.blockRendering.Row');
const SpacerRow = goog.require('Blockly.blockRendering.SpacerRow');
const SquareCorner = goog.require('Blockly.blockRendering.SquareCorner');
const StatementInput = goog.require('Blockly.blockRendering.StatementInput');
const TopRow = goog.require('Blockly.blockRendering.TopRow');
const Types = goog.require('Blockly.blockRendering.Types');
const {ALIGN} = goog.require('Blockly.constants');
const {DUMMY, STATEMENT, VALUE} = goog.require('Blockly.inputTypes');
goog.requireType('Blockly.blockRendering.ConstantProvider');
goog.requireType('Blockly.blockRendering.Icon');
goog.requireType('Blockly.blockRendering.Renderer');
goog.requireType('Blockly.BlockSvg');
goog.requireType('Blockly.Input');
goog.requireType('Blockly.RenderedConnection');
/**
@@ -50,24 +58,24 @@ goog.requireType('Blockly.RenderedConnection');
* may choose to rerender when getSize() is called). However, calling it
* repeatedly may be expensive.
*
* @param {!Blockly.blockRendering.Renderer} renderer The renderer in use.
* @param {!Blockly.BlockSvg} block The block to measure.
* @param {!Renderer} renderer The renderer in use.
* @param {!BlockSvg} block The block to measure.
* @constructor
* @package
*/
Blockly.blockRendering.RenderInfo = function(renderer, block) {
const RenderInfo = function(renderer, block) {
this.block_ = block;
/**
* The block renderer in use.
* @type {!Blockly.blockRendering.Renderer}
* @type {!Renderer}
* @protected
*/
this.renderer_ = renderer;
/**
* The renderer's constant provider.
* @type {!Blockly.blockRendering.ConstantProvider}
* @type {!ConstantProvider}
* @protected
*/
this.constants_ = this.renderer_.getConstants();
@@ -75,12 +83,13 @@ Blockly.blockRendering.RenderInfo = function(renderer, block) {
/**
* A measurable representing the output connection if the block has one.
* Otherwise null.
* @type {Blockly.blockRendering.OutputConnection}
* @type {OutputConnection}
*/
this.outputConnection = !block.outputConnection ? null :
new Blockly.blockRendering.OutputConnection(
this.outputConnection = !block.outputConnection ?
null :
new OutputConnection(
this.constants_,
/** @type {Blockly.RenderedConnection} */(block.outputConnection));
/** @type {RenderedConnection} */ (block.outputConnection));
/**
* Whether the block should be rendered as a single line, either because it's
@@ -135,33 +144,33 @@ Blockly.blockRendering.RenderInfo = function(renderer, block) {
/**
* An array of Row objects containing sizing information.
* @type {!Array<!Blockly.blockRendering.Row>}
* @type {!Array<!Row>}
*/
this.rows = [];
/**
* An array of input rows on the block.
* @type {!Array<!Blockly.blockRendering.InputRow>}
* @type {!Array<!InputRow>}
*/
this.inputRows = [];
/**
* An array of measurable objects containing hidden icons.
* @type {!Array<!Blockly.blockRendering.Icon>}
* @type {!Array<!Icon>}
*/
this.hiddenIcons = [];
/**
* An object with rendering information about the top row of the block.
* @type {!Blockly.blockRendering.TopRow}
* @type {!TopRow}
*/
this.topRow = new Blockly.blockRendering.TopRow(this.constants_);
this.topRow = new TopRow(this.constants_);
/**
* An object with rendering information about the bottom row of the block.
* @type {!Blockly.blockRendering.BottomRow}
* @type {!BottomRow}
*/
this.bottomRow = new Blockly.blockRendering.BottomRow(this.constants_);
this.bottomRow = new BottomRow(this.constants_);
// The position of the start point for drawing, relative to the block's
// location.
@@ -171,10 +180,10 @@ Blockly.blockRendering.RenderInfo = function(renderer, block) {
/**
* Get the block renderer in use.
* @return {!Blockly.blockRendering.Renderer} The block renderer in use.
* @return {!Renderer} The block renderer in use.
* @package
*/
Blockly.blockRendering.RenderInfo.prototype.getRenderer = function() {
RenderInfo.prototype.getRenderer = function() {
return this.renderer_;
};
@@ -188,7 +197,7 @@ Blockly.blockRendering.RenderInfo.prototype.getRenderer = function() {
*
* @package
*/
Blockly.blockRendering.RenderInfo.prototype.measure = function() {
RenderInfo.prototype.measure = function() {
this.createRows_();
this.addElemSpacing_();
this.addRowSpacing_();
@@ -202,16 +211,16 @@ Blockly.blockRendering.RenderInfo.prototype.measure = function() {
* block.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() {
RenderInfo.prototype.createRows_ = function() {
this.populateTopRow_();
this.rows.push(this.topRow);
var activeRow = new Blockly.blockRendering.InputRow(this.constants_);
let activeRow = new InputRow(this.constants_);
this.inputRows.push(activeRow);
// Icons always go on the first row, before anything else.
var icons = this.block_.getIcons();
for (var i = 0, icon; (icon = icons[i]); i++) {
var iconInfo = new Blockly.blockRendering.Icon(this.constants_, icon);
const icons = this.block_.getIcons();
for (let i = 0, icon; (icon = icons[i]); i++) {
const iconInfo = new Icon(this.constants_, icon);
if (this.isCollapsed && icon.collapseHidden) {
this.hiddenIcons.push(iconInfo);
} else {
@@ -219,24 +228,23 @@ Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() {
}
}
var lastInput = null;
let lastInput = null;
// Loop across all of the inputs on the block, creating objects for anything
// that needs to be rendered and breaking the block up into visual rows.
for (var i = 0, input; (input = this.block_.inputList[i]); i++) {
for (let i = 0, input; (input = this.block_.inputList[i]); i++) {
if (!input.isVisible()) {
continue;
}
if (this.shouldStartNewRow_(input, lastInput)) {
// Finish this row and create a new one.
this.rows.push(activeRow);
activeRow = new Blockly.blockRendering.InputRow(this.constants_);
activeRow = new InputRow(this.constants_);
this.inputRows.push(activeRow);
}
// All of the fields in an input go on the same row.
for (var j = 0, field; (field = input.fieldRow[j]); j++) {
activeRow.elements.push(
new Blockly.blockRendering.Field(this.constants_, field, input));
for (let j = 0, field; (field = input.fieldRow[j]); j++) {
activeRow.elements.push(new Field(this.constants_, field, input));
}
this.addInput_(input, activeRow);
lastInput = input;
@@ -244,8 +252,7 @@ Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() {
if (this.isCollapsed) {
activeRow.hasJaggedEdge = true;
activeRow.elements.push(
new Blockly.blockRendering.JaggedEdge(this.constants_));
activeRow.elements.push(new JaggedEdge(this.constants_));
}
if (activeRow.elements.length || activeRow.hasDummyInput) {
@@ -259,31 +266,31 @@ Blockly.blockRendering.RenderInfo.prototype.createRows_ = function() {
* Create all non-spacer elements that belong on the top row.
* @package
*/
Blockly.blockRendering.RenderInfo.prototype.populateTopRow_ = function() {
var hasPrevious = !!this.block_.previousConnection;
var hasHat = (this.block_.hat ?
this.block_.hat === 'cap' : this.constants_.ADD_START_HATS) &&
RenderInfo.prototype.populateTopRow_ = function() {
const hasPrevious = !!this.block_.previousConnection;
const hasHat = (this.block_.hat ? this.block_.hat === 'cap' :
this.constants_.ADD_START_HATS) &&
!this.outputConnection && !hasPrevious;
var cornerClass = this.topRow.hasLeftSquareCorner(this.block_) ?
Blockly.blockRendering.SquareCorner : Blockly.blockRendering.RoundCorner;
let cornerClass =
this.topRow.hasLeftSquareCorner(this.block_) ? SquareCorner : RoundCorner;
this.topRow.elements.push(new cornerClass(this.constants_));
if (hasHat) {
var hat = new Blockly.blockRendering.Hat(this.constants_);
const hat = new Hat(this.constants_);
this.topRow.elements.push(hat);
this.topRow.capline = hat.ascenderHeight;
} else if (hasPrevious) {
this.topRow.hasPreviousConnection = true;
this.topRow.connection = new Blockly.blockRendering.PreviousConnection(
this.topRow.connection = new PreviousConnection(
this.constants_,
/** @type {Blockly.RenderedConnection} */
/** @type {RenderedConnection} */
(this.block_.previousConnection));
this.topRow.elements.push(this.topRow.connection);
}
var precedesStatement = this.block_.inputList.length &&
this.block_.inputList[0].type == Blockly.inputTypes.STATEMENT;
const precedesStatement = this.block_.inputList.length &&
this.block_.inputList[0].type == STATEMENT;
// This is the minimum height for the row. If one of its elements has a
// greater height it will be overwritten in the compute pass.
@@ -294,8 +301,8 @@ Blockly.blockRendering.RenderInfo.prototype.populateTopRow_ = function() {
this.topRow.minHeight = this.constants_.TOP_ROW_MIN_HEIGHT;
}
cornerClass = this.topRow.hasRightSquareCorner(this.block_) ?
Blockly.blockRendering.SquareCorner : Blockly.blockRendering.RoundCorner;
cornerClass = this.topRow.hasRightSquareCorner(this.block_) ? SquareCorner :
RoundCorner;
this.topRow.elements.push(new cornerClass(this.constants_, 'right'));
};
@@ -303,79 +310,72 @@ Blockly.blockRendering.RenderInfo.prototype.populateTopRow_ = function() {
* Create all non-spacer elements that belong on the bottom row.
* @package
*/
Blockly.blockRendering.RenderInfo.prototype.populateBottomRow_ = function() {
RenderInfo.prototype.populateBottomRow_ = function() {
this.bottomRow.hasNextConnection = !!this.block_.nextConnection;
var followsStatement = this.block_.inputList.length &&
this.block_.inputList[this.block_.inputList.length - 1].type ==
Blockly.inputTypes.STATEMENT;
const followsStatement = this.block_.inputList.length &&
this.block_.inputList[this.block_.inputList.length - 1].type == STATEMENT;
// This is the minimum height for the row. If one of its elements has a
// greater height it will be overwritten in the compute pass.
if (followsStatement) {
this.bottomRow.minHeight =
this.constants_.BOTTOM_ROW_AFTER_STATEMENT_MIN_HEIGHT;
this.constants_.BOTTOM_ROW_AFTER_STATEMENT_MIN_HEIGHT;
} else {
this.bottomRow.minHeight = this.constants_.BOTTOM_ROW_MIN_HEIGHT;
}
var leftSquareCorner = this.bottomRow.hasLeftSquareCorner(this.block_);
const leftSquareCorner = this.bottomRow.hasLeftSquareCorner(this.block_);
if (leftSquareCorner) {
this.bottomRow.elements.push(
new Blockly.blockRendering.SquareCorner(this.constants_));
this.bottomRow.elements.push(new SquareCorner(this.constants_));
} else {
this.bottomRow.elements.push(
new Blockly.blockRendering.RoundCorner(this.constants_));
this.bottomRow.elements.push(new RoundCorner(this.constants_));
}
if (this.bottomRow.hasNextConnection) {
this.bottomRow.connection = new Blockly.blockRendering.NextConnection(
this.bottomRow.connection = new NextConnection(
this.constants_,
/** @type {Blockly.RenderedConnection} */ (this.block_.nextConnection));
/** @type {RenderedConnection} */ (this.block_.nextConnection));
this.bottomRow.elements.push(this.bottomRow.connection);
}
var rightSquareCorner = this.bottomRow.hasRightSquareCorner(this.block_);
const rightSquareCorner = this.bottomRow.hasRightSquareCorner(this.block_);
if (rightSquareCorner) {
this.bottomRow.elements.push(
new Blockly.blockRendering.SquareCorner(this.constants_, 'right'));
this.bottomRow.elements.push(new SquareCorner(this.constants_, 'right'));
} else {
this.bottomRow.elements.push(
new Blockly.blockRendering.RoundCorner(this.constants_, 'right'));
this.bottomRow.elements.push(new RoundCorner(this.constants_, 'right'));
}
};
/**
* Add an input element to the active row, if needed, and record the type of the
* input on the row.
* @param {!Blockly.Input} input The input to record information about.
* @param {!Blockly.blockRendering.Row} activeRow The row that is currently being
* @param {!Input} input The input to record information about.
* @param {!Row} activeRow The row that is currently being
* populated.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.addInput_ = function(input, activeRow) {
RenderInfo.prototype.addInput_ = function(input, activeRow) {
// Non-dummy inputs have visual representations onscreen.
if (this.isInline && input.type == Blockly.inputTypes.VALUE) {
activeRow.elements.push(
new Blockly.blockRendering.InlineInput(this.constants_, input));
if (this.isInline && input.type == VALUE) {
activeRow.elements.push(new InlineInput(this.constants_, input));
activeRow.hasInlineInput = true;
} else if (input.type == Blockly.inputTypes.STATEMENT) {
activeRow.elements.push(
new Blockly.blockRendering.StatementInput(this.constants_, input));
} else if (input.type == STATEMENT) {
activeRow.elements.push(new StatementInput(this.constants_, input));
activeRow.hasStatement = true;
} else if (input.type == Blockly.inputTypes.VALUE) {
activeRow.elements.push(
new Blockly.blockRendering.ExternalValueInput(this.constants_, input));
} else if (input.type == VALUE) {
activeRow.elements.push(new ExternalValueInput(this.constants_, input));
activeRow.hasExternalInput = true;
} else if (input.type == Blockly.inputTypes.DUMMY) {
} else if (input.type == DUMMY) {
// Dummy inputs have no visual representation, but the information is still
// important.
activeRow.minHeight = Math.max(activeRow.minHeight,
activeRow.minHeight = Math.max(
activeRow.minHeight,
input.getSourceBlock() && input.getSourceBlock().isShadow() ?
this.constants_.DUMMY_INPUT_SHADOW_MIN_HEIGHT :
this.constants_.DUMMY_INPUT_MIN_HEIGHT);
this.constants_.DUMMY_INPUT_SHADOW_MIN_HEIGHT :
this.constants_.DUMMY_INPUT_MIN_HEIGHT);
activeRow.hasDummyInput = true;
}
if (activeRow.align == null) {
@@ -385,25 +385,23 @@ Blockly.blockRendering.RenderInfo.prototype.addInput_ = function(input, activeRo
/**
* Decide whether to start a new row between the two Blockly.Inputs.
* @param {!Blockly.Input} input The first input to consider
* @param {Blockly.Input} lastInput The input that follows.
* @param {!Input} input The first input to consider
* @param {Input} lastInput The input that follows.
* @return {boolean} True if the next input should be rendered on a new row.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.shouldStartNewRow_ = function(input, lastInput) {
RenderInfo.prototype.shouldStartNewRow_ = function(input, lastInput) {
// If this is the first input, just add to the existing row.
// That row is either empty or has some icons in it.
if (!lastInput) {
return false;
}
// A statement input or an input following one always gets a new row.
if (input.type == Blockly.inputTypes.STATEMENT ||
lastInput.type == Blockly.inputTypes.STATEMENT) {
if (input.type == STATEMENT || lastInput.type == STATEMENT) {
return true;
}
// Value and dummy inputs get new row if inputs are not inlined.
if (input.type == Blockly.inputTypes.VALUE ||
input.type == Blockly.inputTypes.DUMMY) {
if (input.type == VALUE || input.type == DUMMY) {
return !this.isInline;
}
return false;
@@ -413,29 +411,28 @@ Blockly.blockRendering.RenderInfo.prototype.shouldStartNewRow_ = function(input,
* Add horizontal spacing between and around elements within each row.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.addElemSpacing_ = function() {
for (var i = 0, row; (row = this.rows[i]); i++) {
var oldElems = row.elements;
RenderInfo.prototype.addElemSpacing_ = function() {
for (let i = 0, row; (row = this.rows[i]); i++) {
const oldElems = row.elements;
row.elements = [];
// No spacing needed before the corner on the top row or the bottom row.
if (row.startsWithElemSpacer()) {
// There's a spacer before the first element in the row.
row.elements.push(new Blockly.blockRendering.InRowSpacer(
row.elements.push(new InRowSpacer(
this.constants_, this.getInRowSpacing_(null, oldElems[0])));
}
if (!oldElems.length) {
continue;
}
for (var e = 0; e < oldElems.length - 1; e++) {
for (let e = 0; e < oldElems.length - 1; e++) {
row.elements.push(oldElems[e]);
var spacing = this.getInRowSpacing_(oldElems[e], oldElems[e + 1]);
row.elements.push(
new Blockly.blockRendering.InRowSpacer(this.constants_, spacing));
const spacing = this.getInRowSpacing_(oldElems[e], oldElems[e + 1]);
row.elements.push(new InRowSpacer(this.constants_, spacing));
}
row.elements.push(oldElems[oldElems.length - 1]);
if (row.endsWithElemSpacer()) {
// There's a spacer after the last element in the row.
row.elements.push(new Blockly.blockRendering.InRowSpacer(
row.elements.push(new InRowSpacer(
this.constants_,
this.getInRowSpacing_(oldElems[oldElems.length - 1], null)));
}
@@ -446,42 +443,40 @@ Blockly.blockRendering.RenderInfo.prototype.addElemSpacing_ = function() {
* Calculate the width of a spacer element in a row based on the previous and
* next elements in that row. For instance, extra padding is added between two
* editable fields.
* @param {Blockly.blockRendering.Measurable} prev The element before the
* @param {Measurable} prev The element before the
* spacer.
* @param {Blockly.blockRendering.Measurable} next The element after the spacer.
* @param {Measurable} next The element after the spacer.
* @return {number} The size of the spacing between the two elements.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) {
RenderInfo.prototype.getInRowSpacing_ = function(prev, next) {
if (!prev) {
// Statement input padding.
if (next && Blockly.blockRendering.Types.isStatementInput(next)) {
if (next && Types.isStatementInput(next)) {
return this.constants_.STATEMENT_INPUT_PADDING_LEFT;
}
}
// Between inputs and the end of the row.
if (prev && Blockly.blockRendering.Types.isInput(prev) && !next) {
if (Blockly.blockRendering.Types.isExternalInput(prev)) {
if (prev && Types.isInput(prev) && !next) {
if (Types.isExternalInput(prev)) {
return this.constants_.NO_PADDING;
} else if (Blockly.blockRendering.Types.isInlineInput(prev)) {
} else if (Types.isInlineInput(prev)) {
return this.constants_.LARGE_PADDING;
} else if (Blockly.blockRendering.Types.isStatementInput(prev)) {
} else if (Types.isStatementInput(prev)) {
return this.constants_.NO_PADDING;
}
}
// Spacing between a square corner and a previous or next connection
if (prev && Blockly.blockRendering.Types.isLeftSquareCorner(prev) && next) {
if (Blockly.blockRendering.Types.isPreviousConnection(next) ||
Blockly.blockRendering.Types.isNextConnection(next)) {
if (prev && Types.isLeftSquareCorner(prev) && next) {
if (Types.isPreviousConnection(next) || Types.isNextConnection(next)) {
return next.notchOffset;
}
}
// Spacing between a rounded corner and a previous or next connection.
if (prev && Blockly.blockRendering.Types.isLeftRoundedCorner(prev) && next) {
if (Blockly.blockRendering.Types.isPreviousConnection(next) ||
Blockly.blockRendering.Types.isNextConnection(next)) {
if (prev && Types.isLeftRoundedCorner(prev) && next) {
if (Types.isPreviousConnection(next) || Types.isNextConnection(next)) {
return next.notchOffset - this.constants_.CORNER_RADIUS;
}
}
@@ -490,21 +485,21 @@ Blockly.blockRendering.RenderInfo.prototype.getInRowSpacing_ = function(prev, ne
};
/**
* Figure out where the right edge of the block and right edge of statement inputs
* should be placed.
* Figure out where the right edge of the block and right edge of statement
* inputs should be placed.
* @protected
*/
// TODO: More cleanup.
Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() {
var widestStatementRowFields = 0;
var blockWidth = 0;
var widestRowWithConnectedBlocks = 0;
for (var i = 0, row; (row = this.rows[i]); i++) {
RenderInfo.prototype.computeBounds_ = function() {
let widestStatementRowFields = 0;
let blockWidth = 0;
let widestRowWithConnectedBlocks = 0;
for (let i = 0, row; (row = this.rows[i]); i++) {
row.measure();
blockWidth = Math.max(blockWidth, row.width);
if (row.hasStatement) {
var statementInput = row.getLastInput();
var innerWidth = row.width - statementInput.width;
const statementInput = row.getLastInput();
const innerWidth = row.width - statementInput.width;
widestStatementRowFields = Math.max(widestStatementRowFields, innerWidth);
}
widestRowWithConnectedBlocks =
@@ -514,7 +509,7 @@ Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() {
this.statementEdge = widestStatementRowFields;
this.width = blockWidth;
for (var i = 0, row; (row = this.rows[i]); i++) {
for (let i = 0, row; (row = this.rows[i]); i++) {
if (row.hasStatement) {
row.statementEdge = this.statementEdge;
}
@@ -535,19 +530,19 @@ Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() {
* the sizes of all rows.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.alignRowElements_ = function() {
for (var i = 0, row; (row = this.rows[i]); i++) {
RenderInfo.prototype.alignRowElements_ = function() {
for (let i = 0, row; (row = this.rows[i]); i++) {
if (row.hasStatement) {
this.alignStatementRow_(
/** @type {!Blockly.blockRendering.InputRow} */ (row));
/** @type {!InputRow} */ (row));
} else {
var currentWidth = row.width;
var desiredWidth = this.getDesiredRowWidth_(row);
var missingSpace = desiredWidth - currentWidth;
const currentWidth = row.width;
const desiredWidth = this.getDesiredRowWidth_(row);
const missingSpace = desiredWidth - currentWidth;
if (missingSpace > 0) {
this.addAlignmentPadding_(row, missingSpace);
}
if (Blockly.blockRendering.Types.isTopOrBottomRow(row)) {
if (Types.isTopOrBottomRow(row)) {
row.widthWithConnectedBlocks = row.width;
}
}
@@ -556,12 +551,11 @@ Blockly.blockRendering.RenderInfo.prototype.alignRowElements_ = function() {
/**
* Calculate the desired width of an input row.
* @param {!Blockly.blockRendering.Row} _row The input row.
* @param {!Row} _row The input row.
* @return {number} The desired width of the input row.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.getDesiredRowWidth_ = function(
_row) {
RenderInfo.prototype.getDesiredRowWidth_ = function(_row) {
return this.width - this.startX;
};
@@ -569,27 +563,26 @@ Blockly.blockRendering.RenderInfo.prototype.getDesiredRowWidth_ = function(
* Modify the given row to add the given amount of padding around its fields.
* The exact location of the padding is based on the alignment property of the
* last input in the field.
* @param {Blockly.blockRendering.Row} row The row to add padding to.
* @param {Row} row The row to add padding to.
* @param {number} missingSpace How much padding to add.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.addAlignmentPadding_ = function(row,
missingSpace) {
var firstSpacer = row.getFirstSpacer();
var lastSpacer = row.getLastSpacer();
RenderInfo.prototype.addAlignmentPadding_ = function(row, missingSpace) {
const firstSpacer = row.getFirstSpacer();
const lastSpacer = row.getLastSpacer();
if (row.hasExternalInput || row.hasStatement) {
row.widthWithConnectedBlocks += missingSpace;
}
// Decide where the extra padding goes.
if (row.align == Blockly.constants.ALIGN.LEFT) {
if (row.align == ALIGN.LEFT) {
// Add padding to the end of the row.
lastSpacer.width += missingSpace;
} else if (row.align == Blockly.constants.ALIGN.CENTRE) {
} else if (row.align == ALIGN.CENTRE) {
// Split the padding between the beginning and end of the row.
firstSpacer.width += missingSpace / 2;
lastSpacer.width += missingSpace / 2;
} else if (row.align == Blockly.constants.ALIGN.RIGHT) {
} else if (row.align == ALIGN.RIGHT) {
// Add padding at the beginning of the row.
firstSpacer.width += missingSpace;
} else {
@@ -602,15 +595,15 @@ Blockly.blockRendering.RenderInfo.prototype.addAlignmentPadding_ = function(row,
/**
* Align the elements of a statement row based on computed bounds.
* Unlike other types of rows, statement rows add space in multiple places.
* @param {!Blockly.blockRendering.InputRow} row The statement row to resize.
* @param {!InputRow} row The statement row to resize.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.alignStatementRow_ = function(row) {
var statementInput = row.getLastInput();
var currentWidth = row.width - statementInput.width;
var desiredWidth = this.statementEdge;
RenderInfo.prototype.alignStatementRow_ = function(row) {
const statementInput = row.getLastInput();
let currentWidth = row.width - statementInput.width;
let desiredWidth = this.statementEdge;
// Add padding before the statement input.
var missingSpace = desiredWidth - currentWidth;
const missingSpace = desiredWidth - currentWidth;
if (missingSpace > 0) {
this.addAlignmentPadding_(row, missingSpace);
}
@@ -621,19 +614,19 @@ Blockly.blockRendering.RenderInfo.prototype.alignStatementRow_ = function(row) {
statementInput.width += (desiredWidth - currentWidth);
statementInput.height = Math.max(statementInput.height, row.height);
row.width += (desiredWidth - currentWidth);
row.widthWithConnectedBlocks = Math.max(row.width,
this.statementEdge + row.connectedBlockWidths);
row.widthWithConnectedBlocks =
Math.max(row.width, this.statementEdge + row.connectedBlockWidths);
};
/**
* Add spacers between rows and set their sizes.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.addRowSpacing_ = function() {
var oldRows = this.rows;
RenderInfo.prototype.addRowSpacing_ = function() {
const oldRows = this.rows;
this.rows = [];
for (var r = 0; r < oldRows.length; r++) {
for (let r = 0; r < oldRows.length; r++) {
this.rows.push(oldRows[r]);
if (r != oldRows.length - 1) {
this.rows.push(this.makeSpacerRow_(oldRows[r], oldRows[r + 1]));
@@ -643,16 +636,15 @@ Blockly.blockRendering.RenderInfo.prototype.addRowSpacing_ = function() {
/**
* Create a spacer row to go between prev and next, and set its size.
* @param {!Blockly.blockRendering.Row} prev The previous row.
* @param {!Blockly.blockRendering.Row} next The next row.
* @return {!Blockly.blockRendering.SpacerRow} The newly created spacer row.
* @param {!Row} prev The previous row.
* @param {!Row} next The next row.
* @return {!SpacerRow} The newly created spacer row.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.makeSpacerRow_ = function(prev, next) {
var height = this.getSpacerRowHeight_(prev, next);
var width = this.getSpacerRowWidth_(prev, next);
var spacer = new Blockly.blockRendering.SpacerRow(
this.constants_, height, width);
RenderInfo.prototype.makeSpacerRow_ = function(prev, next) {
const height = this.getSpacerRowHeight_(prev, next);
const width = this.getSpacerRowWidth_(prev, next);
const spacer = new SpacerRow(this.constants_, height, width);
if (prev.hasStatement) {
spacer.followsStatement = true;
}
@@ -664,25 +656,23 @@ Blockly.blockRendering.RenderInfo.prototype.makeSpacerRow_ = function(prev, next
/**
* Calculate the width of a spacer row.
* @param {!Blockly.blockRendering.Row} _prev The row before the spacer.
* @param {!Blockly.blockRendering.Row} _next The row after the spacer.
* @param {!Row} _prev The row before the spacer.
* @param {!Row} _next The row after the spacer.
* @return {number} The desired width of the spacer row between these two rows.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.getSpacerRowWidth_ = function(
_prev, _next) {
RenderInfo.prototype.getSpacerRowWidth_ = function(_prev, _next) {
return this.width - this.startX;
};
/**
* Calculate the height of a spacer row.
* @param {!Blockly.blockRendering.Row} _prev The row before the spacer.
* @param {!Blockly.blockRendering.Row} _next The row after the spacer.
* @param {!Row} _prev The row before the spacer.
* @param {!Row} _next The row after the spacer.
* @return {number} The desired height of the spacer row between these two rows.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.getSpacerRowHeight_ = function(
_prev, _next) {
RenderInfo.prototype.getSpacerRowHeight_ = function(_prev, _next) {
return this.constants_.MEDIUM_PADDING;
};
@@ -691,26 +681,25 @@ Blockly.blockRendering.RenderInfo.prototype.getSpacerRowHeight_ = function(
* This base implementation puts the centerline at the middle of the row
* vertically, with no special cases. You will likely need extra logic to
* handle (at minimum) top and bottom rows.
* @param {!Blockly.blockRendering.Row} row The row containing the element.
* @param {!Blockly.blockRendering.Measurable} elem The element to place.
* @param {!Row} row The row containing the element.
* @param {!Measurable} elem The element to place.
* @return {number} The desired centerline of the given element, as an offset
* from the top left of the block.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.getElemCenterline_ = function(row,
elem) {
if (Blockly.blockRendering.Types.isSpacer(elem)) {
RenderInfo.prototype.getElemCenterline_ = function(row, elem) {
if (Types.isSpacer(elem)) {
return row.yPos + elem.height / 2;
}
if (Blockly.blockRendering.Types.isBottomRow(row)) {
var baseline = row.yPos + row.height - row.descenderHeight;
if (Blockly.blockRendering.Types.isNextConnection(elem)) {
if (Types.isBottomRow(row)) {
const baseline = row.yPos + row.height - row.descenderHeight;
if (Types.isNextConnection(elem)) {
return baseline + elem.height / 2;
}
return baseline - elem.height / 2;
}
if (Blockly.blockRendering.Types.isTopRow(row)) {
if (Blockly.blockRendering.Types.isHat(elem)) {
if (Types.isTopRow(row)) {
if (Types.isHat(elem)) {
return row.capline - elem.height / 2;
}
return row.capline + elem.height / 2;
@@ -721,15 +710,14 @@ Blockly.blockRendering.RenderInfo.prototype.getElemCenterline_ = function(row,
/**
* Record final position information on elements on the given row, for use in
* drawing. At minimum this records xPos and centerline on each element.
* @param {!Blockly.blockRendering.Row} row The row containing the elements.
* @param {!Row} row The row containing the elements.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.recordElemPositions_ = function(
row) {
var xCursor = row.xPos;
for (var j = 0, elem; (elem = row.elements[j]); j++) {
RenderInfo.prototype.recordElemPositions_ = function(row) {
let xCursor = row.xPos;
for (let j = 0, elem; (elem = row.elements[j]); j++) {
// Now that row heights are finalized, make spacers use the row height.
if (Blockly.blockRendering.Types.isSpacer(elem)) {
if (Types.isSpacer(elem)) {
elem.height = row.height;
}
elem.xPos = xCursor;
@@ -743,13 +731,13 @@ Blockly.blockRendering.RenderInfo.prototype.recordElemPositions_ = function(
* store the y position of each row, and record the height of the full block.
* @protected
*/
Blockly.blockRendering.RenderInfo.prototype.finalize_ = function() {
RenderInfo.prototype.finalize_ = function() {
// Performance note: this could be combined with the draw pass, if the time
// that this takes is excessive. But it shouldn't be, because it only
// accesses and sets properties that already exist on the objects.
var widestRowWithConnectedBlocks = 0;
var yCursor = 0;
for (var i = 0, row; (row = this.rows[i]); i++) {
let widestRowWithConnectedBlocks = 0;
let yCursor = 0;
for (let i = 0, row; (row = this.rows[i]); i++) {
row.yPos = yCursor;
row.xPos = this.startX;
yCursor += row.height;
@@ -761,9 +749,9 @@ Blockly.blockRendering.RenderInfo.prototype.finalize_ = function() {
if (this.outputConnection && this.block_.nextConnection &&
this.block_.nextConnection.isConnected()) {
// Include width of connected block in value to stack width measurement.
widestRowWithConnectedBlocks =
Math.max(widestRowWithConnectedBlocks,
this.block_.nextConnection.targetBlock().getHeightWidth().width);
widestRowWithConnectedBlocks = Math.max(
widestRowWithConnectedBlocks,
this.block_.nextConnection.targetBlock().getHeightWidth().width);
}
this.widthWithChildren = widestRowWithConnectedBlocks + this.startX;
@@ -772,3 +760,5 @@ Blockly.blockRendering.RenderInfo.prototype.finalize_ = function() {
this.startY = this.topRow.capline;
this.bottomRow.baseline = yCursor - this.bottomRow.descenderHeight;
};
exports = RenderInfo;

View File

@@ -15,8 +15,6 @@ goog.provide('Blockly.blockRendering.MarkerSvg');
goog.require('Blockly.ASTNode');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.Events');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.MarkerMove');

View File

@@ -20,8 +20,6 @@ goog.require('Blockly.blockRendering.MarkerSvg');
goog.require('Blockly.blockRendering.PathObject');
goog.require('Blockly.blockRendering.RenderInfo');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.InsertionMarkerManager');
goog.require('Blockly.IRegistrable');

View File

@@ -19,8 +19,6 @@ goog.require('Blockly.blockRendering.InputRow');
goog.require('Blockly.blockRendering.InRowSpacer');
goog.require('Blockly.blockRendering.RenderInfo');
goog.require('Blockly.blockRendering.Types');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.geras.InlineInput');
goog.require('Blockly.geras.StatementInput');
goog.require('Blockly.inputTypes');

View File

@@ -15,8 +15,6 @@ goog.provide('Blockly.zelos.ConstantProvider');
goog.require('Blockly.blockRendering.ConstantProvider');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.utils.colour');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.object');

View File

@@ -18,8 +18,6 @@ goog.require('Blockly.blockRendering.InRowSpacer');
goog.require('Blockly.blockRendering.Measurable');
goog.require('Blockly.blockRendering.RenderInfo');
goog.require('Blockly.blockRendering.Types');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.FieldImage');
goog.require('Blockly.FieldLabel');
goog.require('Blockly.FieldTextInput');

View File

@@ -15,8 +15,6 @@ goog.provide('Blockly.zelos.Renderer');
goog.require('Blockly.blockRendering');
goog.require('Blockly.blockRendering.Renderer');
goog.require('Blockly.connectionTypes');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.InsertionMarkerManager');
goog.require('Blockly.utils.object');
goog.require('Blockly.zelos.ConstantProvider');

View File

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

View File

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

View File

@@ -16,8 +16,7 @@
*/
goog.provide('Blockly.Touch');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.internalConstants');
goog.require('Blockly.utils');
goog.require('Blockly.utils.global');
goog.require('Blockly.utils.string');
@@ -107,8 +106,7 @@ Blockly.longStart = function(e, gesture) {
if (gesture) {
gesture.handleRightClick(e);
}
}, Blockly.LONGPRESS);
}, Blockly.internalConstants.LONGPRESS);
};
/**

View File

@@ -14,13 +14,12 @@ goog.provide('Blockly.Trashcan');
goog.require('Blockly.browserEvents');
goog.require('Blockly.ComponentManager');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.DeleteArea');
goog.require('Blockly.Events');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.TrashcanOpen');
goog.require('Blockly.IAutoHideable');
goog.require('Blockly.internalConstants');
goog.require('Blockly.IPositionable');
goog.require('Blockly.Options');
goog.require('Blockly.registry');
@@ -316,17 +315,18 @@ Blockly.Trashcan.prototype.createDom = function() {
},
clip);
var body = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.IMAGE,
{
'width': Blockly.SPRITE.width,
Blockly.utils.Svg.IMAGE, {
'width': Blockly.internalConstants.SPRITE.width,
'x': -this.SPRITE_LEFT_,
'height': Blockly.SPRITE.height,
'height': Blockly.internalConstants.SPRITE.height,
'y': -this.SPRITE_TOP_,
'clip-path': 'url(#blocklyTrashBodyClipPath' + rnd + ')'
},
this.svgGroup_);
body.setAttributeNS(Blockly.utils.dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia + Blockly.SPRITE.url);
body.setAttributeNS(
Blockly.utils.dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia +
Blockly.internalConstants.SPRITE.url);
clip = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.CLIPPATH,
@@ -336,17 +336,18 @@ Blockly.Trashcan.prototype.createDom = function() {
Blockly.utils.Svg.RECT,
{'width': this.WIDTH_, 'height': this.LID_HEIGHT_}, clip);
this.svgLid_ = Blockly.utils.dom.createSvgElement(
Blockly.utils.Svg.IMAGE,
{
'width': Blockly.SPRITE.width,
Blockly.utils.Svg.IMAGE, {
'width': Blockly.internalConstants.SPRITE.width,
'x': -this.SPRITE_LEFT_,
'height': Blockly.SPRITE.height,
'height': Blockly.internalConstants.SPRITE.height,
'y': -this.SPRITE_TOP_,
'clip-path': 'url(#blocklyTrashLidClipPath' + rnd + ')'
},
this.svgGroup_);
this.svgLid_.setAttributeNS(Blockly.utils.dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia + Blockly.SPRITE.url);
this.svgLid_.setAttributeNS(
Blockly.utils.dom.XLINK_NS, 'xlink:href',
this.workspace_.options.pathToMedia +
Blockly.internalConstants.SPRITE.url);
// bindEventWithChecks_ quashes events too aggressively. See:
// https://groups.google.com/forum/#!topic/blockly/QF4yB9Wx00s

View File

@@ -18,8 +18,7 @@
*/
goog.provide('Blockly.utils');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.internalConstants');
goog.require('Blockly.Msg');
goog.require('Blockly.utils.colour');
goog.require('Blockly.utils.Coordinate');
@@ -194,13 +193,13 @@ Blockly.utils.getScrollDeltaPixels = function(e) {
};
case 0x01: // Line mode.
return {
x: e.deltaX * Blockly.LINE_MODE_MULTIPLIER,
y: e.deltaY * Blockly.LINE_MODE_MULTIPLIER
x: e.deltaX * Blockly.internalConstants.LINE_MODE_MULTIPLIER,
y: e.deltaY * Blockly.internalConstants.LINE_MODE_MULTIPLIER
};
case 0x02: // Page mode.
return {
x: e.deltaX * Blockly.PAGE_MODE_MULTIPLIER,
y: e.deltaY * Blockly.PAGE_MODE_MULTIPLIER
x: e.deltaX * Blockly.internalConstants.PAGE_MODE_MULTIPLIER,
y: e.deltaY * Blockly.internalConstants.PAGE_MODE_MULTIPLIER
};
}
};
@@ -634,8 +633,9 @@ Blockly.utils.parseBlockColour = function(colour) {
if (!isNaN(hue) && 0 <= hue && hue <= 360) {
return {
hue: hue,
hex: Blockly.utils.colour.hsvToHex(hue, Blockly.HSV_SATURATION,
Blockly.HSV_VALUE * 255)
hex: Blockly.utils.colour.hsvToHex(
hue, Blockly.internalConstants.HSV_SATURATION,
Blockly.internalConstants.HSV_VALUE * 255)
};
} else {
var hex = Blockly.utils.colour.parse(dereferenced);

View File

@@ -16,7 +16,8 @@
* @name Blockly.utils.colour
* @namespace
*/
goog.provide('Blockly.utils.colour');
goog.module('Blockly.utils.colour');
goog.module.declareLegacyNamespace();
/**
@@ -30,9 +31,9 @@ goog.provide('Blockly.utils.colour');
* @return {?string} A string containing a hex representation of the colour,
* or null if can't be parsed.
*/
Blockly.utils.colour.parse = function(str) {
const parse = function(str) {
str = String(str).toLowerCase().trim();
var hex = Blockly.utils.colour.names[str];
let hex = names[str];
if (hex) {
// e.g. 'red'
return hex;
@@ -47,18 +48,19 @@ Blockly.utils.colour.parse = function(str) {
// e.g. '#0f8'
return ['#', hex[1], hex[1], hex[2], hex[2], hex[3], hex[3]].join('');
}
var rgb = str.match(/^(?:rgb)?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
const rgb = str.match(/^(?:rgb)?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
if (rgb) {
// e.g. 'rgb(0, 128, 255)'
var r = Number(rgb[1]);
var g = Number(rgb[2]);
var b = Number(rgb[3]);
const r = Number(rgb[1]);
const g = Number(rgb[2]);
const b = Number(rgb[3]);
if (r >= 0 && r < 256 && g >= 0 && g < 256 && b >= 0 && b < 256) {
return Blockly.utils.colour.rgbToHex(r, g, b);
return rgbToHex(r, g, b);
}
}
return null;
};
exports.parse = parse;
/**
* Converts a colour from RGB to hex representation.
@@ -67,13 +69,14 @@ Blockly.utils.colour.parse = function(str) {
* @param {number} b Amount of blue, int between 0 and 255.
* @return {string} Hex representation of the colour.
*/
Blockly.utils.colour.rgbToHex = function(r, g, b) {
var rgb = (r << 16) | (g << 8) | b;
const rgbToHex = function(r, g, b) {
const rgb = (r << 16) | (g << 8) | b;
if (r < 0x10) {
return '#' + (0x1000000 | rgb).toString(16).substr(1);
}
return '#' + rgb.toString(16);
};
exports.rgbToHex = rgbToHex;
/**
* Converts a colour to RGB.
@@ -81,19 +84,20 @@ Blockly.utils.colour.rgbToHex = function(r, g, b) {
* colour format ('#ff0000', 'red', '0xff000', etc).
* @return {!Array<number>} RGB representation of the colour.
*/
Blockly.utils.colour.hexToRgb = function(colour) {
var hex = Blockly.utils.colour.parse(colour);
const hexToRgb = function(colour) {
const hex = parse(colour);
if (!hex) {
return [0, 0, 0];
}
var rgb = parseInt(hex.substr(1), 16);
var r = rgb >> 16;
var g = (rgb >> 8) & 255;
var b = rgb & 255;
const rgb = parseInt(hex.substr(1), 16);
const r = rgb >> 16;
const g = (rgb >> 8) & 255;
const b = rgb & 255;
return [r, g, b];
};
exports.hexToRgb = hexToRgb;
/**
* Converts an HSV triplet to hex representation.
@@ -102,20 +106,20 @@ Blockly.utils.colour.hexToRgb = function(colour) {
* @param {number} v Brightness in [0, 255].
* @return {string} Hex representation of the colour.
*/
Blockly.utils.colour.hsvToHex = function(h, s, v) {
var red = 0;
var green = 0;
var blue = 0;
const hsvToHex = function(h, s, v) {
let red = 0;
let green = 0;
let blue = 0;
if (s == 0) {
red = v;
green = v;
blue = v;
} else {
var sextant = Math.floor(h / 60);
var remainder = (h / 60) - sextant;
var val1 = v * (1 - s);
var val2 = v * (1 - (s * remainder));
var val3 = v * (1 - (s * (1 - remainder)));
const sextant = Math.floor(h / 60);
const remainder = (h / 60) - sextant;
const val1 = v * (1 - s);
const val2 = v * (1 - (s * remainder));
const val3 = v * (1 - (s * (1 - remainder)));
switch (sextant) {
case 1:
red = val2;
@@ -150,9 +154,9 @@ Blockly.utils.colour.hsvToHex = function(h, s, v) {
break;
}
}
return Blockly.utils.colour.rgbToHex(
Math.floor(red), Math.floor(green), Math.floor(blue));
return rgbToHex(Math.floor(red), Math.floor(green), Math.floor(blue));
};
exports.hsvToHex = hsvToHex;
/**
* Blend two colours together, using the specified factor to indicate the
@@ -163,22 +167,23 @@ Blockly.utils.colour.hsvToHex = function(h, s, v) {
* Values should be in the range [0, 1].
* @return {?string} Combined colour represented in hex.
*/
Blockly.utils.colour.blend = function(colour1, colour2, factor) {
var hex1 = Blockly.utils.colour.parse(colour1);
const blend = function(colour1, colour2, factor) {
const hex1 = parse(colour1);
if (!hex1) {
return null;
}
var hex2 = Blockly.utils.colour.parse(colour2);
const hex2 = parse(colour2);
if (!hex2) {
return null;
}
var rgb1 = Blockly.utils.colour.hexToRgb(hex1);
var rgb2 = Blockly.utils.colour.hexToRgb(hex2);
var r = Math.round(rgb2[0] + factor * (rgb1[0] - rgb2[0]));
var g = Math.round(rgb2[1] + factor * (rgb1[1] - rgb2[1]));
var b = Math.round(rgb2[2] + factor * (rgb1[2] - rgb2[2]));
return Blockly.utils.colour.rgbToHex(r, g, b);
const rgb1 = hexToRgb(hex1);
const rgb2 = hexToRgb(hex2);
const r = Math.round(rgb2[0] + factor * (rgb1[0] - rgb2[0]));
const g = Math.round(rgb2[1] + factor * (rgb1[1] - rgb2[1]));
const b = Math.round(rgb2[2] + factor * (rgb1[2] - rgb2[2]));
return rgbToHex(r, g, b);
};
exports.blend = blend;
/**
* A map that contains the 16 basic colour keywords as defined by W3C:
@@ -188,7 +193,7 @@ Blockly.utils.colour.blend = function(colour1, colour2, factor) {
*
* @type {!Object<string, string>}
*/
Blockly.utils.colour.names = {
const names = {
'aqua': '#00ffff',
'black': '#000000',
'blue': '#0000ff',
@@ -206,3 +211,4 @@ Blockly.utils.colour.names = {
'white': '#ffffff',
'yellow': '#ffff00'
};
exports.names = names;

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