2013-10-30 14:46:03 -07:00
|
|
|
/**
|
2014-01-28 03:00:09 -08:00
|
|
|
* @license
|
2013-10-30 14:46:03 -07:00
|
|
|
* Visual Blocks Editor
|
|
|
|
*
|
|
|
|
* Copyright 2012 Google Inc.
|
2014-10-07 13:09:55 -07:00
|
|
|
* https://developers.google.com/blockly/
|
2013-10-30 14:46:03 -07:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @fileoverview Methods for graphically rendering a block as SVG.
|
|
|
|
* @author fraser@google.com (Neil Fraser)
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
2016-02-18 10:43:28 -05:00
|
|
|
goog.provide('Blockly.BlockSvg');
|
2013-10-30 14:46:03 -07:00
|
|
|
|
2014-12-23 11:22:02 -08:00
|
|
|
goog.require('Blockly.Block');
|
|
|
|
goog.require('Blockly.ContextMenu');
|
2015-06-17 13:05:24 -07:00
|
|
|
goog.require('goog.Timer');
|
2014-12-23 11:22:02 -08:00
|
|
|
goog.require('goog.asserts');
|
2015-02-06 15:27:25 -08:00
|
|
|
goog.require('goog.dom');
|
|
|
|
goog.require('goog.math.Coordinate');
|
2016-01-04 12:05:41 -08:00
|
|
|
goog.require('goog.userAgent');
|
2013-10-30 14:46:03 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class for a block's SVG representation.
|
2015-12-07 16:40:45 +01:00
|
|
|
* Not normally called directly, workspace.newBlock() is preferred.
|
|
|
|
* @param {!Blockly.Workspace} workspace The block's workspace.
|
|
|
|
* @param {?string} prototypeName Name of the language object containing
|
|
|
|
* type-specific functions for this block.
|
2015-12-09 10:02:42 +01:00
|
|
|
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
|
|
|
|
* create a new id.
|
2014-12-23 11:22:02 -08:00
|
|
|
* @extends {Blockly.Block}
|
2013-10-30 14:46:03 -07:00
|
|
|
* @constructor
|
|
|
|
*/
|
2015-12-09 10:02:42 +01:00
|
|
|
Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
|
2013-10-30 14:46:03 -07:00
|
|
|
// Create core elements for the block.
|
2015-12-01 14:04:54 -06:00
|
|
|
/** @type {SVGElement} */
|
2013-10-30 14:46:03 -07:00
|
|
|
this.svgGroup_ = Blockly.createSvgElement('g', {}, null);
|
2015-12-01 14:04:54 -06:00
|
|
|
/** @type {SVGElement} */
|
2013-10-30 14:46:03 -07:00
|
|
|
this.svgPath_ = Blockly.createSvgElement('path', {'class': 'blocklyPath'},
|
|
|
|
this.svgGroup_);
|
2014-12-23 11:22:02 -08:00
|
|
|
this.svgPath_.tooltip = this;
|
2016-03-29 17:30:39 -07:00
|
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
|
this.rendered = false;
|
|
|
|
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.Tooltip.bindMouseEvents(this.svgPath_);
|
2015-12-17 14:16:04 -08:00
|
|
|
Blockly.BlockSvg.superClass_.constructor.call(this,
|
|
|
|
workspace, prototypeName, opt_id);
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
2014-12-23 11:22:02 -08:00
|
|
|
goog.inherits(Blockly.BlockSvg, Blockly.Block);
|
2013-10-30 14:46:03 -07:00
|
|
|
|
2014-09-08 14:26:52 -07:00
|
|
|
/**
|
|
|
|
* Height of this block, not including any statement blocks above or below.
|
2016-04-14 18:04:17 -04:00
|
|
|
* @type {number}
|
2014-09-08 14:26:52 -07:00
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.height = 0;
|
2016-04-14 18:04:17 -04:00
|
|
|
|
2014-09-08 14:26:52 -07:00
|
|
|
/**
|
|
|
|
* Width of this block, including any connected value blocks.
|
2016-04-14 18:04:17 -04:00
|
|
|
* @type {number}
|
2014-09-08 14:26:52 -07:00
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.width = 0;
|
|
|
|
|
2016-04-14 18:04:17 -04:00
|
|
|
/**
|
|
|
|
* Opacity of this block between 0 and 1.
|
|
|
|
* @type {number}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.opacity_ = 1;
|
|
|
|
|
2015-08-19 17:21:05 -07:00
|
|
|
/**
|
|
|
|
* Original location of block being dragged.
|
|
|
|
* @type {goog.math.Coordinate}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.dragStartXY_ = null;
|
|
|
|
|
2016-03-04 10:21:10 -05:00
|
|
|
/**
|
|
|
|
* Whether the block glows as if running.
|
|
|
|
* @type {boolean}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.isGlowing_ = false;
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Constant for identifying rows that are to be rendered inline.
|
|
|
|
* Don't collide with Blockly.INPUT_VALUE and friends.
|
|
|
|
* @const
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.INLINE = -1;
|
|
|
|
|
|
|
|
/**
|
2014-12-23 11:22:02 -08:00
|
|
|
* Create and initialize the SVG representation of the block.
|
2015-01-05 12:04:01 -08:00
|
|
|
* May be called more than once.
|
2013-10-30 14:46:03 -07:00
|
|
|
*/
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.BlockSvg.prototype.initSvg = function() {
|
|
|
|
goog.asserts.assert(this.workspace.rendered, 'Workspace is headless.');
|
2016-04-13 17:57:12 -04:00
|
|
|
if (!this.isInsertionMarker()) { // Insertion markers not allowed to have inputs or icons
|
|
|
|
for (var i = 0, input; input = this.inputList[i]; i++) {
|
|
|
|
input.init();
|
|
|
|
}
|
|
|
|
var icons = this.getIcons();
|
|
|
|
for (i = 0; i < icons.length; i++) {
|
|
|
|
icons[i].createIcon();
|
|
|
|
}
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
2014-12-24 13:48:45 -08:00
|
|
|
this.updateColour();
|
2015-04-28 13:51:25 -07:00
|
|
|
this.updateMovable();
|
|
|
|
if (!this.workspace.options.readOnly && !this.eventsInit_) {
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.bindEvent_(this.getSvgRoot(), 'mousedown', this,
|
|
|
|
this.onMouseDown_);
|
2015-03-17 15:37:33 -07:00
|
|
|
var thisBlock = this;
|
|
|
|
Blockly.bindEvent_(this.getSvgRoot(), 'touchstart', null,
|
|
|
|
function(e) {Blockly.longStart_(e, thisBlock);});
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2015-01-05 11:08:41 -08:00
|
|
|
this.eventsInit_ = true;
|
2015-01-05 12:04:01 -08:00
|
|
|
|
|
|
|
if (!this.getSvgRoot().parentNode) {
|
|
|
|
this.workspace.getCanvas().appendChild(this.getSvgRoot());
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Select this block. Highlight it visually.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.select = function() {
|
2016-03-29 08:10:44 -07:00
|
|
|
if (Blockly.selected == this) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var oldId = null;
|
2014-12-23 11:22:02 -08:00
|
|
|
if (Blockly.selected) {
|
2016-03-29 08:10:44 -07:00
|
|
|
oldId = Blockly.selected.id;
|
2014-12-23 11:22:02 -08:00
|
|
|
// Unselect any previously selected block.
|
2016-03-29 08:10:44 -07:00
|
|
|
Blockly.Events.disable();
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.selected.unselect();
|
2016-03-29 08:10:44 -07:00
|
|
|
Blockly.Events.enable();
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2016-03-29 08:10:44 -07:00
|
|
|
var event = new Blockly.Events.Ui(null, 'selected', oldId, this.id);
|
|
|
|
event.workspaceId = this.workspace.id;
|
|
|
|
Blockly.Events.fire(event);
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.selected = this;
|
|
|
|
this.addSelect();
|
|
|
|
Blockly.fireUiEvent(this.workspace.getCanvas(), 'blocklySelectChange');
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unselect this block. Remove its highlighting.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.unselect = function() {
|
2016-03-28 15:40:37 -07:00
|
|
|
if (Blockly.selected != this) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-29 08:10:44 -07:00
|
|
|
var event = new Blockly.Events.Ui(null, 'selected', this.id, null);
|
|
|
|
event.workspaceId = this.workspace.id;
|
|
|
|
Blockly.Events.fire(event);
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.selected = null;
|
|
|
|
this.removeSelect();
|
|
|
|
Blockly.fireUiEvent(this.workspace.getCanvas(), 'blocklySelectChange');
|
|
|
|
};
|
|
|
|
|
2016-03-04 10:21:10 -05:00
|
|
|
/**
|
|
|
|
* Glow this block. Highlight it visually as if it's running.
|
|
|
|
* @param {boolean} isGlowing Whether the block should glow.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setGlow = function(isGlowing) {
|
|
|
|
this.isGlowing_ = isGlowing;
|
|
|
|
this.updateColour();
|
|
|
|
};
|
|
|
|
|
2014-12-23 11:22:02 -08:00
|
|
|
/**
|
|
|
|
* Block's mutator icon (if any).
|
|
|
|
* @type {Blockly.Mutator}
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.mutator = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Block's comment icon (if any).
|
|
|
|
* @type {Blockly.Comment}
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.comment = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Block's warning icon (if any).
|
|
|
|
* @type {Blockly.Warning}
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.warning = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of mutator, comment, and warning icons.
|
|
|
|
* @return {!Array} List of icons.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.getIcons = function() {
|
|
|
|
var icons = [];
|
|
|
|
if (this.mutator) {
|
|
|
|
icons.push(this.mutator);
|
|
|
|
}
|
|
|
|
if (this.comment) {
|
|
|
|
icons.push(this.comment);
|
|
|
|
}
|
|
|
|
if (this.warning) {
|
|
|
|
icons.push(this.warning);
|
|
|
|
}
|
|
|
|
return icons;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper function called when a mouseUp occurs during a drag operation.
|
|
|
|
* @type {Array.<!Array>}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.onMouseUpWrapper_ = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper function called when a mouseMove occurs during a drag operation.
|
|
|
|
* @type {Array.<!Array>}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.onMouseMoveWrapper_ = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop binding to the global mouseup and mousemove events.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.terminateDrag_ = function() {
|
|
|
|
if (Blockly.BlockSvg.onMouseUpWrapper_) {
|
|
|
|
Blockly.unbindEvent_(Blockly.BlockSvg.onMouseUpWrapper_);
|
|
|
|
Blockly.BlockSvg.onMouseUpWrapper_ = null;
|
|
|
|
}
|
|
|
|
if (Blockly.BlockSvg.onMouseMoveWrapper_) {
|
|
|
|
Blockly.unbindEvent_(Blockly.BlockSvg.onMouseMoveWrapper_);
|
|
|
|
Blockly.BlockSvg.onMouseMoveWrapper_ = null;
|
|
|
|
}
|
|
|
|
var selected = Blockly.selected;
|
2016-03-29 08:36:11 -07:00
|
|
|
if (Blockly.dragMode_ == Blockly.DRAG_FREE) {
|
2014-12-23 11:22:02 -08:00
|
|
|
// Terminate a drag operation.
|
|
|
|
if (selected) {
|
2016-04-06 14:57:16 -07:00
|
|
|
if (Blockly.insertionMarker_) {
|
2016-03-15 15:35:19 -07:00
|
|
|
Blockly.Events.disable();
|
2016-04-06 14:42:12 -07:00
|
|
|
if (Blockly.insertionMarkerConnection_) {
|
2016-04-06 14:57:16 -07:00
|
|
|
Blockly.BlockSvg.disconnectInsertionMarker();
|
2016-03-21 16:57:39 -04:00
|
|
|
}
|
2016-04-06 14:57:16 -07:00
|
|
|
Blockly.insertionMarker_.dispose();
|
|
|
|
Blockly.insertionMarker_ = null;
|
2016-03-15 15:35:19 -07:00
|
|
|
Blockly.Events.enable();
|
2016-02-23 14:46:04 -08:00
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
// Update the connection locations.
|
|
|
|
var xy = selected.getRelativeToSurfaceXY();
|
2015-08-19 17:21:05 -07:00
|
|
|
var dxy = goog.math.Coordinate.difference(xy, selected.dragStartXY_);
|
2016-02-02 00:28:49 -08:00
|
|
|
var event = new Blockly.Events.Move(selected);
|
|
|
|
event.oldCoordinate = selected.dragStartXY_;
|
|
|
|
event.recordNew();
|
|
|
|
Blockly.Events.fire(event);
|
2015-08-19 17:21:05 -07:00
|
|
|
selected.moveConnections_(dxy.x, dxy.y);
|
2014-12-23 11:22:02 -08:00
|
|
|
delete selected.draggedBubbles_;
|
|
|
|
selected.setDragging_(false);
|
2016-04-07 22:58:05 -04:00
|
|
|
selected.moveOffDragSurface_();
|
2014-12-23 11:22:02 -08:00
|
|
|
selected.render();
|
2016-03-03 17:48:54 -08:00
|
|
|
// Ensure that any stap and bump are part of this move's event group.
|
|
|
|
var group = Blockly.Events.getGroup();
|
|
|
|
setTimeout(function() {
|
|
|
|
Blockly.Events.setGroup(group);
|
|
|
|
selected.snapToGrid();
|
|
|
|
Blockly.Events.setGroup(false);
|
2016-03-06 18:32:20 -08:00
|
|
|
}, Blockly.BUMP_DELAY / 2);
|
2016-03-03 17:48:54 -08:00
|
|
|
setTimeout(function() {
|
|
|
|
Blockly.Events.setGroup(group);
|
|
|
|
selected.bumpNeighbours_();
|
|
|
|
Blockly.Events.setGroup(false);
|
2016-03-06 18:32:20 -08:00
|
|
|
}, Blockly.BUMP_DELAY);
|
2014-12-23 11:22:02 -08:00
|
|
|
// Fire an event to allow scrollbars to resize.
|
|
|
|
Blockly.fireUiEvent(window, 'resize');
|
|
|
|
}
|
|
|
|
}
|
2016-03-29 08:36:11 -07:00
|
|
|
Blockly.dragMode_ = Blockly.DRAG_NONE;
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set parent of this block to be a new block or null.
|
|
|
|
* @param {Blockly.BlockSvg} newParent New parent block.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setParent = function(newParent) {
|
2016-03-03 17:48:54 -08:00
|
|
|
if (newParent == this.parentBlock_) {
|
|
|
|
return;
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
var svgRoot = this.getSvgRoot();
|
|
|
|
if (this.parentBlock_ && svgRoot) {
|
|
|
|
// Move this block up the DOM. Keep track of x/y translations.
|
|
|
|
var xy = this.getRelativeToSurfaceXY();
|
2016-04-07 22:58:05 -04:00
|
|
|
// Avoid moving a block up the DOM if it's currently selected/dragging,
|
|
|
|
// so as to avoid taking things off the drag surface.
|
|
|
|
if (Blockly.selected != this) {
|
2016-04-01 17:05:29 -04:00
|
|
|
this.workspace.getCanvas().appendChild(svgRoot);
|
2016-04-01 17:52:17 -04:00
|
|
|
this.translate(xy.x, xy.y);
|
2016-04-01 17:05:29 -04:00
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
|
2015-09-15 16:20:13 -07:00
|
|
|
Blockly.Field.startCache();
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.BlockSvg.superClass_.setParent.call(this, newParent);
|
2015-09-15 16:20:13 -07:00
|
|
|
Blockly.Field.stopCache();
|
2014-12-23 11:22:02 -08:00
|
|
|
|
|
|
|
if (newParent) {
|
|
|
|
var oldXY = this.getRelativeToSurfaceXY();
|
|
|
|
newParent.getSvgRoot().appendChild(svgRoot);
|
|
|
|
var newXY = this.getRelativeToSurfaceXY();
|
|
|
|
// Move the connections to match the child's new position.
|
|
|
|
this.moveConnections_(newXY.x - oldXY.x, newXY.y - oldXY.y);
|
2016-04-05 19:37:32 -04:00
|
|
|
// If we are a shadow block, inherit tertiary colour.
|
|
|
|
if (this.isShadow()) {
|
|
|
|
this.setColour(this.getColour(), this.getColourSecondary(),
|
|
|
|
newParent.getColourTertiary());
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the coordinates of the top-left corner of this block relative to the
|
|
|
|
* drawing surface's origin (0,0).
|
|
|
|
* @return {!goog.math.Coordinate} Object with .x and .y properties.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.getRelativeToSurfaceXY = function() {
|
2016-04-05 00:10:44 -04:00
|
|
|
// The drawing surface is relative to either the workspace canvas
|
|
|
|
// or to the drag surface group.
|
2014-12-23 11:22:02 -08:00
|
|
|
var x = 0;
|
|
|
|
var y = 0;
|
2016-04-01 17:05:29 -04:00
|
|
|
var dragSurfaceGroup = (this.workspace.dragSurface) ?
|
|
|
|
this.workspace.dragSurface.getGroup() : null;
|
2014-12-23 11:22:02 -08:00
|
|
|
var element = this.getSvgRoot();
|
|
|
|
if (element) {
|
|
|
|
do {
|
|
|
|
// Loop through this block and every parent.
|
|
|
|
var xy = Blockly.getRelativeXY_(element);
|
|
|
|
x += xy.x;
|
|
|
|
y += xy.y;
|
2016-04-13 16:14:19 -04:00
|
|
|
// If this element is the current element on the drag surface, include
|
|
|
|
// the translation of the drag surface itself.
|
|
|
|
if (this.workspace.dragSurface &&
|
|
|
|
this.workspace.dragSurface.getCurrentBlock() == element) {
|
|
|
|
var surfaceTranslation = this.workspace.dragSurface.getSurfaceTranslation();
|
|
|
|
x += surfaceTranslation.x;
|
|
|
|
y += surfaceTranslation.y;
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
element = element.parentNode;
|
2016-04-01 17:05:29 -04:00
|
|
|
} while (element && element != this.workspace.getCanvas() &&
|
|
|
|
element != dragSurfaceGroup);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
return new goog.math.Coordinate(x, y);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move a block by a relative offset.
|
|
|
|
* @param {number} dx Horizontal offset.
|
|
|
|
* @param {number} dy Vertical offset.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.moveBy = function(dx, dy) {
|
2016-03-01 18:21:02 -08:00
|
|
|
goog.asserts.assert(!this.parentBlock_, 'Block has parent.');
|
2016-02-02 00:28:49 -08:00
|
|
|
var event = new Blockly.Events.Move(this);
|
2014-12-23 11:22:02 -08:00
|
|
|
var xy = this.getRelativeToSurfaceXY();
|
2016-04-01 17:52:17 -04:00
|
|
|
this.translate(xy.x + dx, xy.y + dy);
|
2014-12-23 11:22:02 -08:00
|
|
|
this.moveConnections_(dx, dy);
|
2016-02-02 00:28:49 -08:00
|
|
|
event.recordNew();
|
|
|
|
Blockly.Events.fire(event);
|
2016-04-15 14:09:15 -04:00
|
|
|
Blockly.WidgetDiv.hide(true);
|
2014-12-23 11:22:02 -08:00
|
|
|
};
|
|
|
|
|
2016-04-01 17:52:17 -04:00
|
|
|
/**
|
2016-04-05 00:10:44 -04:00
|
|
|
* Set this block to an absolute translation.
|
|
|
|
* @param {number} x Horizontal translation.
|
|
|
|
* @param {number} y Vertical translation.
|
2016-04-11 14:19:56 -04:00
|
|
|
* @param {boolean=} opt_use3d If set, use 3d translation.
|
2016-04-01 17:52:17 -04:00
|
|
|
*/
|
2016-04-11 14:19:56 -04:00
|
|
|
Blockly.BlockSvg.prototype.translate = function(x, y, opt_use3d) {
|
|
|
|
if (opt_use3d) {
|
|
|
|
this.getSvgRoot().setAttribute('style', 'transform: translate3d(' + x + 'px,' + y + 'px, 0px)');
|
|
|
|
} else {
|
|
|
|
this.getSvgRoot().setAttribute('transform', 'translate(' + x + ',' + y + ')');
|
|
|
|
}
|
2016-04-05 00:10:44 -04:00
|
|
|
};
|
2016-04-01 17:52:17 -04:00
|
|
|
|
2015-03-07 19:44:58 -06:00
|
|
|
/**
|
|
|
|
* Snap this block to the nearest grid point.
|
|
|
|
*/
|
2015-10-09 19:22:22 -07:00
|
|
|
Blockly.BlockSvg.prototype.snapToGrid = function() {
|
2015-03-08 17:39:30 -07:00
|
|
|
if (!this.workspace) {
|
|
|
|
return; // Deleted block.
|
|
|
|
}
|
2016-03-29 08:36:11 -07:00
|
|
|
if (Blockly.dragMode_ != Blockly.DRAG_NONE) {
|
2015-03-08 17:39:30 -07:00
|
|
|
return; // Don't bump blocks during a drag.
|
|
|
|
}
|
2015-03-07 19:44:58 -06:00
|
|
|
if (this.getParent()) {
|
|
|
|
return; // Only snap top-level blocks.
|
|
|
|
}
|
2015-03-08 17:39:30 -07:00
|
|
|
if (this.isInFlyout) {
|
|
|
|
return; // Don't move blocks around in a flyout.
|
|
|
|
}
|
2015-10-09 19:22:22 -07:00
|
|
|
if (!this.workspace.options.gridOptions ||
|
|
|
|
!this.workspace.options.gridOptions['snap']) {
|
|
|
|
return; // Config says no snapping.
|
|
|
|
}
|
2015-04-28 13:51:25 -07:00
|
|
|
var spacing = this.workspace.options.gridOptions['spacing'];
|
2015-03-07 19:44:58 -06:00
|
|
|
var half = spacing / 2;
|
|
|
|
var xy = this.getRelativeToSurfaceXY();
|
|
|
|
var dx = Math.round((xy.x - half) / spacing) * spacing + half - xy.x;
|
|
|
|
var dy = Math.round((xy.y - half) / spacing) * spacing + half - xy.y;
|
2015-03-10 22:36:48 -07:00
|
|
|
dx = Math.round(dx);
|
|
|
|
dy = Math.round(dy);
|
2015-03-07 19:44:58 -06:00
|
|
|
if (dx != 0 || dy != 0) {
|
|
|
|
this.moveBy(dx, dy);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-12-23 11:22:02 -08:00
|
|
|
/**
|
|
|
|
* Returns a bounding box describing the dimensions of this block
|
|
|
|
* and any blocks stacked below it.
|
2016-04-07 13:22:07 -07:00
|
|
|
* @return {!{height: number, width: number}} Object with height and width
|
|
|
|
* properties.
|
2014-12-23 11:22:02 -08:00
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.getHeightWidth = function() {
|
|
|
|
var height = this.height;
|
|
|
|
var width = this.width;
|
|
|
|
// Recursively add size of subsequent blocks.
|
|
|
|
var nextBlock = this.getNextBlock();
|
|
|
|
if (nextBlock) {
|
|
|
|
var nextHeightWidth = nextBlock.getHeightWidth();
|
|
|
|
height += nextHeightWidth.height - 4; // Height of tab.
|
|
|
|
width = Math.max(width, nextHeightWidth.width);
|
2015-07-19 16:49:11 -07:00
|
|
|
} else if (!this.nextConnection && !this.outputConnection) {
|
|
|
|
// Add a bit of margin under blocks with no bottom tab.
|
|
|
|
height += 2;
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
return {height: height, width: width};
|
|
|
|
};
|
|
|
|
|
2016-02-29 12:52:38 -08:00
|
|
|
/**
|
2016-04-07 13:22:07 -07:00
|
|
|
* Returns the coordinates of a bounding box describing the dimensions of this
|
|
|
|
* block and any blocks stacked below it.
|
2016-02-29 12:52:38 -08:00
|
|
|
* @return {!{topLeft: goog.math.Coordinate, bottomRight: goog.math.Coordinate}}
|
2016-04-07 13:22:07 -07:00
|
|
|
* Object with top left and bottom right coordinates of the bounding box.
|
2016-02-29 12:52:38 -08:00
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.getBoundingRectangle = function() {
|
|
|
|
var blockXY = this.getRelativeToSurfaceXY(this);
|
|
|
|
var tab = this.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0;
|
|
|
|
var blockBounds = this.getHeightWidth();
|
|
|
|
var topLeft;
|
|
|
|
var bottomRight;
|
|
|
|
if (this.RTL) {
|
|
|
|
// Width has the tab built into it already so subtract it here.
|
2016-04-07 13:22:07 -07:00
|
|
|
topLeft = new goog.math.Coordinate(blockXY.x - (blockBounds.width - tab),
|
|
|
|
blockXY.y);
|
2016-02-29 12:52:38 -08:00
|
|
|
// Add the width of the tab/puzzle piece knob to the x coordinate
|
|
|
|
// since X is the corner of the rectangle, not the whole puzzle piece.
|
2016-04-07 13:22:07 -07:00
|
|
|
bottomRight = new goog.math.Coordinate(blockXY.x + tab,
|
|
|
|
blockXY.y + blockBounds.height);
|
2016-02-29 12:52:38 -08:00
|
|
|
} else {
|
|
|
|
// Subtract the width of the tab/puzzle piece knob to the x coordinate
|
|
|
|
// since X is the corner of the rectangle, not the whole puzzle piece.
|
|
|
|
topLeft = new goog.math.Coordinate(blockXY.x - tab, blockXY.y);
|
|
|
|
// Width has the tab built into it already so subtract it here.
|
|
|
|
bottomRight = new goog.math.Coordinate(blockXY.x + blockBounds.width - tab,
|
2016-03-18 15:19:26 -07:00
|
|
|
blockXY.y + blockBounds.height);
|
2016-02-29 12:52:38 -08:00
|
|
|
}
|
2016-03-18 15:19:26 -07:00
|
|
|
return {topLeft: topLeft, bottomRight: bottomRight};
|
2016-02-29 12:52:38 -08:00
|
|
|
};
|
|
|
|
|
2016-04-14 18:04:17 -04:00
|
|
|
/**
|
|
|
|
* Set block opacity for SVG rendering.
|
|
|
|
* @param {number} opacity Intended opacity, betweeen 0 and 1
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setOpacity = function(opacity) {
|
|
|
|
this.opacity_ = opacity;
|
|
|
|
if (this.rendered) {
|
|
|
|
this.updateColour();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get block opacity for SVG rendering.
|
|
|
|
* @return {number} Intended opacity, betweeen 0 and 1
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.getOpacity = function() {
|
|
|
|
return this.opacity_;
|
|
|
|
};
|
|
|
|
|
2014-12-23 11:22:02 -08:00
|
|
|
/**
|
|
|
|
* Set whether the block is collapsed or not.
|
|
|
|
* @param {boolean} collapsed True if collapsed.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) {
|
|
|
|
if (this.collapsed_ == collapsed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var renderList = [];
|
|
|
|
// Show/hide the inputs.
|
2015-07-19 16:49:11 -07:00
|
|
|
for (var i = 0, input; input = this.inputList[i]; i++) {
|
2014-12-23 11:22:02 -08:00
|
|
|
renderList.push.apply(renderList, input.setVisible(!collapsed));
|
|
|
|
}
|
|
|
|
|
|
|
|
var COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT';
|
|
|
|
if (collapsed) {
|
|
|
|
var icons = this.getIcons();
|
2016-04-13 09:53:41 -07:00
|
|
|
for (i = 0; i < icons.length; i++) {
|
2015-07-19 16:49:11 -07:00
|
|
|
icons[i].setVisible(false);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
var text = this.toString(Blockly.COLLAPSE_CHARS);
|
|
|
|
this.appendDummyInput(COLLAPSED_INPUT_NAME).appendField(text).init();
|
|
|
|
} else {
|
|
|
|
this.removeInput(COLLAPSED_INPUT_NAME);
|
2015-07-19 16:49:11 -07:00
|
|
|
// Clear any warnings inherited from enclosed blocks.
|
|
|
|
this.setWarningText(null);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2015-03-02 12:45:20 -05:00
|
|
|
Blockly.BlockSvg.superClass_.setCollapsed.call(this, collapsed);
|
2014-12-23 11:22:02 -08:00
|
|
|
|
|
|
|
if (!renderList.length) {
|
|
|
|
// No child blocks, just render this block.
|
|
|
|
renderList[0] = this;
|
|
|
|
}
|
|
|
|
if (this.rendered) {
|
2015-07-19 16:49:11 -07:00
|
|
|
for (var i = 0, block; block = renderList[i]; i++) {
|
2014-12-23 11:22:02 -08:00
|
|
|
block.render();
|
|
|
|
}
|
2015-03-10 17:47:03 -07:00
|
|
|
// Don't bump neighbours.
|
|
|
|
// Although bumping neighbours would make sense, users often collapse
|
|
|
|
// all their functions and store them next to each other. Expanding and
|
|
|
|
// bumping causes all their definitions to go out of alignment.
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-10-14 16:23:23 -07:00
|
|
|
/**
|
|
|
|
* Open the next (or previous) FieldTextInput.
|
|
|
|
* @param {Blockly.Field|Blockly.Block} start Current location.
|
|
|
|
* @param {boolean} forward If true go forward, otherwise backward.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.tab = function(start, forward) {
|
|
|
|
// This function need not be efficient since it runs once on a keypress.
|
|
|
|
// Create an ordered list of all text fields and connected inputs.
|
|
|
|
var list = [];
|
|
|
|
for (var i = 0, input; input = this.inputList[i]; i++) {
|
|
|
|
for (var j = 0, field; field = input.fieldRow[j]; j++) {
|
|
|
|
if (field instanceof Blockly.FieldTextInput) {
|
|
|
|
// TODO: Also support dropdown fields.
|
|
|
|
list.push(field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (input.connection) {
|
|
|
|
var block = input.connection.targetBlock();
|
|
|
|
if (block) {
|
|
|
|
list.push(block);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-13 09:53:41 -07:00
|
|
|
i = list.indexOf(start);
|
2015-10-14 16:23:23 -07:00
|
|
|
if (i == -1) {
|
|
|
|
// No start location, start at the beginning or end.
|
|
|
|
i = forward ? -1 : list.length;
|
|
|
|
}
|
|
|
|
var target = list[forward ? i + 1 : i - 1];
|
|
|
|
if (!target) {
|
|
|
|
// Ran off of list.
|
|
|
|
var parent = this.getParent();
|
|
|
|
if (parent) {
|
|
|
|
parent.tab(this, forward);
|
|
|
|
}
|
|
|
|
} else if (target instanceof Blockly.Field) {
|
|
|
|
target.showEditor_();
|
|
|
|
} else {
|
|
|
|
target.tab(null, forward);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-12-23 11:22:02 -08:00
|
|
|
/**
|
|
|
|
* Handle a mouse-down on an SVG block.
|
|
|
|
* @param {!Event} e Mouse down event.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
|
2016-03-16 19:10:36 -07:00
|
|
|
if (this.workspace.options.readOnly) {
|
|
|
|
return;
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
if (this.isInFlyout) {
|
2015-12-17 14:16:04 -08:00
|
|
|
e.stopPropagation();
|
2014-12-23 11:22:02 -08:00
|
|
|
return;
|
|
|
|
}
|
2016-03-23 14:39:02 -07:00
|
|
|
Blockly.setPageSelectable(false);
|
2015-09-23 14:46:29 -07:00
|
|
|
this.workspace.markFocused();
|
2014-12-23 11:22:02 -08:00
|
|
|
// Update Blockly's knowledge of its own location.
|
2015-04-28 13:51:25 -07:00
|
|
|
Blockly.svgResize(this.workspace);
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.terminateDrag_();
|
|
|
|
this.select();
|
|
|
|
Blockly.hideChaff();
|
2016-02-03 15:28:29 -08:00
|
|
|
this.workspace.recordDeleteAreas();
|
2014-12-23 11:22:02 -08:00
|
|
|
if (Blockly.isRightButton(e)) {
|
|
|
|
// Right-click.
|
|
|
|
this.showContextMenu_(e);
|
|
|
|
} else if (!this.isMovable()) {
|
2016-03-26 19:37:12 -07:00
|
|
|
// Allow immovable blocks to be selected and context menued, but not
|
2014-12-23 11:22:02 -08:00
|
|
|
// dragged. Let this event bubble up to document, so the workspace may be
|
|
|
|
// dragged instead.
|
|
|
|
return;
|
|
|
|
} else {
|
2016-03-03 17:48:54 -08:00
|
|
|
if (!Blockly.Events.getGroup()) {
|
|
|
|
Blockly.Events.setGroup(true);
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
// Left-click (or middle click)
|
|
|
|
Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);
|
2015-08-19 17:21:05 -07:00
|
|
|
|
|
|
|
this.dragStartXY_ = this.getRelativeToSurfaceXY();
|
|
|
|
this.workspace.startDrag(e, this.dragStartXY_.x, this.dragStartXY_.y);
|
|
|
|
|
2016-03-29 08:36:11 -07:00
|
|
|
Blockly.dragMode_ = Blockly.DRAG_STICKY;
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.BlockSvg.onMouseUpWrapper_ = Blockly.bindEvent_(document,
|
|
|
|
'mouseup', this, this.onMouseUp_);
|
|
|
|
Blockly.BlockSvg.onMouseMoveWrapper_ = Blockly.bindEvent_(document,
|
|
|
|
'mousemove', this, this.onMouseMove_);
|
|
|
|
// Build a list of bubbles that need to be moved and where they started.
|
|
|
|
this.draggedBubbles_ = [];
|
|
|
|
var descendants = this.getDescendants();
|
2015-07-19 16:49:11 -07:00
|
|
|
for (var i = 0, descendant; descendant = descendants[i]; i++) {
|
2014-12-23 11:22:02 -08:00
|
|
|
var icons = descendant.getIcons();
|
2015-07-19 16:49:11 -07:00
|
|
|
for (var j = 0; j < icons.length; j++) {
|
|
|
|
var data = icons[j].getIconLocation();
|
|
|
|
data.bubble = icons[j];
|
2014-12-23 11:22:02 -08:00
|
|
|
this.draggedBubbles_.push(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// This event has been handled. No need to bubble up to the document.
|
|
|
|
e.stopPropagation();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a mouse-up anywhere in the SVG pane. Is only registered when a
|
|
|
|
* block is clicked. We can't use mouseUp on the block since a fast-moving
|
|
|
|
* cursor can briefly escape the block before it catches up.
|
|
|
|
* @param {!Event} e Mouse up event.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.onMouseUp_ = function(e) {
|
2016-03-29 08:36:11 -07:00
|
|
|
if (Blockly.dragMode_ != Blockly.DRAG_FREE) {
|
2016-03-29 08:10:44 -07:00
|
|
|
Blockly.Events.fire(
|
|
|
|
new Blockly.Events.Ui(this, 'click', undefined, undefined));
|
2016-02-24 13:57:14 -05:00
|
|
|
}
|
2016-03-23 14:39:02 -07:00
|
|
|
Blockly.setPageSelectable(true);
|
2016-01-08 13:03:22 -08:00
|
|
|
Blockly.terminateDrag_();
|
|
|
|
if (Blockly.selected && Blockly.highlightedConnection_) {
|
2016-04-19 11:28:25 +02:00
|
|
|
this.positionNewBlock(Blockly.selected,
|
2016-04-19 10:10:56 +02:00
|
|
|
Blockly.localConnection_, Blockly.highlightedConnection_);
|
2016-01-08 13:03:22 -08:00
|
|
|
// Connect two blocks together.
|
|
|
|
Blockly.localConnection_.connect(Blockly.highlightedConnection_);
|
|
|
|
if (this.rendered) {
|
|
|
|
// Trigger a connection animation.
|
|
|
|
// Determine which connection is inferior (lower in the source stack).
|
2016-02-01 16:13:05 -08:00
|
|
|
var inferiorConnection = Blockly.localConnection_.isSuperior() ?
|
|
|
|
Blockly.highlightedConnection_ : Blockly.localConnection_;
|
2016-03-15 16:03:49 -07:00
|
|
|
inferiorConnection.getSourceBlock().connectionUiEffect();
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2016-01-08 13:03:22 -08:00
|
|
|
if (this.workspace.trashcan) {
|
|
|
|
// Don't throw an object in the trash can if it just got connected.
|
|
|
|
this.workspace.trashcan.close();
|
|
|
|
}
|
|
|
|
} else if (!this.getParent() && Blockly.selected.isDeletable() &&
|
|
|
|
this.workspace.isDeleteArea(e)) {
|
|
|
|
var trashcan = this.workspace.trashcan;
|
|
|
|
if (trashcan) {
|
|
|
|
goog.Timer.callOnce(trashcan.close, 100, trashcan);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2016-01-08 13:03:22 -08:00
|
|
|
Blockly.selected.dispose(false, true);
|
|
|
|
// Dropping a block on the trash can will usually cause the workspace to
|
|
|
|
// resize to contain the newly positioned block. Force a second resize
|
|
|
|
// now that the block has been deleted.
|
|
|
|
Blockly.fireUiEvent(window, 'resize');
|
|
|
|
}
|
|
|
|
if (Blockly.highlightedConnection_) {
|
|
|
|
Blockly.highlightedConnection_ = null;
|
|
|
|
}
|
|
|
|
Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
|
2016-03-03 17:48:54 -08:00
|
|
|
if (!Blockly.WidgetDiv.isVisible()) {
|
|
|
|
Blockly.Events.setGroup(false);
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load the block's help page in a new window.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.showHelp_ = function() {
|
|
|
|
var url = goog.isFunction(this.helpUrl) ? this.helpUrl() : this.helpUrl;
|
|
|
|
if (url) {
|
2016-02-18 10:43:28 -05:00
|
|
|
// @todo rewrite
|
|
|
|
alert(url);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Show the context menu for this block.
|
|
|
|
* @param {!Event} e Mouse event.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.showContextMenu_ = function(e) {
|
2015-04-28 13:51:25 -07:00
|
|
|
if (this.workspace.options.readOnly || !this.contextMenu) {
|
2014-12-23 11:22:02 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Save the current block in a variable for use in closures.
|
|
|
|
var block = this;
|
2015-12-02 22:10:09 -08:00
|
|
|
var menuOptions = [];
|
2014-12-23 11:22:02 -08:00
|
|
|
|
|
|
|
if (this.isDeletable() && this.isMovable() && !block.isInFlyout) {
|
|
|
|
// Option to duplicate this block.
|
|
|
|
var duplicateOption = {
|
|
|
|
text: Blockly.Msg.DUPLICATE_BLOCK,
|
|
|
|
enabled: true,
|
|
|
|
callback: function() {
|
2015-09-18 18:46:19 -07:00
|
|
|
Blockly.duplicate_(block);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
if (this.getDescendants().length > this.workspace.remainingCapacity()) {
|
|
|
|
duplicateOption.enabled = false;
|
|
|
|
}
|
2015-12-02 22:10:09 -08:00
|
|
|
menuOptions.push(duplicateOption);
|
2014-12-23 11:22:02 -08:00
|
|
|
|
2016-02-18 10:43:28 -05:00
|
|
|
if (this.isEditable() && this.workspace.options.comments) {
|
2014-12-23 11:22:02 -08:00
|
|
|
// Option to add/remove a comment.
|
2016-01-04 12:05:41 -08:00
|
|
|
var commentOption = {enabled: !goog.userAgent.IE};
|
2014-12-23 11:22:02 -08:00
|
|
|
if (this.comment) {
|
|
|
|
commentOption.text = Blockly.Msg.REMOVE_COMMENT;
|
|
|
|
commentOption.callback = function() {
|
|
|
|
block.setCommentText(null);
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
commentOption.text = Blockly.Msg.ADD_COMMENT;
|
|
|
|
commentOption.callback = function() {
|
|
|
|
block.setCommentText('');
|
|
|
|
};
|
|
|
|
}
|
2015-12-02 22:10:09 -08:00
|
|
|
menuOptions.push(commentOption);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Option to delete this block.
|
|
|
|
// Count the number of blocks that are nested in this block.
|
|
|
|
var descendantCount = this.getDescendants().length;
|
|
|
|
var nextBlock = this.getNextBlock();
|
|
|
|
if (nextBlock) {
|
|
|
|
// Blocks in the current stack would survive this block's deletion.
|
|
|
|
descendantCount -= nextBlock.getDescendants().length;
|
|
|
|
}
|
|
|
|
var deleteOption = {
|
|
|
|
text: descendantCount == 1 ? Blockly.Msg.DELETE_BLOCK :
|
|
|
|
Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(descendantCount)),
|
|
|
|
enabled: true,
|
|
|
|
callback: function() {
|
|
|
|
block.dispose(true, true);
|
|
|
|
}
|
|
|
|
};
|
2015-12-02 22:10:09 -08:00
|
|
|
menuOptions.push(deleteOption);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Option to get help.
|
|
|
|
var url = goog.isFunction(this.helpUrl) ? this.helpUrl() : this.helpUrl;
|
|
|
|
var helpOption = {enabled: !!url};
|
|
|
|
helpOption.text = Blockly.Msg.HELP;
|
|
|
|
helpOption.callback = function() {
|
|
|
|
block.showHelp_();
|
|
|
|
};
|
2015-12-02 22:10:09 -08:00
|
|
|
menuOptions.push(helpOption);
|
2014-12-23 11:22:02 -08:00
|
|
|
|
2015-12-02 22:10:09 -08:00
|
|
|
// Allow the block to add or modify menuOptions.
|
2014-12-23 11:22:02 -08:00
|
|
|
if (this.customContextMenu && !block.isInFlyout) {
|
2015-12-02 22:10:09 -08:00
|
|
|
this.customContextMenu(menuOptions);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
|
2015-12-02 22:10:09 -08:00
|
|
|
Blockly.ContextMenu.show(e, menuOptions, this.RTL);
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.ContextMenu.currentBlock = this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move the connections for this block and all blocks attached under it.
|
|
|
|
* Also update any attached bubbles.
|
|
|
|
* @param {number} dx Horizontal offset from current location.
|
|
|
|
* @param {number} dy Vertical offset from current location.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.moveConnections_ = function(dx, dy) {
|
|
|
|
if (!this.rendered) {
|
|
|
|
// Rendering is required to lay out the blocks.
|
|
|
|
// This is probably an invisible block attached to a collapsed block.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var myConnections = this.getConnections_(false);
|
2015-07-19 16:49:11 -07:00
|
|
|
for (var i = 0; i < myConnections.length; i++) {
|
|
|
|
myConnections[i].moveBy(dx, dy);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
var icons = this.getIcons();
|
2016-04-13 09:53:41 -07:00
|
|
|
for (i = 0; i < icons.length; i++) {
|
2015-07-19 16:49:11 -07:00
|
|
|
icons[i].computeIconLocation();
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Recurse through all blocks attached under this one.
|
2016-04-13 09:53:41 -07:00
|
|
|
for (i = 0; i < this.childBlocks_.length; i++) {
|
2015-07-19 16:49:11 -07:00
|
|
|
this.childBlocks_[i].moveConnections_(dx, dy);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursively adds or removes the dragging class to this node and its children.
|
|
|
|
* @param {boolean} adding True if adding, false if removing.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setDragging_ = function(adding) {
|
|
|
|
if (adding) {
|
|
|
|
this.addDragging();
|
2016-04-07 13:22:07 -07:00
|
|
|
Blockly.draggingConnections_ =
|
|
|
|
Blockly.draggingConnections_.concat(this.getConnections_(true));
|
2014-12-23 11:22:02 -08:00
|
|
|
} else {
|
|
|
|
this.removeDragging();
|
2016-04-07 13:22:07 -07:00
|
|
|
Blockly.draggingConnections_ = [];
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
// Recurse through all blocks attached under this one.
|
2015-07-19 16:49:11 -07:00
|
|
|
for (var i = 0; i < this.childBlocks_.length; i++) {
|
|
|
|
this.childBlocks_[i].setDragging_(adding);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-04-07 22:58:05 -04:00
|
|
|
/**
|
|
|
|
* Move this block to its workspace's drag surface, accounting for positioning.
|
|
|
|
* Generally should be called at the same time as setDragging_(true).
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.moveToDragSurface_ = function() {
|
|
|
|
// The translation for drag surface blocks,
|
|
|
|
// is equal to the current relative-to-surface position,
|
|
|
|
// to keep the position in sync as it move on/off the surface.
|
|
|
|
var xy = this.getRelativeToSurfaceXY();
|
2016-04-11 14:19:56 -04:00
|
|
|
this.clearTransformAttributes_();
|
2016-04-13 16:14:19 -04:00
|
|
|
this.workspace.dragSurface.translateSurface(xy.x, xy.y);
|
2016-04-07 22:58:05 -04:00
|
|
|
// Execute the move on the top-level SVG component
|
|
|
|
this.workspace.dragSurface.setBlocksAndShow(this.getSvgRoot());
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move this block back to the workspace block canvas.
|
|
|
|
* Generally should be called at the same time as setDragging_(false).
|
|
|
|
* @private
|
|
|
|
*/
|
2016-04-13 16:14:19 -04:00
|
|
|
Blockly.BlockSvg.prototype.moveOffDragSurface_ = function() {
|
2016-04-11 14:19:56 -04:00
|
|
|
// Translate to current position, turning off 3d.
|
|
|
|
var xy = this.getRelativeToSurfaceXY();
|
|
|
|
this.clearTransformAttributes_();
|
|
|
|
this.translate(xy.x, xy.y, false);
|
2016-04-13 16:14:19 -04:00
|
|
|
this.workspace.dragSurface.clearAndHide(this.workspace.getCanvas());
|
2016-04-07 22:58:05 -04:00
|
|
|
};
|
|
|
|
|
2016-04-11 14:19:56 -04:00
|
|
|
/**
|
|
|
|
* Clear the block of style="..." and transform="..." attributes.
|
|
|
|
* Used when the block is switching from 3d to 2d transform or vice versa.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.clearTransformAttributes_ = function() {
|
|
|
|
if (this.getSvgRoot().hasAttribute('transform')) {
|
|
|
|
this.getSvgRoot().removeAttribute('transform');
|
|
|
|
}
|
|
|
|
if (this.getSvgRoot().hasAttribute('style')) {
|
|
|
|
this.getSvgRoot().removeAttribute('style');
|
|
|
|
}
|
|
|
|
};
|
2016-04-07 22:58:05 -04:00
|
|
|
|
2014-12-23 11:22:02 -08:00
|
|
|
/**
|
|
|
|
* Drag this block to follow the mouse.
|
|
|
|
* @param {!Event} e Mouse move event.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.onMouseMove_ = function(e) {
|
2016-01-08 13:03:22 -08:00
|
|
|
if (e.type == 'mousemove' && e.clientX <= 1 && e.clientY == 0 &&
|
|
|
|
e.button == 0) {
|
|
|
|
/* HACK:
|
|
|
|
Safari Mobile 6.0 and Chrome for Android 18.0 fire rogue mousemove
|
|
|
|
events on certain touch actions. Ignore events with these signatures.
|
|
|
|
This may result in a one-pixel blind spot in other browsers,
|
|
|
|
but this shouldn't be noticeable. */
|
|
|
|
e.stopPropagation();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var oldXY = this.getRelativeToSurfaceXY();
|
|
|
|
var newXY = this.workspace.moveDrag(e);
|
|
|
|
|
2016-03-29 08:36:11 -07:00
|
|
|
if (Blockly.dragMode_ == Blockly.DRAG_STICKY) {
|
2016-01-08 13:03:22 -08:00
|
|
|
// Still dragging within the sticky DRAG_RADIUS.
|
|
|
|
var dr = goog.math.Coordinate.distance(oldXY, newXY) * this.workspace.scale;
|
|
|
|
if (dr > Blockly.DRAG_RADIUS) {
|
|
|
|
// Switch to unrestricted dragging.
|
2016-03-29 08:36:11 -07:00
|
|
|
Blockly.dragMode_ = Blockly.DRAG_FREE;
|
2016-01-08 13:03:22 -08:00
|
|
|
Blockly.longStop_();
|
2016-04-09 11:09:13 -04:00
|
|
|
// Must move to drag surface before unplug(),
|
|
|
|
// or else connections will calculate the wrong relative to surface XY
|
|
|
|
// in tighten_(). Then blocks connected to this block move around on the
|
|
|
|
// drag surface. By moving to the drag surface before unplug, connection
|
|
|
|
// positions will be calculated correctly.
|
|
|
|
this.moveToDragSurface_();
|
2016-04-15 14:09:15 -04:00
|
|
|
// Clear all WidgetDivs without animating, in case blocks are moved around
|
|
|
|
Blockly.WidgetDiv.hide(true);
|
2016-01-08 13:03:22 -08:00
|
|
|
if (this.parentBlock_) {
|
|
|
|
// Push this block to the very top of the stack.
|
2016-03-03 17:48:54 -08:00
|
|
|
this.unplug();
|
2016-01-08 13:03:22 -08:00
|
|
|
this.disconnectUiEffect();
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2016-01-08 13:03:22 -08:00
|
|
|
this.setDragging_(true);
|
|
|
|
}
|
|
|
|
}
|
2016-03-29 08:36:11 -07:00
|
|
|
if (Blockly.dragMode_ == Blockly.DRAG_FREE) {
|
2016-01-08 13:03:22 -08:00
|
|
|
var dx = oldXY.x - this.dragStartXY_.x;
|
|
|
|
var dy = oldXY.y - this.dragStartXY_.y;
|
2016-04-13 16:14:19 -04:00
|
|
|
this.workspace.dragSurface.translateSurface(newXY.x, newXY.y);
|
2016-01-08 13:03:22 -08:00
|
|
|
// Drag all the nested bubbles.
|
|
|
|
for (var i = 0; i < this.draggedBubbles_.length; i++) {
|
|
|
|
var commentData = this.draggedBubbles_[i];
|
|
|
|
commentData.bubble.setIconLocation(commentData.x + dx,
|
|
|
|
commentData.y + dy);
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
|
2016-01-08 13:03:22 -08:00
|
|
|
// Check to see if any of this block's connections are within range of
|
|
|
|
// another block's connection.
|
|
|
|
var myConnections = this.getConnections_(false);
|
2016-03-11 18:07:26 -08:00
|
|
|
// Also check the last connection on this stack
|
|
|
|
var lastOnStack = this.lastConnectionInStack_();
|
|
|
|
if (lastOnStack && lastOnStack != this.nextConnection) {
|
|
|
|
myConnections.push(lastOnStack);
|
|
|
|
}
|
2016-01-08 13:03:22 -08:00
|
|
|
var closestConnection = null;
|
|
|
|
var localConnection = null;
|
|
|
|
var radiusConnection = Blockly.SNAP_RADIUS;
|
2016-04-13 09:53:41 -07:00
|
|
|
for (i = 0; i < myConnections.length; i++) {
|
2016-01-08 13:03:22 -08:00
|
|
|
var myConnection = myConnections[i];
|
|
|
|
var neighbour = myConnection.closest(radiusConnection, dx, dy);
|
|
|
|
if (neighbour.connection) {
|
|
|
|
closestConnection = neighbour.connection;
|
|
|
|
localConnection = myConnection;
|
|
|
|
radiusConnection = neighbour.radius;
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2016-01-08 13:03:22 -08:00
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
|
2016-04-07 13:22:07 -07:00
|
|
|
var candidateIsLast = (localConnection == lastOnStack);
|
2016-03-04 14:39:24 -08:00
|
|
|
this.updatePreviews(closestConnection, localConnection, radiusConnection,
|
2016-04-07 13:22:07 -07:00
|
|
|
e, newXY.x - this.dragStartXY_.x, newXY.y - this.dragStartXY_.y,
|
|
|
|
candidateIsLast);
|
2016-03-04 14:39:24 -08:00
|
|
|
}
|
|
|
|
// This event has been handled. No need to bubble up to the document.
|
|
|
|
e.stopPropagation();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Preview the results of the drag if the mouse is released immediately.
|
|
|
|
* @param {Blockly.Connection} closestConnection The closest connection found
|
|
|
|
* during the search
|
|
|
|
* @param {Blockly.Connection} localConnection The connection on the moving
|
|
|
|
* block.
|
|
|
|
* @param {number} radiusConnection The distance between closestConnection and
|
|
|
|
* localConnection.
|
|
|
|
* @param {!Event} e Mouse move event.
|
2016-03-16 12:01:43 -07:00
|
|
|
* @param {number} dx The x distance the block has moved onscreen up to this
|
|
|
|
* point in the drag.
|
|
|
|
* @param {number} dy The y distance the block has moved onscreen up to this
|
|
|
|
* point in the drag.
|
2016-04-07 13:22:07 -07:00
|
|
|
* @param {boolean} candidateIsLast True if the dragging stack is more than one
|
|
|
|
* block long and localConnection is the last connection on the stack.
|
2016-03-04 14:39:24 -08:00
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.updatePreviews = function(closestConnection,
|
2016-04-07 13:22:07 -07:00
|
|
|
localConnection, radiusConnection, e, dx, dy, candidateIsLast) {
|
2016-04-06 14:42:12 -07:00
|
|
|
// Don't fire events for insertion marker creation or movement.
|
2016-03-18 17:01:39 -07:00
|
|
|
Blockly.Events.disable();
|
2016-04-06 14:42:12 -07:00
|
|
|
// Remove an insertion marker if needed. For Scratch-Blockly we are using
|
|
|
|
// grayed-out blocks instead of highlighting the connection; for compatibility
|
|
|
|
// with Web Blockly the name "highlightedConnection" will still be used.
|
2016-04-19 11:35:00 +02:00
|
|
|
if (Blockly.highlightedConnection_ && Blockly.localConnection_ &&
|
|
|
|
(Blockly.highlightedConnection_ != closestConnection ||
|
|
|
|
Blockly.localConnection_ != localConnection)) {
|
2016-04-06 14:57:16 -07:00
|
|
|
if (Blockly.insertionMarker_ && Blockly.insertionMarkerConnection_) {
|
|
|
|
Blockly.BlockSvg.disconnectInsertionMarker();
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2016-04-07 13:22:07 -07:00
|
|
|
// If there's already an insertion marker but it's representing the wrong
|
|
|
|
// block, delete it so we can create the correct one.
|
|
|
|
if (Blockly.insertionMarker_ &&
|
2016-04-19 11:28:25 +02:00
|
|
|
((candidateIsLast && Blockly.localConnection_.sourceBlock_ == this) ||
|
|
|
|
(!candidateIsLast && Blockly.localConnection_.sourceBlock_ != this))) {
|
2016-04-07 13:22:07 -07:00
|
|
|
Blockly.insertionMarker_.dispose();
|
|
|
|
Blockly.insertionMarker_ = null;
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2016-03-04 14:39:24 -08:00
|
|
|
Blockly.highlightedConnection_ = null;
|
|
|
|
Blockly.localConnection_ = null;
|
|
|
|
}
|
2016-03-16 12:05:36 -07:00
|
|
|
|
2016-04-06 14:42:12 -07:00
|
|
|
// Add an insertion marker if needed.
|
2016-03-04 14:39:24 -08:00
|
|
|
if (closestConnection &&
|
2016-03-22 17:12:00 -04:00
|
|
|
closestConnection != Blockly.highlightedConnection_ &&
|
2016-04-06 14:42:12 -07:00
|
|
|
!closestConnection.sourceBlock_.isInsertionMarker()) {
|
2016-03-04 14:39:24 -08:00
|
|
|
Blockly.highlightedConnection_ = closestConnection;
|
|
|
|
Blockly.localConnection_ = localConnection;
|
2016-04-07 13:22:07 -07:00
|
|
|
if (!Blockly.insertionMarker_) {
|
|
|
|
Blockly.insertionMarker_ =
|
|
|
|
this.workspace.newBlock(Blockly.localConnection_.sourceBlock_.type);
|
2016-04-06 14:57:16 -07:00
|
|
|
Blockly.insertionMarker_.setInsertionMarker(true);
|
|
|
|
Blockly.insertionMarker_.initSvg();
|
2016-01-08 13:03:22 -08:00
|
|
|
}
|
2016-03-04 14:39:24 -08:00
|
|
|
|
2016-04-06 14:57:16 -07:00
|
|
|
var insertionMarker = Blockly.insertionMarker_;
|
2016-04-07 13:22:07 -07:00
|
|
|
var insertionMarkerConnection = insertionMarker.getMatchingConnection(
|
|
|
|
localConnection.sourceBlock_, localConnection);
|
2016-04-06 14:42:12 -07:00
|
|
|
if (insertionMarkerConnection != Blockly.insertionMarkerConnection_) {
|
|
|
|
insertionMarker.rendered = true;
|
2016-04-13 09:53:41 -07:00
|
|
|
// Render disconnected from everything else so that we have a valid
|
|
|
|
// connection location.
|
|
|
|
insertionMarker.render();
|
|
|
|
insertionMarker.getSvgRoot().setAttribute('visibility', 'visible');
|
2016-03-30 16:57:29 -07:00
|
|
|
|
2016-04-19 11:28:25 +02:00
|
|
|
this.positionNewBlock(insertionMarker,
|
|
|
|
insertionMarkerConnection, closestConnection);
|
2016-03-18 17:01:39 -07:00
|
|
|
|
2016-04-06 14:42:12 -07:00
|
|
|
if (insertionMarkerConnection.type == Blockly.PREVIOUS_STATEMENT &&
|
|
|
|
!insertionMarker.nextConnection) {
|
2016-03-14 17:14:00 -07:00
|
|
|
Blockly.bumpedConnection_ = closestConnection.targetConnection;
|
|
|
|
}
|
2016-04-19 09:15:52 +02:00
|
|
|
// Renders insertion marker.
|
2016-04-06 14:42:12 -07:00
|
|
|
insertionMarkerConnection.connect(closestConnection);
|
|
|
|
Blockly.insertionMarkerConnection_ = insertionMarkerConnection;
|
2016-01-08 13:03:22 -08:00
|
|
|
}
|
|
|
|
}
|
2016-03-18 17:01:39 -07:00
|
|
|
// Reenable events.
|
|
|
|
Blockly.Events.enable();
|
2016-03-04 14:39:24 -08:00
|
|
|
|
|
|
|
// Provide visual indication of whether the block will be deleted if
|
|
|
|
// dropped here.
|
|
|
|
if (this.isDeletable()) {
|
|
|
|
this.workspace.isDeleteArea(e);
|
|
|
|
}
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
2016-03-16 12:01:43 -07:00
|
|
|
/**
|
2016-04-06 14:42:12 -07:00
|
|
|
* Disconnect the current insertion marker from the stack, and heal the stack to
|
|
|
|
* its previous state.
|
2016-03-16 12:01:43 -07:00
|
|
|
*/
|
2016-04-06 14:57:16 -07:00
|
|
|
Blockly.BlockSvg.disconnectInsertionMarker = function() {
|
|
|
|
// The insertion marker is the first block in a stack, either because it
|
|
|
|
// doesn't have a previous connection or because the previous connection is
|
|
|
|
// not connected. Unplug won't do anything in that case. Instead, unplug the
|
2016-03-21 16:57:39 -04:00
|
|
|
// following block.
|
2016-04-06 14:57:16 -07:00
|
|
|
if (Blockly.insertionMarkerConnection_ ==
|
|
|
|
Blockly.insertionMarker_.nextConnection &&
|
|
|
|
(!Blockly.insertionMarker_.previousConnection ||
|
|
|
|
!Blockly.insertionMarker_.previousConnection.targetConnection)) {
|
2016-04-06 14:42:12 -07:00
|
|
|
Blockly.insertionMarkerConnection_.targetBlock().unplug(false);
|
2016-03-09 16:58:18 -08:00
|
|
|
}
|
2016-03-21 16:57:39 -04:00
|
|
|
// Inside of a C-block, first statement connection.
|
2016-04-06 14:42:12 -07:00
|
|
|
else if (Blockly.insertionMarkerConnection_.type == Blockly.NEXT_STATEMENT &&
|
2016-04-06 14:57:16 -07:00
|
|
|
Blockly.insertionMarkerConnection_ !=
|
|
|
|
Blockly.insertionMarker_.nextConnection) {
|
2016-04-06 14:42:12 -07:00
|
|
|
var innerConnection = Blockly.insertionMarkerConnection_.targetConnection;
|
2016-03-21 16:57:39 -04:00
|
|
|
innerConnection.sourceBlock_.unplug(false);
|
2016-03-09 16:58:18 -08:00
|
|
|
var previousBlockNextConnection =
|
2016-04-06 14:57:16 -07:00
|
|
|
Blockly.insertionMarker_.previousConnection.targetConnection;
|
|
|
|
Blockly.insertionMarker_.unplug(true);
|
2016-03-09 16:58:18 -08:00
|
|
|
if (previousBlockNextConnection) {
|
|
|
|
previousBlockNextConnection.connect(innerConnection);
|
|
|
|
}
|
|
|
|
}
|
2016-03-21 16:57:39 -04:00
|
|
|
else {
|
2016-04-06 14:57:16 -07:00
|
|
|
Blockly.insertionMarker_.unplug(true /* healStack */);
|
2016-03-21 16:57:39 -04:00
|
|
|
}
|
|
|
|
|
2016-04-06 14:42:12 -07:00
|
|
|
if (Blockly.insertionMarkerConnection_.targetConnection) {
|
|
|
|
throw 'insertionMarkerConnection still connected at the end of disconnectInsertionMarker';
|
2016-03-21 16:57:39 -04:00
|
|
|
}
|
2016-04-06 14:42:12 -07:00
|
|
|
Blockly.insertionMarkerConnection_ = null;
|
2016-04-06 14:57:16 -07:00
|
|
|
Blockly.insertionMarker_.getSvgRoot().setAttribute('visibility', 'hidden');
|
2016-03-09 16:58:18 -08:00
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Add or remove the UI indicating if this block is movable or not.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.updateMovable = function() {
|
2014-12-23 11:22:02 -08:00
|
|
|
if (this.isMovable()) {
|
2013-10-30 14:46:03 -07:00
|
|
|
Blockly.addClass_(/** @type {!Element} */ (this.svgGroup_),
|
|
|
|
'blocklyDraggable');
|
|
|
|
} else {
|
|
|
|
Blockly.removeClass_(/** @type {!Element} */ (this.svgGroup_),
|
|
|
|
'blocklyDraggable');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-04-28 13:51:25 -07:00
|
|
|
/**
|
|
|
|
* Set whether this block is movable or not.
|
|
|
|
* @param {boolean} movable True if movable.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setMovable = function(movable) {
|
|
|
|
Blockly.BlockSvg.superClass_.setMovable.call(this, movable);
|
|
|
|
this.updateMovable();
|
|
|
|
};
|
|
|
|
|
2015-09-30 19:33:05 -07:00
|
|
|
/**
|
|
|
|
* Set whether this block is editable or not.
|
2016-02-16 13:04:47 -08:00
|
|
|
* @param {boolean} editable True if editable.
|
2015-09-30 19:33:05 -07:00
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setEditable = function(editable) {
|
|
|
|
Blockly.BlockSvg.superClass_.setEditable.call(this, editable);
|
|
|
|
if (this.rendered) {
|
|
|
|
for (var i = 0; i < this.icons_.length; i++) {
|
|
|
|
this.icons_[i].updateEditable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-10-14 18:20:53 -07:00
|
|
|
/**
|
|
|
|
* Set whether this block is a shadow block or not.
|
|
|
|
* @param {boolean} shadow True if a shadow.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setShadow = function(shadow) {
|
|
|
|
Blockly.BlockSvg.superClass_.setShadow.call(this, shadow);
|
|
|
|
this.updateColour();
|
|
|
|
};
|
|
|
|
|
2016-02-23 14:46:04 -08:00
|
|
|
/**
|
2016-04-06 14:42:12 -07:00
|
|
|
* Set whether this block is an insertion marker block or not.
|
|
|
|
* @param {boolean} insertionMarker True if an insertion marker.
|
2016-02-23 14:46:04 -08:00
|
|
|
*/
|
2016-04-06 14:42:12 -07:00
|
|
|
Blockly.BlockSvg.prototype.setInsertionMarker = function(insertionMarker) {
|
|
|
|
Blockly.BlockSvg.superClass_.setInsertionMarker.call(this, insertionMarker);
|
2016-02-23 14:46:04 -08:00
|
|
|
this.updateColour();
|
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
2014-12-23 11:22:02 -08:00
|
|
|
* Return the root node of the SVG or null if none exists.
|
|
|
|
* @return {Element} The root SVG node (probably a group).
|
2013-10-30 14:46:03 -07:00
|
|
|
*/
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.BlockSvg.prototype.getSvgRoot = function() {
|
2013-10-30 14:46:03 -07:00
|
|
|
return this.svgGroup_;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-12-23 11:22:02 -08:00
|
|
|
* Dispose of this block.
|
|
|
|
* @param {boolean} healStack If true, then try to heal any gap by connecting
|
|
|
|
* the next statement with the previous statement. Otherwise, dispose of
|
|
|
|
* all children of this block.
|
|
|
|
* @param {boolean} animate If true, show a disposal animation and sound.
|
2013-10-30 14:46:03 -07:00
|
|
|
*/
|
2016-01-20 19:11:03 -08:00
|
|
|
Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
|
2015-09-15 16:20:13 -07:00
|
|
|
Blockly.Field.startCache();
|
2015-04-28 13:51:25 -07:00
|
|
|
// If this block is being dragged, unlink the mouse events.
|
|
|
|
if (Blockly.selected == this) {
|
2016-03-28 18:10:54 -07:00
|
|
|
this.unselect();
|
2015-04-28 13:51:25 -07:00
|
|
|
Blockly.terminateDrag_();
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
// If this block has a context menu open, close it.
|
|
|
|
if (Blockly.ContextMenu.currentBlock == this) {
|
|
|
|
Blockly.ContextMenu.hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (animate && this.rendered) {
|
2016-02-01 16:13:05 -08:00
|
|
|
this.unplug(healStack);
|
2014-12-23 11:22:02 -08:00
|
|
|
this.disposeUiEffect();
|
|
|
|
}
|
|
|
|
// Stop rerendering.
|
|
|
|
this.rendered = false;
|
|
|
|
|
2016-01-20 19:11:03 -08:00
|
|
|
Blockly.Events.disable();
|
2014-12-23 11:22:02 -08:00
|
|
|
var icons = this.getIcons();
|
2015-07-19 16:49:11 -07:00
|
|
|
for (var i = 0; i < icons.length; i++) {
|
|
|
|
icons[i].dispose();
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
2016-01-20 19:11:03 -08:00
|
|
|
Blockly.Events.enable();
|
2016-03-14 23:14:58 -07:00
|
|
|
Blockly.BlockSvg.superClass_.dispose.call(this, healStack);
|
2014-12-23 11:22:02 -08:00
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
goog.dom.removeNode(this.svgGroup_);
|
|
|
|
// Sever JavaScript to DOM connections.
|
|
|
|
this.svgGroup_ = null;
|
|
|
|
this.svgPath_ = null;
|
2015-09-15 16:20:13 -07:00
|
|
|
Blockly.Field.stopCache();
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Play some UI effects (sound, animation) when disposing of a block.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.disposeUiEffect = function() {
|
2015-04-28 13:51:25 -07:00
|
|
|
this.workspace.playAudio('delete');
|
2013-10-30 14:46:03 -07:00
|
|
|
|
2015-08-19 17:21:05 -07:00
|
|
|
var xy = Blockly.getSvgXY_(/** @type {!Element} */ (this.svgGroup_),
|
|
|
|
this.workspace);
|
2013-10-30 14:46:03 -07:00
|
|
|
// Deeply clone the current block.
|
|
|
|
var clone = this.svgGroup_.cloneNode(true);
|
|
|
|
clone.translateX_ = xy.x;
|
|
|
|
clone.translateY_ = xy.y;
|
|
|
|
clone.setAttribute('transform',
|
|
|
|
'translate(' + clone.translateX_ + ',' + clone.translateY_ + ')');
|
2016-01-07 17:01:01 -08:00
|
|
|
this.workspace.getParentSvg().appendChild(clone);
|
2013-10-30 14:46:03 -07:00
|
|
|
clone.bBox_ = clone.getBBox();
|
|
|
|
// Start the animation.
|
2015-08-19 17:21:05 -07:00
|
|
|
Blockly.BlockSvg.disposeUiStep_(clone, this.RTL, new Date(),
|
|
|
|
this.workspace.scale);
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Animate a cloned block and eventually dispose of it.
|
2015-08-19 17:21:05 -07:00
|
|
|
* This is a class method, not an instace method since the original block has
|
|
|
|
* been destroyed and is no longer accessible.
|
2013-10-30 14:46:03 -07:00
|
|
|
* @param {!Element} clone SVG element to animate and dispose of.
|
2015-04-28 13:51:25 -07:00
|
|
|
* @param {boolean} rtl True if RTL, false if LTR.
|
2015-08-19 17:21:05 -07:00
|
|
|
* @param {!Date} start Date of animation's start.
|
|
|
|
* @param {number} workspaceScale Scale of workspace.
|
2013-10-30 14:46:03 -07:00
|
|
|
* @private
|
|
|
|
*/
|
2015-08-19 17:21:05 -07:00
|
|
|
Blockly.BlockSvg.disposeUiStep_ = function(clone, rtl, start, workspaceScale) {
|
|
|
|
var ms = (new Date()) - start;
|
2013-10-30 14:46:03 -07:00
|
|
|
var percent = ms / 150;
|
|
|
|
if (percent > 1) {
|
|
|
|
goog.dom.removeNode(clone);
|
|
|
|
} else {
|
|
|
|
var x = clone.translateX_ +
|
2015-08-19 17:21:05 -07:00
|
|
|
(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 + ')' +
|
2013-10-30 14:46:03 -07:00
|
|
|
' scale(' + scale + ')');
|
|
|
|
var closure = function() {
|
2015-08-19 17:21:05 -07:00
|
|
|
Blockly.BlockSvg.disposeUiStep_(clone, rtl, start, workspaceScale);
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
2014-12-23 16:44:00 -08:00
|
|
|
setTimeout(closure, 10);
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-02-29 16:15:03 -05:00
|
|
|
* Play some UI effects (sound) when disconnecting a block.
|
2015-09-20 14:28:39 -07:00
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.disconnectUiEffect = function() {
|
|
|
|
this.workspace.playAudio('disconnect');
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable or disable a block.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.updateDisabled = function() {
|
2016-02-18 10:43:28 -05:00
|
|
|
// not supported
|
2014-12-23 11:22:02 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the comment on this block (or '' if none).
|
|
|
|
* @return {string} Block's comment.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.getCommentText = function() {
|
|
|
|
if (this.comment) {
|
|
|
|
var comment = this.comment.getText();
|
|
|
|
// Trim off trailing whitespace.
|
|
|
|
return comment.replace(/\s+$/, '').replace(/ +\n/g, '\n');
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set this block's comment text.
|
|
|
|
* @param {?string} text The text, or null to delete.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setCommentText = function(text) {
|
|
|
|
var changedState = false;
|
|
|
|
if (goog.isString(text)) {
|
|
|
|
if (!this.comment) {
|
|
|
|
this.comment = new Blockly.Comment(this);
|
|
|
|
changedState = true;
|
|
|
|
}
|
|
|
|
this.comment.setText(/** @type {string} */ (text));
|
|
|
|
} else {
|
|
|
|
if (this.comment) {
|
|
|
|
this.comment.dispose();
|
|
|
|
changedState = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (changedState && this.rendered) {
|
|
|
|
this.render();
|
|
|
|
// Adding or removing a comment icon will cause the block to change shape.
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set this block's warning text.
|
|
|
|
* @param {?string} text The text, or null to delete.
|
2015-07-14 22:22:13 +01:00
|
|
|
* @param {string=} opt_id An optional ID for the warning text to be able to
|
|
|
|
* maintain multiple warnings.
|
2014-12-23 11:22:02 -08:00
|
|
|
*/
|
2015-07-14 22:22:13 +01:00
|
|
|
Blockly.BlockSvg.prototype.setWarningText = function(text, opt_id) {
|
2015-07-19 16:49:11 -07:00
|
|
|
if (!this.setWarningText.pid_) {
|
|
|
|
// Create a database of warning PIDs.
|
|
|
|
// Only runs once per block (and only those with warnings).
|
|
|
|
this.setWarningText.pid_ = Object.create(null);
|
|
|
|
}
|
|
|
|
var id = opt_id || '';
|
|
|
|
if (!id) {
|
|
|
|
// Kill all previous pending processes, this edit supercedes them all.
|
|
|
|
for (var n in this.setWarningText.pid_) {
|
|
|
|
clearTimeout(this.setWarningText.pid_[n]);
|
|
|
|
delete this.setWarningText.pid_[n];
|
|
|
|
}
|
|
|
|
} else if (this.setWarningText.pid_[id]) {
|
2015-02-25 15:07:04 -08:00
|
|
|
// Only queue up the latest change. Kill any earlier pending process.
|
2015-07-19 16:49:11 -07:00
|
|
|
clearTimeout(this.setWarningText.pid_[id]);
|
|
|
|
delete this.setWarningText.pid_[id];
|
2015-02-25 15:07:04 -08:00
|
|
|
}
|
2016-03-29 08:36:11 -07:00
|
|
|
if (Blockly.dragMode_ == Blockly.DRAG_FREE) {
|
2015-02-25 15:07:04 -08:00
|
|
|
// Don't change the warning text during a drag.
|
|
|
|
// Wait until the drag finishes.
|
|
|
|
var thisBlock = this;
|
2015-07-19 16:49:11 -07:00
|
|
|
this.setWarningText.pid_[id] = setTimeout(function() {
|
2015-08-19 18:42:35 -07:00
|
|
|
if (thisBlock.workspace) { // Check block wasn't deleted.
|
|
|
|
delete thisBlock.setWarningText.pid_[id];
|
|
|
|
thisBlock.setWarningText(text, id);
|
|
|
|
}
|
2015-02-25 15:07:04 -08:00
|
|
|
}, 100);
|
|
|
|
return;
|
|
|
|
}
|
2014-12-23 11:22:02 -08:00
|
|
|
if (this.isInFlyout) {
|
|
|
|
text = null;
|
|
|
|
}
|
2015-07-19 16:49:11 -07:00
|
|
|
|
2014-12-23 11:22:02 -08:00
|
|
|
var changedState = false;
|
|
|
|
if (goog.isString(text)) {
|
|
|
|
if (!this.warning) {
|
|
|
|
this.warning = new Blockly.Warning(this);
|
|
|
|
changedState = true;
|
|
|
|
}
|
2015-07-19 16:49:11 -07:00
|
|
|
this.warning.setText(/** @type {string} */ (text), id);
|
2014-12-23 11:22:02 -08:00
|
|
|
} else {
|
2015-07-19 16:49:11 -07:00
|
|
|
// Dispose all warnings if no id is given.
|
|
|
|
if (this.warning && !id) {
|
2014-12-23 11:22:02 -08:00
|
|
|
this.warning.dispose();
|
|
|
|
changedState = true;
|
2015-07-14 22:22:13 +01:00
|
|
|
} else if (this.warning) {
|
2015-08-17 15:23:08 +01:00
|
|
|
var oldText = this.warning.getText();
|
2015-07-19 16:49:11 -07:00
|
|
|
this.warning.setText('', id);
|
2015-08-17 15:23:08 +01:00
|
|
|
var newText = this.warning.getText();
|
2015-07-19 16:49:11 -07:00
|
|
|
if (!newText) {
|
|
|
|
this.warning.dispose();
|
|
|
|
}
|
|
|
|
changedState = oldText == newText;
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (changedState && this.rendered) {
|
|
|
|
this.render();
|
|
|
|
// Adding or removing a warning icon will cause the block to change shape.
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Give this block a mutator dialog.
|
|
|
|
* @param {Blockly.Mutator} mutator A mutator dialog instance or null to remove.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setMutator = function(mutator) {
|
|
|
|
if (this.mutator && this.mutator !== mutator) {
|
|
|
|
this.mutator.dispose();
|
|
|
|
}
|
|
|
|
if (mutator) {
|
|
|
|
mutator.block_ = this;
|
|
|
|
this.mutator = mutator;
|
2015-12-17 14:16:04 -08:00
|
|
|
mutator.createIcon();
|
2014-12-23 11:22:02 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Select this block. Highlight it visually.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.addSelect = function() {
|
|
|
|
Blockly.addClass_(/** @type {!Element} */ (this.svgGroup_),
|
|
|
|
'blocklySelected');
|
|
|
|
// Move the selected block to the top of the stack.
|
|
|
|
this.svgGroup_.parentNode.appendChild(this.svgGroup_);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unselect this block. Remove its highlighting.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.removeSelect = function() {
|
|
|
|
Blockly.removeClass_(/** @type {!Element} */ (this.svgGroup_),
|
|
|
|
'blocklySelected');
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds the dragging class to this block.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.addDragging = function() {
|
|
|
|
Blockly.addClass_(/** @type {!Element} */ (this.svgGroup_),
|
|
|
|
'blocklyDragging');
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the dragging class from this block.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.removeDragging = function() {
|
|
|
|
Blockly.removeClass_(/** @type {!Element} */ (this.svgGroup_),
|
|
|
|
'blocklyDragging');
|
|
|
|
};
|
2016-03-29 17:30:39 -07:00
|
|
|
|
|
|
|
// Overrides of functions on Blockly.Block that take into account whether the
|
|
|
|
// block has been rendered.
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change the colour of a block.
|
|
|
|
* @param {number|string} colour HSV hue value, or #RRGGBB string.
|
2016-04-07 13:22:07 -07:00
|
|
|
* @param {number|string} colourSecondary Secondary HSV hue value, or #RRGGBB
|
|
|
|
* string.
|
|
|
|
* @param {number|string} colourTertiary Tertiary HSV hue value, or #RRGGBB
|
|
|
|
* string.
|
2016-03-29 17:30:39 -07:00
|
|
|
*/
|
2016-04-07 13:22:07 -07:00
|
|
|
Blockly.BlockSvg.prototype.setColour = function(colour, colourSecondary,
|
|
|
|
colourTertiary) {
|
|
|
|
Blockly.BlockSvg.superClass_.setColour.call(this, colour, colourSecondary,
|
|
|
|
colourTertiary);
|
2016-03-29 17:30:39 -07:00
|
|
|
|
|
|
|
if (this.rendered) {
|
|
|
|
this.updateColour();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set whether this block can chain onto the bottom of another block.
|
|
|
|
* @param {boolean} newBoolean True if there can be a previous statement.
|
|
|
|
* @param {string|Array.<string>|null|undefined} opt_check Statement type or
|
|
|
|
* list of statement types. Null/undefined if any type could be connected.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setPreviousStatement =
|
|
|
|
function(newBoolean, opt_check) {
|
|
|
|
Blockly.BlockSvg.superClass_.setPreviousStatement.call(this, newBoolean,
|
|
|
|
opt_check);
|
|
|
|
|
|
|
|
if (this.rendered) {
|
|
|
|
this.render();
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set whether another block can chain onto the bottom of this block.
|
|
|
|
* @param {boolean} newBoolean True if there can be a next statement.
|
|
|
|
* @param {string|Array.<string>|null|undefined} opt_check Statement type or
|
|
|
|
* list of statement types. Null/undefined if any type could be connected.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setNextStatement = function(newBoolean, opt_check) {
|
|
|
|
Blockly.BlockSvg.superClass_.setNextStatement.call(this, newBoolean,
|
|
|
|
opt_check);
|
|
|
|
|
|
|
|
if (this.rendered) {
|
|
|
|
this.render();
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set whether this block returns a value.
|
|
|
|
* @param {boolean} newBoolean True if there is an output.
|
|
|
|
* @param {string|Array.<string>|null|undefined} opt_check Returned type or list
|
|
|
|
* of returned types. Null or undefined if any type could be returned
|
|
|
|
* (e.g. variable get).
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setOutput = function(newBoolean, opt_check) {
|
|
|
|
Blockly.BlockSvg.superClass_.setOutput.call(this, newBoolean, opt_check);
|
|
|
|
|
|
|
|
if (this.rendered) {
|
|
|
|
this.render();
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set whether value inputs are arranged horizontally or vertically.
|
|
|
|
* @param {boolean} newBoolean True if inputs are horizontal.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.setInputsInline = function(newBoolean) {
|
|
|
|
Blockly.BlockSvg.superClass_.setInputsInline.call(this, newBoolean);
|
|
|
|
|
|
|
|
if (this.rendered) {
|
|
|
|
this.render();
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove an input from this block.
|
|
|
|
* @param {string} name The name of the input.
|
|
|
|
* @param {boolean=} opt_quiet True to prevent error if input is not present.
|
|
|
|
* @throws {goog.asserts.AssertionError} if the input is not present and
|
|
|
|
* opt_quiet is not true.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.removeInput = function(name, opt_quiet) {
|
|
|
|
Blockly.BlockSvg.superClass_.removeInput.call(this, name, opt_quiet);
|
|
|
|
|
|
|
|
if (this.rendered) {
|
|
|
|
this.render();
|
|
|
|
// Removing an input will cause the block to change shape.
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move a numbered input to a different location on this block.
|
|
|
|
* @param {number} inputIndex Index of the input to move.
|
|
|
|
* @param {number} refIndex Index of input that should be after the moved input.
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.moveNumberedInputBefore = function(
|
|
|
|
inputIndex, refIndex) {
|
|
|
|
Blockly.BlockSvg.superClass_.moveNumberedInputBefore.call(this, inputIndex,
|
|
|
|
refIndex);
|
|
|
|
|
|
|
|
if (this.rendered) {
|
|
|
|
this.render();
|
|
|
|
// Moving an input will cause the block to change shape.
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a value input, statement input or local variable to this block.
|
|
|
|
* @param {number} type Either Blockly.INPUT_VALUE or Blockly.NEXT_STATEMENT or
|
|
|
|
* Blockly.DUMMY_INPUT.
|
|
|
|
* @param {string} name Language-neutral identifier which may used to find this
|
|
|
|
* input again. Should be unique to this block.
|
|
|
|
* @return {!Blockly.Input} The input object created.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.appendInput_ = function(type, name) {
|
|
|
|
var input = Blockly.BlockSvg.superClass_.appendInput_.call(this, type, name);
|
|
|
|
|
|
|
|
if (this.rendered) {
|
|
|
|
this.render();
|
|
|
|
// Adding an input will cause the block to change shape.
|
|
|
|
this.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
return input;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns connections originating from this block.
|
|
|
|
* @param {boolean} all If true, return all connections even hidden ones.
|
|
|
|
* Otherwise, for a non-rendered block return an empty list, and for a
|
|
|
|
* collapsed block don't return inputs connections.
|
|
|
|
* @return {!Array.<!Blockly.Connection>} Array of connections.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.BlockSvg.prototype.getConnections_ = function(all) {
|
|
|
|
var myConnections = [];
|
|
|
|
if (all || this.rendered) {
|
|
|
|
if (this.outputConnection) {
|
|
|
|
myConnections.push(this.outputConnection);
|
|
|
|
}
|
|
|
|
if (this.previousConnection) {
|
|
|
|
myConnections.push(this.previousConnection);
|
|
|
|
}
|
|
|
|
if (this.nextConnection) {
|
|
|
|
myConnections.push(this.nextConnection);
|
|
|
|
}
|
|
|
|
if (all || !this.collapsed_) {
|
|
|
|
for (var i = 0, input; input = this.inputList[i]; i++) {
|
|
|
|
if (input.connection) {
|
|
|
|
myConnections.push(input.connection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return myConnections;
|
|
|
|
};
|