mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
with a manual calculation. getBBox() can cause the browser to re-layout the whole page and we have much of the information (e.g. individual block height and width) we need already cached. Note that getBoundingRectangle's calculation is slightly different than the one returned by getBBox. It is off by 3 in the y direction due to how one of the curves is drawn. This new calculation is technically more accurate.
626 lines
18 KiB
JavaScript
626 lines
18 KiB
JavaScript
/**
|
|
* @license
|
|
* Visual Blocks Editor
|
|
*
|
|
* Copyright 2011 Google Inc.
|
|
* https://developers.google.com/blockly/
|
|
*
|
|
* 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 Core JavaScript library for Blockly.
|
|
* @author fraser@google.com (Neil Fraser)
|
|
*/
|
|
'use strict';
|
|
|
|
// Top level object for Blockly.
|
|
goog.provide('Blockly');
|
|
|
|
goog.require('Blockly.BlockSvg.render');
|
|
goog.require('Blockly.Events');
|
|
goog.require('Blockly.FieldAngle');
|
|
goog.require('Blockly.FieldCheckbox');
|
|
goog.require('Blockly.FieldColour');
|
|
// Date picker commented out since it increases footprint by 60%.
|
|
// Add it only if you need it.
|
|
//goog.require('Blockly.FieldDate');
|
|
goog.require('Blockly.FieldDropdown');
|
|
goog.require('Blockly.FieldImage');
|
|
goog.require('Blockly.FieldTextInput');
|
|
goog.require('Blockly.FieldVariable');
|
|
goog.require('Blockly.Generator');
|
|
goog.require('Blockly.Msg');
|
|
goog.require('Blockly.Procedures');
|
|
goog.require('Blockly.Toolbox');
|
|
goog.require('Blockly.WidgetDiv');
|
|
goog.require('Blockly.WorkspaceSvg');
|
|
goog.require('Blockly.inject');
|
|
goog.require('Blockly.utils');
|
|
goog.require('goog.color');
|
|
goog.require('goog.userAgent');
|
|
|
|
|
|
// Turn off debugging when compiled.
|
|
var CLOSURE_DEFINES = {'goog.DEBUG': false};
|
|
|
|
/**
|
|
* Required name space for SVG elements.
|
|
* @const
|
|
*/
|
|
Blockly.SVG_NS = 'http://www.w3.org/2000/svg';
|
|
/**
|
|
* Required name space for HTML elements.
|
|
* @const
|
|
*/
|
|
Blockly.HTML_NS = 'http://www.w3.org/1999/xhtml';
|
|
|
|
/**
|
|
* 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'
|
|
};
|
|
|
|
/**
|
|
* Convert a hue (HSV model) into an RGB hex triplet.
|
|
* @param {number} hue Hue on a colour wheel (0-360).
|
|
* @return {string} RGB code, e.g. '#5ba65b'.
|
|
*/
|
|
Blockly.hueToRgb = function(hue) {
|
|
return goog.color.hsvToHex(hue, Blockly.HSV_SATURATION,
|
|
Blockly.HSV_VALUE * 255);
|
|
};
|
|
|
|
/**
|
|
* ENUM for a right-facing value input. E.g. 'set item to' or 'return'.
|
|
* @const
|
|
*/
|
|
Blockly.INPUT_VALUE = 1;
|
|
/**
|
|
* ENUM for a left-facing value output. E.g. 'random fraction'.
|
|
* @const
|
|
*/
|
|
Blockly.OUTPUT_VALUE = 2;
|
|
/**
|
|
* ENUM for a down-facing block stack. E.g. 'if-do' or 'else'.
|
|
* @const
|
|
*/
|
|
Blockly.NEXT_STATEMENT = 3;
|
|
/**
|
|
* ENUM for an up-facing block stack. E.g. 'break out of loop'.
|
|
* @const
|
|
*/
|
|
Blockly.PREVIOUS_STATEMENT = 4;
|
|
/**
|
|
* ENUM for an dummy input. Used to add field(s) with no input.
|
|
* @const
|
|
*/
|
|
Blockly.DUMMY_INPUT = 5;
|
|
|
|
/**
|
|
* ENUM for left alignment.
|
|
* @const
|
|
*/
|
|
Blockly.ALIGN_LEFT = -1;
|
|
/**
|
|
* ENUM for centre alignment.
|
|
* @const
|
|
*/
|
|
Blockly.ALIGN_CENTRE = 0;
|
|
/**
|
|
* ENUM for right alignment.
|
|
* @const
|
|
*/
|
|
Blockly.ALIGN_RIGHT = 1;
|
|
|
|
/**
|
|
* Lookup table for determining the opposite type of a connection.
|
|
* @const
|
|
*/
|
|
Blockly.OPPOSITE_TYPE = [];
|
|
Blockly.OPPOSITE_TYPE[Blockly.INPUT_VALUE] = Blockly.OUTPUT_VALUE;
|
|
Blockly.OPPOSITE_TYPE[Blockly.OUTPUT_VALUE] = Blockly.INPUT_VALUE;
|
|
Blockly.OPPOSITE_TYPE[Blockly.NEXT_STATEMENT] = Blockly.PREVIOUS_STATEMENT;
|
|
Blockly.OPPOSITE_TYPE[Blockly.PREVIOUS_STATEMENT] = Blockly.NEXT_STATEMENT;
|
|
|
|
/**
|
|
* Currently selected block.
|
|
* @type {Blockly.Block}
|
|
*/
|
|
Blockly.selected = null;
|
|
|
|
/**
|
|
* Currently highlighted connection (during a drag).
|
|
* @type {Blockly.Connection}
|
|
* @private
|
|
*/
|
|
Blockly.highlightedConnection_ = null;
|
|
|
|
/**
|
|
* Connection on dragged block that matches the highlighted connection.
|
|
* @type {Blockly.Connection}
|
|
* @private
|
|
*/
|
|
Blockly.localConnection_ = null;
|
|
|
|
/**
|
|
* Number of pixels the mouse must move before a drag starts.
|
|
*/
|
|
Blockly.DRAG_RADIUS = 5;
|
|
|
|
/**
|
|
* Maximum misalignment between connections for them to snap together.
|
|
*/
|
|
Blockly.SNAP_RADIUS = 20;
|
|
|
|
/**
|
|
* Delay in ms between trigger and bumping unconnected block out of alignment.
|
|
*/
|
|
Blockly.BUMP_DELAY = 250;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* The main workspace most recently used.
|
|
* Set by Blockly.WorkspaceSvg.prototype.markFocused
|
|
* @type {Blockly.Workspace}
|
|
*/
|
|
Blockly.mainWorkspace = null;
|
|
|
|
/**
|
|
* Contents of the local clipboard.
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
Blockly.clipboardXml_ = null;
|
|
|
|
/**
|
|
* Source of the local clipboard.
|
|
* @type {Blockly.WorkspaceSvg}
|
|
* @private
|
|
*/
|
|
Blockly.clipboardSource_ = null;
|
|
|
|
/**
|
|
* Is the mouse dragging a block?
|
|
* 0 - No drag operation.
|
|
* 1 - Still inside the sticky DRAG_RADIUS.
|
|
* 2 - Freely draggable.
|
|
* @private
|
|
*/
|
|
Blockly.dragMode_ = 0;
|
|
|
|
/**
|
|
* Wrapper function called when a touch mouseUp occurs during a drag operation.
|
|
* @type {Array.<!Array>}
|
|
* @private
|
|
*/
|
|
Blockly.onTouchUpWrapper_ = null;
|
|
|
|
/**
|
|
* Returns the dimensions of the specified SVG image.
|
|
* @param {!Element} svg SVG image.
|
|
* @return {!Object} Contains width and height properties.
|
|
*/
|
|
Blockly.svgSize = function(svg) {
|
|
return {width: svg.cachedWidth_,
|
|
height: svg.cachedHeight_};
|
|
};
|
|
|
|
/**
|
|
* Size the SVG image to completely fill its container.
|
|
* Record the height/width of the SVG image.
|
|
* @param {!Blockly.WorkspaceSvg} workspace Any workspace in the SVG.
|
|
*/
|
|
Blockly.svgResize = function(workspace) {
|
|
var mainWorkspace = workspace;
|
|
while (mainWorkspace.options.parentWorkspace) {
|
|
mainWorkspace = mainWorkspace.options.parentWorkspace;
|
|
}
|
|
var svg = mainWorkspace.getParentSvg();
|
|
var div = svg.parentNode;
|
|
if (!div) {
|
|
// Workspace deteted, or something.
|
|
return;
|
|
}
|
|
var width = div.offsetWidth;
|
|
var height = div.offsetHeight;
|
|
if (svg.cachedWidth_ != width) {
|
|
svg.setAttribute('width', width + 'px');
|
|
svg.cachedWidth_ = width;
|
|
}
|
|
if (svg.cachedHeight_ != height) {
|
|
svg.setAttribute('height', height + 'px');
|
|
svg.cachedHeight_ = height;
|
|
}
|
|
mainWorkspace.resize();
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-up anywhere on the page.
|
|
* @param {!Event} e Mouse up event.
|
|
* @private
|
|
*/
|
|
Blockly.onMouseUp_ = function(e) {
|
|
var workspace = Blockly.getMainWorkspace();
|
|
Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
|
|
workspace.isScrolling = false;
|
|
|
|
// Unbind the touch event if it exists.
|
|
if (Blockly.onTouchUpWrapper_) {
|
|
Blockly.unbindEvent_(Blockly.onTouchUpWrapper_);
|
|
Blockly.onTouchUpWrapper_ = null;
|
|
}
|
|
if (Blockly.onMouseMoveWrapper_) {
|
|
Blockly.unbindEvent_(Blockly.onMouseMoveWrapper_);
|
|
Blockly.onMouseMoveWrapper_ = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse-move on SVG drawing surface.
|
|
* @param {!Event} e Mouse move event.
|
|
* @private
|
|
*/
|
|
Blockly.onMouseMove_ = function(e) {
|
|
if (e.touches && e.touches.length >= 2) {
|
|
return; // Multi-touch gestures won't have e.clientX.
|
|
}
|
|
var workspace = Blockly.getMainWorkspace();
|
|
if (workspace.isScrolling) {
|
|
Blockly.removeAllRanges();
|
|
var dx = e.clientX - workspace.startDragMouseX;
|
|
var dy = e.clientY - workspace.startDragMouseY;
|
|
var metrics = workspace.startDragMetrics;
|
|
var x = workspace.startScrollX + dx;
|
|
var y = workspace.startScrollY + dy;
|
|
x = Math.min(x, -metrics.contentLeft);
|
|
y = Math.min(y, -metrics.contentTop);
|
|
x = Math.max(x, metrics.viewWidth - metrics.contentLeft -
|
|
metrics.contentWidth);
|
|
y = Math.max(y, metrics.viewHeight - metrics.contentTop -
|
|
metrics.contentHeight);
|
|
|
|
// Move the scrollbars and the page will scroll automatically.
|
|
workspace.scrollbar.set(-x - metrics.contentLeft,
|
|
-y - metrics.contentTop);
|
|
// Cancel the long-press if the drag has moved too far.
|
|
if (Math.sqrt(dx * dx + dy * dy) > Blockly.DRAG_RADIUS) {
|
|
Blockly.longStop_();
|
|
}
|
|
e.stopPropagation();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle a key-down on SVG drawing surface.
|
|
* @param {!Event} e Key down event.
|
|
* @private
|
|
*/
|
|
Blockly.onKeyDown_ = function(e) {
|
|
if (Blockly.isTargetInput_(e)) {
|
|
// When focused on an HTML text input widget, don't trap any keys.
|
|
return;
|
|
}
|
|
var deleteBlock = false;
|
|
if (e.keyCode == 27) {
|
|
// Pressing esc closes the context menu.
|
|
Blockly.hideChaff();
|
|
} else if (e.keyCode == 8 || e.keyCode == 46) {
|
|
// Delete or backspace.
|
|
try {
|
|
if (Blockly.selected && Blockly.selected.isDeletable()) {
|
|
deleteBlock = true;
|
|
}
|
|
} finally {
|
|
// Stop the browser from going back to the previous page.
|
|
// Use a finally so that any error in delete code above doesn't disappear
|
|
// from the console when the page rolls back.
|
|
e.preventDefault();
|
|
}
|
|
} else if (e.altKey || e.ctrlKey || e.metaKey) {
|
|
if (Blockly.selected &&
|
|
Blockly.selected.isDeletable() && Blockly.selected.isMovable()) {
|
|
if (e.keyCode == 67) {
|
|
// 'c' for copy.
|
|
Blockly.hideChaff();
|
|
Blockly.copy_(Blockly.selected);
|
|
} else if (e.keyCode == 88) {
|
|
// 'x' for cut.
|
|
Blockly.copy_(Blockly.selected);
|
|
deleteBlock = true;
|
|
}
|
|
}
|
|
if (e.keyCode == 86) {
|
|
// 'v' for paste.
|
|
if (Blockly.clipboardXml_) {
|
|
Blockly.clipboardSource_.paste(Blockly.clipboardXml_);
|
|
}
|
|
}
|
|
}
|
|
if (deleteBlock) {
|
|
// Common code for delete and cut.
|
|
Blockly.hideChaff();
|
|
var heal = Blockly.dragMode_ != 2;
|
|
Blockly.selected.dispose(heal, true);
|
|
if (Blockly.highlightedConnection_) {
|
|
Blockly.highlightedConnection_.unhighlight();
|
|
Blockly.highlightedConnection_ = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Stop binding to the global mouseup and mousemove events.
|
|
* @private
|
|
*/
|
|
Blockly.terminateDrag_ = function() {
|
|
Blockly.BlockSvg.terminateDrag_();
|
|
Blockly.Flyout.terminateDrag_();
|
|
};
|
|
|
|
/**
|
|
* PID of queued long-press task.
|
|
* @private
|
|
*/
|
|
Blockly.longPid_ = 0;
|
|
|
|
/**
|
|
* Context menus on touch devices are activated using a long-press.
|
|
* Unfortunately the contextmenu touch event is currently (2015) only suported
|
|
* by Chrome. This function is fired on any touchstart event, queues a task,
|
|
* which after about a second opens the context menu. The tasks is killed
|
|
* if the touch event terminates early.
|
|
* @param {!Event} e Touch start event.
|
|
* @param {!Blockly.Block|!Blockly.WorkspaceSvg} uiObject The block or workspace
|
|
* under the touchstart event.
|
|
* @private
|
|
*/
|
|
Blockly.longStart_ = function(e, uiObject) {
|
|
Blockly.longStop_();
|
|
Blockly.longPid_ = setTimeout(function() {
|
|
e.button = 2; // Simulate a right button click.
|
|
uiObject.onMouseDown_(e);
|
|
}, Blockly.LONGPRESS);
|
|
};
|
|
|
|
/**
|
|
* Nope, that's not a long-press. Either touchend or touchcancel was fired,
|
|
* or a drag hath begun. Kill the queued long-press task.
|
|
* @private
|
|
*/
|
|
Blockly.longStop_ = function() {
|
|
if (Blockly.longPid_) {
|
|
clearTimeout(Blockly.longPid_);
|
|
Blockly.longPid_ = 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Copy a block onto the local clipboard.
|
|
* @param {!Blockly.Block} block Block to be copied.
|
|
* @private
|
|
*/
|
|
Blockly.copy_ = function(block) {
|
|
var xmlBlock = Blockly.Xml.blockToDom(block);
|
|
if (Blockly.dragMode_ != 2) {
|
|
Blockly.Xml.deleteNext(xmlBlock);
|
|
}
|
|
// Encode start position in XML.
|
|
var xy = block.getRelativeToSurfaceXY();
|
|
xmlBlock.setAttribute('x', block.RTL ? -xy.x : xy.x);
|
|
xmlBlock.setAttribute('y', xy.y);
|
|
Blockly.clipboardXml_ = xmlBlock;
|
|
Blockly.clipboardSource_ = block.workspace;
|
|
};
|
|
|
|
/**
|
|
* Duplicate this block and its children.
|
|
* @param {!Blockly.Block} block Block to be copied.
|
|
* @private
|
|
*/
|
|
Blockly.duplicate_ = function(block) {
|
|
// Save the clipboard.
|
|
var clipboardXml = Blockly.clipboardXml_;
|
|
var clipboardSource = Blockly.clipboardSource_;
|
|
|
|
// Create a duplicate via a copy/paste operation.
|
|
Blockly.copy_(block);
|
|
block.workspace.paste(Blockly.clipboardXml_);
|
|
|
|
// Restore the clipboard.
|
|
Blockly.clipboardXml_ = clipboardXml;
|
|
Blockly.clipboardSource_ = clipboardSource;
|
|
};
|
|
|
|
/**
|
|
* Cancel the native context menu, unless the focus is on an HTML input widget.
|
|
* @param {!Event} e Mouse down event.
|
|
* @private
|
|
*/
|
|
Blockly.onContextMenu_ = function(e) {
|
|
if (!Blockly.isTargetInput_(e)) {
|
|
// When focused on an HTML text input widget, don't cancel the context menu.
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Close tooltips, context menus, dropdown selections, etc.
|
|
* @param {boolean=} opt_allowToolbox If true, don't close the toolbox.
|
|
*/
|
|
Blockly.hideChaff = function(opt_allowToolbox) {
|
|
Blockly.Tooltip.hide();
|
|
Blockly.WidgetDiv.hide();
|
|
if (!opt_allowToolbox) {
|
|
var workspace = Blockly.getMainWorkspace();
|
|
if (workspace.toolbox_ &&
|
|
workspace.toolbox_.flyout_ &&
|
|
workspace.toolbox_.flyout_.autoClose) {
|
|
workspace.toolbox_.clearSelection();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Return an object with all the metrics required to size scrollbars for the
|
|
* main workspace. The following properties are computed:
|
|
* .viewHeight: Height of the visible rectangle,
|
|
* .viewWidth: Width of the visible rectangle,
|
|
* .contentHeight: Height of the contents,
|
|
* .contentWidth: Width of the content,
|
|
* .viewTop: Offset of top edge of visible rectangle from parent,
|
|
* .viewLeft: Offset of left edge of visible rectangle from parent,
|
|
* .contentTop: Offset of the top-most content from the y=0 coordinate,
|
|
* .contentLeft: Offset of the left-most content from the x=0 coordinate.
|
|
* .absoluteTop: Top-edge of view.
|
|
* .absoluteLeft: Left-edge of view.
|
|
* @return {Object} Contains size and position metrics of main workspace.
|
|
* @private
|
|
* @this Blockly.WorkspaceSvg
|
|
*/
|
|
Blockly.getMainWorkspaceMetrics_ = function() {
|
|
var svgSize = Blockly.svgSize(this.getParentSvg());
|
|
if (this.toolbox_) {
|
|
svgSize.width -= this.toolbox_.width;
|
|
}
|
|
// Set the margin to match the flyout's margin so that the workspace does
|
|
// not jump as blocks are added.
|
|
var MARGIN = Blockly.Flyout.prototype.CORNER_RADIUS - 1;
|
|
var viewWidth = svgSize.width - MARGIN;
|
|
var viewHeight = svgSize.height - MARGIN;
|
|
var blockBox = this.getBlocksBoundingBox();
|
|
|
|
// Fix scale.
|
|
var contentWidth = blockBox.width * this.scale;
|
|
var contentHeight = blockBox.height * this.scale;
|
|
var contentX = blockBox.x * this.scale;
|
|
var contentY = blockBox.y * this.scale;
|
|
if (this.scrollbar) {
|
|
// Add a border around the content that is at least half a screenful wide.
|
|
// Ensure border is wide enough that blocks can scroll over entire screen.
|
|
var leftEdge = Math.min(contentX - viewWidth / 2,
|
|
contentX + contentWidth - viewWidth);
|
|
var rightEdge = Math.max(contentX + contentWidth + viewWidth / 2,
|
|
contentX + viewWidth);
|
|
var topEdge = Math.min(contentY - viewHeight / 2,
|
|
contentY + contentHeight - viewHeight);
|
|
var bottomEdge = Math.max(contentY + contentHeight + viewHeight / 2,
|
|
contentY + viewHeight);
|
|
} else {
|
|
var leftEdge = blockBox.x;
|
|
var rightEdge = leftEdge + blockBox.width;
|
|
var topEdge = blockBox.y;
|
|
var bottomEdge = topEdge + blockBox.height;
|
|
}
|
|
var absoluteLeft = 0;
|
|
if (!this.RTL && this.toolbox_) {
|
|
absoluteLeft = this.toolbox_.width;
|
|
}
|
|
var metrics = {
|
|
viewHeight: svgSize.height,
|
|
viewWidth: svgSize.width,
|
|
contentHeight: bottomEdge - topEdge,
|
|
contentWidth: rightEdge - leftEdge,
|
|
viewTop: -this.scrollY,
|
|
viewLeft: -this.scrollX,
|
|
contentTop: topEdge,
|
|
contentLeft: leftEdge,
|
|
absoluteTop: 0,
|
|
absoluteLeft: absoluteLeft
|
|
};
|
|
return metrics;
|
|
};
|
|
|
|
/**
|
|
* Sets the X/Y translations of the main workspace to match the scrollbars.
|
|
* @param {!Object} xyRatio Contains an x and/or y property which is a float
|
|
* between 0 and 1 specifying the degree of scrolling.
|
|
* @private
|
|
* @this Blockly.WorkspaceSvg
|
|
*/
|
|
Blockly.setMainWorkspaceMetrics_ = function(xyRatio) {
|
|
if (!this.scrollbar) {
|
|
throw 'Attempt to set main workspace scroll without scrollbars.';
|
|
}
|
|
var metrics = this.getMetrics();
|
|
if (goog.isNumber(xyRatio.x)) {
|
|
this.scrollX = -metrics.contentWidth * xyRatio.x - metrics.contentLeft;
|
|
}
|
|
if (goog.isNumber(xyRatio.y)) {
|
|
this.scrollY = -metrics.contentHeight * xyRatio.y - metrics.contentTop;
|
|
}
|
|
var x = this.scrollX + metrics.absoluteLeft;
|
|
var y = this.scrollY + metrics.absoluteTop;
|
|
this.translate(x, y);
|
|
if (this.options.gridPattern) {
|
|
this.options.gridPattern.setAttribute('x', x);
|
|
this.options.gridPattern.setAttribute('y', y);
|
|
if (goog.userAgent.IE) {
|
|
// IE doesn't notice that the x/y offsets have changed. Force an update.
|
|
this.updateGridPattern_();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* When something in Blockly's workspace changes, call a function.
|
|
* @param {!Function} func Function to call.
|
|
* @return {!Array.<!Array>} Opaque data that can be passed to
|
|
* removeChangeListener.
|
|
* @deprecated April 2015
|
|
*/
|
|
Blockly.addChangeListener = function(func) {
|
|
// Backwards compatability from before there could be multiple workspaces.
|
|
console.warn('Deprecated call to Blockly.addChangeListener, ' +
|
|
'use workspace.addChangeListener instead.');
|
|
return Blockly.getMainWorkspace().addChangeListener(func);
|
|
};
|
|
|
|
/**
|
|
* Returns the main workspace. Returns the last used main workspace (based on
|
|
* focus).
|
|
* @return {!Blockly.Workspace} The main workspace.
|
|
*/
|
|
Blockly.getMainWorkspace = function() {
|
|
return Blockly.mainWorkspace;
|
|
};
|
|
|
|
// Export symbols that would otherwise be renamed by Closure compiler.
|
|
if (!goog.global['Blockly']) {
|
|
goog.global['Blockly'] = {};
|
|
}
|
|
goog.global['Blockly']['getMainWorkspace'] = Blockly.getMainWorkspace;
|
|
goog.global['Blockly']['addChangeListener'] = Blockly.addChangeListener;
|