diff --git a/core/blockly.js.orig b/core/blockly.js.orig
deleted file mode 100644
index fb6562ed2..000000000
--- a/core/blockly.js.orig
+++ /dev/null
@@ -1,453 +0,0 @@
-/**
- * @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.FieldNumber');
-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.constants');
-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};
-
-/**
- * The main workspace most recently used.
- * Set by Blockly.WorkspaceSvg.prototype.markFocused
- * @type {Blockly.Workspace}
- */
-Blockly.mainWorkspace = null;
-
-/**
- * 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;
-
-/**
- * All of the connections on blocks that are currently being dragged.
- * @type {!Array.}
- * @private
- */
-Blockly.draggingConnections_ = [];
-
-/**
- * Contents of the local clipboard.
- * @type {Element}
- * @private
- */
-Blockly.clipboardXml_ = null;
-
-/**
- * Source of the local clipboard.
- * @type {Blockly.WorkspaceSvg}
- * @private
- */
-Blockly.clipboardSource_ = null;
-
-/**
- * Is the mouse dragging a block?
- * DRAG_NONE - No drag operation.
- * DRAG_STICKY - Still inside the sticky DRAG_RADIUS.
- * DRAG_FREE - Freely draggable.
- * @private
- */
-Blockly.dragMode_ = Blockly.DRAG_NONE;
-
-/**
- * Wrapper function called when a touch mouseUp occurs during a drag operation.
- * @type {Array.}
- * @private
- */
-Blockly.onTouchUpWrapper_ = null;
-
-/**
- * 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);
-};
-
-/**
- * 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 workspace when the contents change. This also updates
- * scrollbars accordingly.
- * @param {!Blockly.WorkspaceSvg} workspace The workspace to resize.
- */
-Blockly.resizeSvgContents = function(workspace) {
- workspace.resizeContents();
-};
-
-/**
- * Size the SVG image to completely fill its container. Call this when the view
- * actually changes sizes (e.g. on a window resize/device orientation change).
- * See Blockly.resizeSvgContents to resize the workspace when the contents
- * change (e.g. when a block is added or removed).
- * 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 deleted, 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.dragMode_ = Blockly.DRAG_NONE;
- // 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.dragMode_ != Blockly.DRAG_NONE) {
- 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_();
- workspace.dragMode_ = Blockly.DRAG_FREE;
- }
- e.stopPropagation();
- e.preventDefault();
- }
-};
-
-/**
- * Handle a key-down on SVG drawing surface.
- * @param {!Event} e Key down event.
- * @private
- */
-Blockly.onKeyDown_ = function(e) {
- if (Blockly.mainWorkspace.options.readOnly || Blockly.isTargetInput_(e)) {
- // No key actions on readonly workspaces.
- // 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.
- // Stop the browser from going back to the previous page.
- // Do this first to prevent an error in the delete code from resulting in
- // data loss.
- e.preventDefault();
- if (Blockly.selected && Blockly.selected.isDeletable()) {
- deleteBlock = true;
- }
- } 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.Events.setGroup(true);
- Blockly.clipboardSource_.paste(Blockly.clipboardXml_);
- Blockly.Events.setGroup(false);
- }
- } else if (e.keyCode == 90) {
- // 'z' for undo 'Z' is for redo.
- Blockly.hideChaff();
- Blockly.mainWorkspace.undo(e.shiftKey);
- }
- }
- if (deleteBlock) {
- // Common code for delete and cut.
- Blockly.Events.setGroup(true);
- Blockly.hideChaff();
- var heal = Blockly.dragMode_ != Blockly.DRAG_FREE;
- Blockly.selected.dispose(heal, true);
- if (Blockly.highlightedConnection_) {
- Blockly.highlightedConnection_.unhighlight();
- Blockly.highlightedConnection_ = null;
- }
- Blockly.Events.setGroup(false);
- }
-};
-
-/**
- * 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_ != Blockly.DRAG_FREE) {
- 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();
- }
- }
-};
-
-/**
- * When something in Blockly's workspace changes, call a function.
- * @param {!Function} func Function to call.
- * @return {!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). Try not to use this function, particularly if there are multiple
- * Blockly instances on a page.
- * @return {!Blockly.Workspace} The main workspace.
- */
-Blockly.getMainWorkspace = function() {
- return Blockly.mainWorkspace;
-};
-
-// IE9 does not have a console. Create a stub to stop errors.
-if (!goog.global['console']) {
- goog.global['console'] = {
- 'log': function() {},
- 'warn': function() {}
- };
-}
-
-// 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;
diff --git a/core/blocks.js.orig b/core/blocks.js.orig
deleted file mode 100644
index d6932ceb9..000000000
--- a/core/blocks.js.orig
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2013 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 Empty name space for the Blocks singleton.
- * @author spertus@google.com (Ellen Spertus)
- */
-'use strict';
-
-goog.provide('Blockly.Blocks');
-
-/**
- * Allow for switching between one and zero based indexing for lists and text,
- * one based by default.
- */
-Blockly.Blocks.ONE_BASED_INDEXING = true;
diff --git a/core/bubble.js.orig b/core/bubble.js.orig
deleted file mode 100644
index d4c1e2719..000000000
--- a/core/bubble.js.orig
+++ /dev/null
@@ -1,579 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Object representing a UI bubble.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Bubble');
-
-goog.require('Blockly.Workspace');
-goog.require('goog.dom');
-goog.require('goog.math');
-goog.require('goog.math.Coordinate');
-goog.require('goog.userAgent');
-
-
-/**
- * Class for UI bubble.
- * @param {!Blockly.WorkspaceSvg} workspace The workspace on which to draw the
- * bubble.
- * @param {!Element} content SVG content for the bubble.
- * @param {Element} shape SVG element to avoid eclipsing.
- * @param {!goog.math.Coodinate} anchorXY Absolute position of bubble's anchor
- * point.
- * @param {?number} bubbleWidth Width of bubble, or null if not resizable.
- * @param {?number} bubbleHeight Height of bubble, or null if not resizable.
- * @constructor
- */
-Blockly.Bubble = function(workspace, content, shape, anchorXY,
- bubbleWidth, bubbleHeight) {
- this.workspace_ = workspace;
- this.content_ = content;
- this.shape_ = shape;
-
- var angle = Blockly.Bubble.ARROW_ANGLE;
- if (this.workspace_.RTL) {
- angle = -angle;
- }
- this.arrow_radians_ = goog.math.toRadians(angle);
-
- var canvas = workspace.getBubbleCanvas();
- canvas.appendChild(this.createDom_(content, !!(bubbleWidth && bubbleHeight)));
-
- this.setAnchorLocation(anchorXY);
- if (!bubbleWidth || !bubbleHeight) {
- var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox();
- bubbleWidth = bBox.width + 2 * Blockly.Bubble.BORDER_WIDTH;
- bubbleHeight = bBox.height + 2 * Blockly.Bubble.BORDER_WIDTH;
- }
- this.setBubbleSize(bubbleWidth, bubbleHeight);
-
- // Render the bubble.
- this.positionBubble_();
- this.renderArrow_();
- this.rendered_ = true;
-
- if (!workspace.options.readOnly) {
- Blockly.bindEvent_(this.bubbleBack_, 'mousedown', this,
- this.bubbleMouseDown_);
- if (this.resizeGroup_) {
- Blockly.bindEvent_(this.resizeGroup_, 'mousedown', this,
- this.resizeMouseDown_);
- }
- }
-};
-
-/**
- * Width of the border around the bubble.
- */
-Blockly.Bubble.BORDER_WIDTH = 6;
-
-/**
- * Determines the thickness of the base of the arrow in relation to the size
- * of the bubble. Higher numbers result in thinner arrows.
- */
-Blockly.Bubble.ARROW_THICKNESS = 10;
-
-/**
- * The number of degrees that the arrow bends counter-clockwise.
- */
-Blockly.Bubble.ARROW_ANGLE = 20;
-
-/**
- * The sharpness of the arrow's bend. Higher numbers result in smoother arrows.
- */
-Blockly.Bubble.ARROW_BEND = 4;
-
-/**
- * Distance between arrow point and anchor point.
- */
-Blockly.Bubble.ANCHOR_RADIUS = 8;
-
-/**
- * Wrapper function called when a mouseUp occurs during a drag operation.
- * @type {Array.}
- * @private
- */
-Blockly.Bubble.onMouseUpWrapper_ = null;
-
-/**
- * Wrapper function called when a mouseMove occurs during a drag operation.
- * @type {Array.}
- * @private
- */
-Blockly.Bubble.onMouseMoveWrapper_ = null;
-
-/**
- * Function to call on resize of bubble.
- * @type {Function}
- */
-Blockly.Bubble.prototype.resizeCallback_ = null;
-
-/**
- * Stop binding to the global mouseup and mousemove events.
- * @private
- */
-Blockly.Bubble.unbindDragEvents_ = function() {
- if (Blockly.Bubble.onMouseUpWrapper_) {
- Blockly.unbindEvent_(Blockly.Bubble.onMouseUpWrapper_);
- Blockly.Bubble.onMouseUpWrapper_ = null;
- }
- if (Blockly.Bubble.onMouseMoveWrapper_) {
- Blockly.unbindEvent_(Blockly.Bubble.onMouseMoveWrapper_);
- Blockly.Bubble.onMouseMoveWrapper_ = null;
- }
-};
-
-/**
- * Flag to stop incremental rendering during construction.
- * @private
- */
-Blockly.Bubble.prototype.rendered_ = false;
-
-/**
- * Absolute coordinate of anchor point.
- * @type {goog.math.Coordinate}
- * @private
- */
-Blockly.Bubble.prototype.anchorXY_ = null;
-
-/**
- * Relative X coordinate of bubble with respect to the anchor's centre.
- * In RTL mode the initial value is negated.
- * @private
- */
-Blockly.Bubble.prototype.relativeLeft_ = 0;
-
-/**
- * Relative Y coordinate of bubble with respect to the anchor's centre.
- * @private
- */
-Blockly.Bubble.prototype.relativeTop_ = 0;
-
-/**
- * Width of bubble.
- * @private
- */
-Blockly.Bubble.prototype.width_ = 0;
-
-/**
- * Height of bubble.
- * @private
- */
-Blockly.Bubble.prototype.height_ = 0;
-
-/**
- * Automatically position and reposition the bubble.
- * @private
- */
-Blockly.Bubble.prototype.autoLayout_ = true;
-
-/**
- * Create the bubble's DOM.
- * @param {!Element} content SVG content for the bubble.
- * @param {boolean} hasResize Add diagonal resize gripper if true.
- * @return {!Element} The bubble's SVG group.
- * @private
- */
-Blockly.Bubble.prototype.createDom_ = function(content, hasResize) {
- /* Create the bubble. Here's the markup that will be generated:
-
-
-
-
-
-
-
-
-
-
- [...content goes here...]
-
- */
- this.bubbleGroup_ = Blockly.createSvgElement('g', {}, null);
- var filter =
- {'filter': 'url(#' + this.workspace_.options.embossFilterId + ')'};
- if (goog.userAgent.getUserAgentString().indexOf('JavaFX') != -1) {
- // Multiple reports that JavaFX can't handle filters. UserAgent:
- // Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.44
- // (KHTML, like Gecko) JavaFX/8.0 Safari/537.44
- // https://github.com/google/blockly/issues/99
- filter = {};
- }
- var bubbleEmboss = Blockly.createSvgElement('g',
- filter, this.bubbleGroup_);
- this.bubbleArrow_ = Blockly.createSvgElement('path', {}, bubbleEmboss);
- this.bubbleBack_ = Blockly.createSvgElement('rect',
- {'class': 'blocklyDraggable', 'x': 0, 'y': 0,
- 'rx': Blockly.Bubble.BORDER_WIDTH, 'ry': Blockly.Bubble.BORDER_WIDTH},
- bubbleEmboss);
- if (hasResize) {
- this.resizeGroup_ = Blockly.createSvgElement('g',
- {'class': this.workspace_.RTL ?
- 'blocklyResizeSW' : 'blocklyResizeSE'},
- this.bubbleGroup_);
- var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH;
- Blockly.createSvgElement('polygon',
- {'points': '0,x x,x x,0'.replace(/x/g, resizeSize.toString())},
- this.resizeGroup_);
- Blockly.createSvgElement('line',
- {'class': 'blocklyResizeLine',
- 'x1': resizeSize / 3, 'y1': resizeSize - 1,
- 'x2': resizeSize - 1, 'y2': resizeSize / 3}, this.resizeGroup_);
- Blockly.createSvgElement('line',
- {'class': 'blocklyResizeLine',
- 'x1': resizeSize * 2 / 3, 'y1': resizeSize - 1,
- 'x2': resizeSize - 1, 'y2': resizeSize * 2 / 3}, this.resizeGroup_);
- } else {
- this.resizeGroup_ = null;
- }
- this.bubbleGroup_.appendChild(content);
- return this.bubbleGroup_;
-};
-
-/**
- * Handle a mouse-down on bubble's border.
- * @param {!Event} e Mouse down event.
- * @private
- */
-Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) {
- this.promote_();
- Blockly.Bubble.unbindDragEvents_();
- if (Blockly.isRightButton(e)) {
- // No right-click.
- e.stopPropagation();
- return;
- } else if (Blockly.isTargetInput_(e)) {
- // When focused on an HTML text input widget, don't trap any events.
- return;
- }
- // Left-click (or middle click)
- Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);
-
- this.workspace_.startDrag(e, new goog.math.Coordinate(
- this.workspace_.RTL ? -this.relativeLeft_ : this.relativeLeft_,
- this.relativeTop_));
-
- Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEvent_(document,
- 'mouseup', this, Blockly.Bubble.unbindDragEvents_);
- Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEvent_(document,
- 'mousemove', this, this.bubbleMouseMove_);
- Blockly.hideChaff();
- // This event has been handled. No need to bubble up to the document.
- e.stopPropagation();
-};
-
-/**
- * Drag this bubble to follow the mouse.
- * @param {!Event} e Mouse move event.
- * @private
- */
-Blockly.Bubble.prototype.bubbleMouseMove_ = function(e) {
- this.autoLayout_ = false;
- var newXY = this.workspace_.moveDrag(e);
- this.relativeLeft_ = this.workspace_.RTL ? -newXY.x : newXY.x;
- this.relativeTop_ = newXY.y;
- this.positionBubble_();
- this.renderArrow_();
-};
-
-/**
- * Handle a mouse-down on bubble's resize corner.
- * @param {!Event} e Mouse down event.
- * @private
- */
-Blockly.Bubble.prototype.resizeMouseDown_ = function(e) {
- this.promote_();
- Blockly.Bubble.unbindDragEvents_();
- if (Blockly.isRightButton(e)) {
- // No right-click.
- e.stopPropagation();
- return;
- }
- // Left-click (or middle click)
- Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);
-
- this.workspace_.startDrag(e, new goog.math.Coordinate(
- this.workspace_.RTL ? -this.width_ : this.width_, this.height_));
-
- Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEvent_(document,
- 'mouseup', this, Blockly.Bubble.unbindDragEvents_);
- Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEvent_(document,
- 'mousemove', this, this.resizeMouseMove_);
- Blockly.hideChaff();
- // This event has been handled. No need to bubble up to the document.
- e.stopPropagation();
-};
-
-/**
- * Resize this bubble to follow the mouse.
- * @param {!Event} e Mouse move event.
- * @private
- */
-Blockly.Bubble.prototype.resizeMouseMove_ = function(e) {
- this.autoLayout_ = false;
- var newXY = this.workspace_.moveDrag(e);
- this.setBubbleSize(this.workspace_.RTL ? -newXY.x : newXY.x, newXY.y);
- if (this.workspace_.RTL) {
- // RTL requires the bubble to move its left edge.
- this.positionBubble_();
- }
-};
-
-/**
- * Register a function as a callback event for when the bubble is resized.
- * @param {!Function} callback The function to call on resize.
- */
-Blockly.Bubble.prototype.registerResizeEvent = function(callback) {
- this.resizeCallback_ = callback;
-};
-
-/**
- * Move this bubble to the top of the stack.
- * @private
- */
-Blockly.Bubble.prototype.promote_ = function() {
- var svgGroup = this.bubbleGroup_.parentNode;
- svgGroup.appendChild(this.bubbleGroup_);
-};
-
-/**
- * Notification that the anchor has moved.
- * Update the arrow and bubble accordingly.
- * @param {!goog.math.Coordinate} xy Absolute location.
- */
-Blockly.Bubble.prototype.setAnchorLocation = function(xy) {
- this.anchorXY_ = xy;
- if (this.rendered_) {
- this.positionBubble_();
- }
-};
-
-/**
- * Position the bubble so that it does not fall off-screen.
- * @private
- */
-Blockly.Bubble.prototype.layoutBubble_ = function() {
- // Compute the preferred bubble location.
- var relativeLeft = -this.width_ / 4;
- var relativeTop = -this.height_ - Blockly.BlockSvg.MIN_BLOCK_Y;
- // Prevent the bubble from being off-screen.
- var metrics = this.workspace_.getMetrics();
- metrics.viewWidth /= this.workspace_.scale;
- metrics.viewLeft /= this.workspace_.scale;
- var anchorX = this.anchorXY_.x;
- if (this.workspace_.RTL) {
- if (anchorX - metrics.viewLeft - relativeLeft - this.width_ <
- Blockly.Scrollbar.scrollbarThickness) {
- // Slide the bubble right until it is onscreen.
- relativeLeft = anchorX - metrics.viewLeft - this.width_ -
- Blockly.Scrollbar.scrollbarThickness;
- } else if (anchorX - metrics.viewLeft - relativeLeft >
- metrics.viewWidth) {
- // Slide the bubble left until it is onscreen.
- relativeLeft = anchorX - metrics.viewLeft - metrics.viewWidth;
- }
- } else {
- if (anchorX + relativeLeft < metrics.viewLeft) {
- // Slide the bubble right until it is onscreen.
- relativeLeft = metrics.viewLeft - anchorX;
- } else if (metrics.viewLeft + metrics.viewWidth <
- anchorX + relativeLeft + this.width_ +
- Blockly.BlockSvg.SEP_SPACE_X +
- Blockly.Scrollbar.scrollbarThickness) {
- // Slide the bubble left until it is onscreen.
- relativeLeft = metrics.viewLeft + metrics.viewWidth - anchorX -
- this.width_ - Blockly.Scrollbar.scrollbarThickness;
- }
- }
- if (this.anchorXY_.y + relativeTop < metrics.viewTop) {
- // Slide the bubble below the block.
- var bBox = /** @type {SVGLocatable} */ (this.shape_).getBBox();
- relativeTop = bBox.height;
- }
- this.relativeLeft_ = relativeLeft;
- this.relativeTop_ = relativeTop;
-};
-
-/**
- * Move the bubble to a location relative to the anchor's centre.
- * @private
- */
-Blockly.Bubble.prototype.positionBubble_ = function() {
- var left = this.anchorXY_.x;
- if (this.workspace_.RTL) {
- left -= this.relativeLeft_ + this.width_;
- } else {
- left += this.relativeLeft_;
- }
- var top = this.relativeTop_ + this.anchorXY_.y;
- this.bubbleGroup_.setAttribute('transform',
- 'translate(' + left + ',' + top + ')');
-};
-
-/**
- * Get the dimensions of this bubble.
- * @return {!Object} Object with width and height properties.
- */
-Blockly.Bubble.prototype.getBubbleSize = function() {
- return {width: this.width_, height: this.height_};
-};
-
-/**
- * Size this bubble.
- * @param {number} width Width of the bubble.
- * @param {number} height Height of the bubble.
- */
-Blockly.Bubble.prototype.setBubbleSize = function(width, height) {
- var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH;
- // Minimum size of a bubble.
- width = Math.max(width, doubleBorderWidth + 45);
- height = Math.max(height, doubleBorderWidth + 20);
- this.width_ = width;
- this.height_ = height;
- this.bubbleBack_.setAttribute('width', width);
- this.bubbleBack_.setAttribute('height', height);
- if (this.resizeGroup_) {
- if (this.workspace_.RTL) {
- // Mirror the resize group.
- var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH;
- this.resizeGroup_.setAttribute('transform', 'translate(' +
- resizeSize + ',' + (height - doubleBorderWidth) + ') scale(-1 1)');
- } else {
- this.resizeGroup_.setAttribute('transform', 'translate(' +
- (width - doubleBorderWidth) + ',' +
- (height - doubleBorderWidth) + ')');
- }
- }
- if (this.rendered_) {
- if (this.autoLayout_) {
- this.layoutBubble_();
- }
- this.positionBubble_();
- this.renderArrow_();
- }
- // Allow the contents to resize.
- if (this.resizeCallback_) {
- this.resizeCallback_();
- }
-};
-
-/**
- * Draw the arrow between the bubble and the origin.
- * @private
- */
-Blockly.Bubble.prototype.renderArrow_ = function() {
- var steps = [];
- // Find the relative coordinates of the center of the bubble.
- var relBubbleX = this.width_ / 2;
- var relBubbleY = this.height_ / 2;
- // Find the relative coordinates of the center of the anchor.
- var relAnchorX = -this.relativeLeft_;
- var relAnchorY = -this.relativeTop_;
- if (relBubbleX == relAnchorX && relBubbleY == relAnchorY) {
- // Null case. Bubble is directly on top of the anchor.
- // Short circuit this rather than wade through divide by zeros.
- steps.push('M ' + relBubbleX + ',' + relBubbleY);
- } else {
- // Compute the angle of the arrow's line.
- var rise = relAnchorY - relBubbleY;
- var run = relAnchorX - relBubbleX;
- if (this.workspace_.RTL) {
- run *= -1;
- }
- var hypotenuse = Math.sqrt(rise * rise + run * run);
- var angle = Math.acos(run / hypotenuse);
- if (rise < 0) {
- angle = 2 * Math.PI - angle;
- }
- // Compute a line perpendicular to the arrow.
- var rightAngle = angle + Math.PI / 2;
- if (rightAngle > Math.PI * 2) {
- rightAngle -= Math.PI * 2;
- }
- var rightRise = Math.sin(rightAngle);
- var rightRun = Math.cos(rightAngle);
-
- // Calculate the thickness of the base of the arrow.
- var bubbleSize = this.getBubbleSize();
- var thickness = (bubbleSize.width + bubbleSize.height) /
- Blockly.Bubble.ARROW_THICKNESS;
- thickness = Math.min(thickness, bubbleSize.width, bubbleSize.height) / 2;
-
- // Back the tip of the arrow off of the anchor.
- var backoffRatio = 1 - Blockly.Bubble.ANCHOR_RADIUS / hypotenuse;
- relAnchorX = relBubbleX + backoffRatio * run;
- relAnchorY = relBubbleY + backoffRatio * rise;
-
- // Coordinates for the base of the arrow.
- var baseX1 = relBubbleX + thickness * rightRun;
- var baseY1 = relBubbleY + thickness * rightRise;
- var baseX2 = relBubbleX - thickness * rightRun;
- var baseY2 = relBubbleY - thickness * rightRise;
-
- // Distortion to curve the arrow.
- var swirlAngle = angle + this.arrow_radians_;
- if (swirlAngle > Math.PI * 2) {
- swirlAngle -= Math.PI * 2;
- }
- var swirlRise = Math.sin(swirlAngle) *
- hypotenuse / Blockly.Bubble.ARROW_BEND;
- var swirlRun = Math.cos(swirlAngle) *
- hypotenuse / Blockly.Bubble.ARROW_BEND;
-
- steps.push('M' + baseX1 + ',' + baseY1);
- steps.push('C' + (baseX1 + swirlRun) + ',' + (baseY1 + swirlRise) +
- ' ' + relAnchorX + ',' + relAnchorY +
- ' ' + relAnchorX + ',' + relAnchorY);
- steps.push('C' + relAnchorX + ',' + relAnchorY +
- ' ' + (baseX2 + swirlRun) + ',' + (baseY2 + swirlRise) +
- ' ' + baseX2 + ',' + baseY2);
- }
- steps.push('z');
- this.bubbleArrow_.setAttribute('d', steps.join(' '));
-};
-
-/**
- * Change the colour of a bubble.
- * @param {string} hexColour Hex code of colour.
- */
-Blockly.Bubble.prototype.setColour = function(hexColour) {
- this.bubbleBack_.setAttribute('fill', hexColour);
- this.bubbleArrow_.setAttribute('fill', hexColour);
-};
-
-/**
- * Dispose of this bubble.
- */
-Blockly.Bubble.prototype.dispose = function() {
- Blockly.Bubble.unbindDragEvents_();
- // Dispose of and unlink the bubble.
- goog.dom.removeNode(this.bubbleGroup_);
- this.bubbleGroup_ = null;
- this.bubbleArrow_ = null;
- this.bubbleBack_ = null;
- this.resizeGroup_ = null;
- this.workspace_ = null;
- this.content_ = null;
- this.shape_ = null;
-};
diff --git a/core/connection.js.orig b/core/connection.js.orig
deleted file mode 100644
index 3b8c82af8..000000000
--- a/core/connection.js.orig
+++ /dev/null
@@ -1,615 +0,0 @@
-/**
- * @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 Components for creating connections between blocks.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Connection');
-
-goog.require('goog.asserts');
-goog.require('goog.dom');
-
-
-/**
- * Class for a connection between blocks.
- * @param {!Blockly.Block} source The block establishing this connection.
- * @param {number} type The type of the connection.
- * @constructor
- */
-Blockly.Connection = function(source, type) {
- /**
- * @type {!Blockly.Block}
- * @private
- */
- this.sourceBlock_ = source;
- /** @type {number} */
- this.type = type;
- // Shortcut for the databases for this connection's workspace.
- if (source.workspace.connectionDBList) {
- this.db_ = source.workspace.connectionDBList[type];
- this.dbOpposite_ =
- source.workspace.connectionDBList[Blockly.OPPOSITE_TYPE[type]];
- this.hidden_ = !this.db_;
- }
-};
-
-/**
- * Constants for checking whether two connections are compatible.
- */
-Blockly.Connection.CAN_CONNECT = 0;
-Blockly.Connection.REASON_SELF_CONNECTION = 1;
-Blockly.Connection.REASON_WRONG_TYPE = 2;
-Blockly.Connection.REASON_TARGET_NULL = 3;
-Blockly.Connection.REASON_CHECKS_FAILED = 4;
-Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5;
-Blockly.Connection.REASON_SHADOW_PARENT = 6;
-
-/**
- * Connection this connection connects to. Null if not connected.
- * @type {Blockly.Connection}
- */
-Blockly.Connection.prototype.targetConnection = null;
-
-/**
- * List of compatible value types. Null if all types are compatible.
- * @type {Array}
- * @private
- */
-Blockly.Connection.prototype.check_ = null;
-
-/**
- * DOM representation of a shadow block, or null if none.
- * @type {Element}
- * @private
- */
-Blockly.Connection.prototype.shadowDom_ = null;
-
-/**
- * Horizontal location of this connection.
- * @type {number}
- * @private
- */
-Blockly.Connection.prototype.x_ = 0;
-
-/**
- * Vertical location of this connection.
- * @type {number}
- * @private
- */
-Blockly.Connection.prototype.y_ = 0;
-
-/**
- * Has this connection been added to the connection database?
- * @type {boolean}
- * @private
- */
-Blockly.Connection.prototype.inDB_ = false;
-
-/**
- * Connection database for connections of this type on the current workspace.
- * @type {Blockly.ConnectionDB}
- * @private
- */
-Blockly.Connection.prototype.db_ = null;
-
-/**
- * Connection database for connections compatible with this type on the
- * current workspace.
- * @type {Blockly.ConnectionDB}
- * @private
- */
-Blockly.Connection.prototype.dbOpposite_ = null;
-
-/**
- * Whether this connections is hidden (not tracked in a database) or not.
- * @type {boolean}
- * @private
- */
-Blockly.Connection.prototype.hidden_ = null;
-
-/**
- * Connect two connections together. This is the connection on the superior
- * block.
- * @param {!Blockly.Connection} childConnection Connection on inferior block.
- * @private
- */
-Blockly.Connection.prototype.connect_ = function(childConnection) {
- var parentConnection = this;
- var parentBlock = parentConnection.getSourceBlock();
- var childBlock = childConnection.getSourceBlock();
- // Disconnect any existing parent on the child connection.
- if (childConnection.isConnected()) {
- childConnection.disconnect();
- }
- if (parentConnection.isConnected()) {
- // Other connection is already connected to something.
- // Disconnect it and reattach it or bump it as needed.
- var orphanBlock = parentConnection.targetBlock();
- var shadowDom = parentConnection.getShadowDom();
- // Temporarily set the shadow DOM to null so it does not respawn.
- parentConnection.setShadowDom(null);
- // Displaced shadow blocks dissolve rather than reattaching or bumping.
- if (orphanBlock.isShadow()) {
- // Save the shadow block so that field values are preserved.
- shadowDom = Blockly.Xml.blockToDom(orphanBlock);
- orphanBlock.dispose();
- orphanBlock = null;
- } else if (parentConnection.type == Blockly.INPUT_VALUE) {
- // Value connections.
- // If female block is already connected, disconnect and bump the male.
- if (!orphanBlock.outputConnection) {
- throw 'Orphan block does not have an output connection.';
- }
- // Attempt to reattach the orphan at the end of the newly inserted
- // block. Since this block may be a row, walk down to the end
- // or to the first (and only) shadow block.
- var connection = Blockly.Connection.lastConnectionInRow_(
- childBlock, orphanBlock);
- if (connection) {
- orphanBlock.outputConnection.connect(connection);
- orphanBlock = null;
- }
- } else if (parentConnection.type == Blockly.NEXT_STATEMENT) {
- // Statement connections.
- // Statement blocks may be inserted into the middle of a stack.
- // Split the stack.
- if (!orphanBlock.previousConnection) {
- throw 'Orphan block does not have a previous connection.';
- }
- // Attempt to reattach the orphan at the bottom of the newly inserted
- // block. Since this block may be a stack, walk down to the end.
- var newBlock = childBlock;
- while (newBlock.nextConnection) {
- var nextBlock = newBlock.getNextBlock();
- if (nextBlock && !nextBlock.isShadow()) {
- newBlock = nextBlock;
- } else {
- if (orphanBlock.previousConnection.checkType_(
- newBlock.nextConnection)) {
- newBlock.nextConnection.connect(orphanBlock.previousConnection);
- orphanBlock = null;
- }
- break;
- }
- }
- }
- if (orphanBlock) {
- // Unable to reattach orphan.
- parentConnection.disconnect();
- if (Blockly.Events.recordUndo) {
- // Bump it off to the side after a moment.
- var group = Blockly.Events.getGroup();
- setTimeout(function() {
- // Verify orphan hasn't been deleted or reconnected (user on meth).
- if (orphanBlock.workspace && !orphanBlock.getParent()) {
- Blockly.Events.setGroup(group);
- if (orphanBlock.outputConnection) {
- orphanBlock.outputConnection.bumpAwayFrom_(parentConnection);
- } else if (orphanBlock.previousConnection) {
- orphanBlock.previousConnection.bumpAwayFrom_(parentConnection);
- }
- Blockly.Events.setGroup(false);
- }
- }, Blockly.BUMP_DELAY);
- }
- }
- // Restore the shadow DOM.
- parentConnection.setShadowDom(shadowDom);
- }
-
- var event;
- if (Blockly.Events.isEnabled()) {
- event = new Blockly.Events.Move(childBlock);
- }
- // Establish the connections.
- Blockly.Connection.connectReciprocally_(parentConnection, childConnection);
- // Demote the inferior block so that one is a child of the superior one.
- childBlock.setParent(parentBlock);
- if (event) {
- event.recordNew();
- Blockly.Events.fire(event);
- }
-};
-
-/**
- * Sever all links to this connection (not including from the source object).
- */
-Blockly.Connection.prototype.dispose = function() {
- if (this.isConnected()) {
- throw 'Disconnect connection before disposing of it.';
- }
- if (this.inDB_) {
- this.db_.removeConnection_(this);
- }
- if (Blockly.highlightedConnection_ == this) {
- Blockly.highlightedConnection_ = null;
- }
- if (Blockly.localConnection_ == this) {
- Blockly.localConnection_ = null;
- }
- this.db_ = null;
- this.dbOpposite_ = null;
-};
-
-/**
- * Get the source block for this connection.
- * @return {Blockly.Block} The source block, or null if there is none.
- */
-Blockly.Connection.prototype.getSourceBlock = function() {
- return this.sourceBlock_;
-};
-
-/**
- * Does the connection belong to a superior block (higher in the source stack)?
- * @return {boolean} True if connection faces down or right.
- */
-Blockly.Connection.prototype.isSuperior = function() {
- return this.type == Blockly.INPUT_VALUE ||
- this.type == Blockly.NEXT_STATEMENT;
-};
-
-/**
- * Is the connection connected?
- * @return {boolean} True if connection is connected to another connection.
- */
-Blockly.Connection.prototype.isConnected = function() {
- return !!this.targetConnection;
-};
-
-/**
- * Checks whether the current connection can connect with the target
- * connection.
- * @param {Blockly.Connection} target Connection to check compatibility with.
- * @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
- * an error code otherwise.
- * @private
- */
-Blockly.Connection.prototype.canConnectWithReason_ = function(target) {
- if (!target) {
- return Blockly.Connection.REASON_TARGET_NULL;
- }
- if (this.isSuperior()) {
- var blockA = this.sourceBlock_;
- var blockB = target.getSourceBlock();
- } else {
- var blockB = this.sourceBlock_;
- var blockA = target.getSourceBlock();
- }
- if (blockA && blockA == blockB) {
- return Blockly.Connection.REASON_SELF_CONNECTION;
- } else if (target.type != Blockly.OPPOSITE_TYPE[this.type]) {
- return Blockly.Connection.REASON_WRONG_TYPE;
- } else if (blockA && blockB && blockA.workspace !== blockB.workspace) {
- return Blockly.Connection.REASON_DIFFERENT_WORKSPACES;
- } else if (!this.checkType_(target)) {
- return Blockly.Connection.REASON_CHECKS_FAILED;
- } else if (blockA.isShadow() && !blockB.isShadow()) {
- return Blockly.Connection.REASON_SHADOW_PARENT;
- }
- return Blockly.Connection.CAN_CONNECT;
-};
-
-/**
- * Checks whether the current connection and target connection are compatible
- * and throws an exception if they are not.
- * @param {Blockly.Connection} target The connection to check compatibility
- * with.
- * @private
- */
-Blockly.Connection.prototype.checkConnection_ = function(target) {
- switch (this.canConnectWithReason_(target)) {
- case Blockly.Connection.CAN_CONNECT:
- break;
- case Blockly.Connection.REASON_SELF_CONNECTION:
- throw 'Attempted to connect a block to itself.';
- case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:
- // Usually this means one block has been deleted.
- throw 'Blocks not on same workspace.';
- case Blockly.Connection.REASON_WRONG_TYPE:
- throw 'Attempt to connect incompatible types.';
- case Blockly.Connection.REASON_TARGET_NULL:
- throw 'Target connection is null.';
- case Blockly.Connection.REASON_CHECKS_FAILED:
- throw 'Connection checks failed.';
- case Blockly.Connection.REASON_SHADOW_PARENT:
- throw 'Connecting non-shadow to shadow block.';
- default:
- throw 'Unknown connection failure: this should never happen!';
- }
-};
-
-/**
- * Check if the two connections can be dragged to connect to each other.
- * @param {!Blockly.Connection} candidate A nearby connection to check.
- * @return {boolean} True if the connection is allowed, false otherwise.
- */
-Blockly.Connection.prototype.isConnectionAllowed = function(candidate) {
- // Type checking.
- var canConnect = this.canConnectWithReason_(candidate);
- if (canConnect != Blockly.Connection.CAN_CONNECT) {
- return false;
- }
-
- // Don't offer to connect an already connected left (male) value plug to
- // an available right (female) value plug. Don't offer to connect the
- // bottom of a statement block to one that's already connected.
- if (candidate.type == Blockly.OUTPUT_VALUE ||
- candidate.type == Blockly.PREVIOUS_STATEMENT) {
- if (candidate.isConnected() || this.isConnected()) {
- return false;
- }
- }
-
- // Offering to connect the left (male) of a value block to an already
- // connected value pair is ok, we'll splice it in.
- // However, don't offer to splice into an immovable block.
- if (candidate.type == Blockly.INPUT_VALUE && candidate.isConnected() &&
- !candidate.targetBlock().isMovable() &&
- !candidate.targetBlock().isShadow()) {
- return false;
- }
-
- // Don't let a block with no next connection bump other blocks out of the
- // stack. But covering up a shadow block or stack of shadow blocks is fine.
- // Similarly, replacing a terminal statement with another terminal statement
- // is allowed.
- if (this.type == Blockly.PREVIOUS_STATEMENT &&
- candidate.isConnected() &&
- !this.sourceBlock_.nextConnection &&
- !candidate.targetBlock().isShadow() &&
- candidate.targetBlock().nextConnection) {
- return false;
- }
-
- // Don't let blocks try to connect to themselves or ones they nest.
- if (Blockly.draggingConnections_.indexOf(candidate) != -1) {
- return false;
- }
-
- return true;
-};
-
-/**
- * Connect this connection to another connection.
- * @param {!Blockly.Connection} otherConnection Connection to connect to.
- */
-Blockly.Connection.prototype.connect = function(otherConnection) {
- if (this.targetConnection == otherConnection) {
- // Already connected together. NOP.
- return;
- }
- this.checkConnection_(otherConnection);
- // Determine which block is superior (higher in the source stack).
- if (this.isSuperior()) {
- // Superior block.
- this.connect_(otherConnection);
- } else {
- // Inferior block.
- otherConnection.connect_(this);
- }
-};
-
-/**
- * Update two connections to target each other.
- * @param {Blockly.Connection} first The first connection to update.
- * @param {Blockly.Connection} second The second conneciton to update.
- * @private
- */
-Blockly.Connection.connectReciprocally_ = function(first, second) {
- goog.asserts.assert(first && second, 'Cannot connect null connections.');
- first.targetConnection = second;
- second.targetConnection = first;
-};
-
-/**
- * Does the given block have one and only one connection point that will accept
- * an orphaned block?
- * @param {!Blockly.Block} block The superior block.
- * @param {!Blockly.Block} orphanBlock The inferior block.
- * @return {Blockly.Connection} The suitable connection point on 'block',
- * or null.
- * @private
- */
-Blockly.Connection.singleConnection_ = function(block, orphanBlock) {
- var connection = false;
- for (var i = 0; i < block.inputList.length; i++) {
- var thisConnection = block.inputList[i].connection;
- if (thisConnection && thisConnection.type == Blockly.INPUT_VALUE &&
- orphanBlock.outputConnection.checkType_(thisConnection)) {
- if (connection) {
- return null; // More than one connection.
- }
- connection = thisConnection;
- }
- }
- return connection;
-};
-
-/**
- * Walks down a row a blocks, at each stage checking if there are any
- * connections that will accept the orphaned block. If at any point there
- * are zero or multiple eligible connections, returns null. Otherwise
- * returns the only input on the last block in the chain.
- * Terminates early for shadow blocks.
- * @param {!Blockly.Block} startBlock The block on which to start the search.
- * @param {!Blockly.Block} orphanBlock The block that is looking for a home.
- * @return {Blockly.Connection} The suitable connection point on the chain
- * of blocks, or null.
- * @private
- */
-Blockly.Connection.lastConnectionInRow_ = function(startBlock, orphanBlock) {
- var newBlock = startBlock;
- var connection;
- while (connection = Blockly.Connection.singleConnection_(
- /** @type {!Blockly.Block} */ (newBlock), orphanBlock)) {
- // '=' is intentional in line above.
- newBlock = connection.targetBlock();
- if (!newBlock || newBlock.isShadow()) {
- return connection;
- }
- }
- return null;
-};
-
-/**
- * Disconnect this connection.
- */
-Blockly.Connection.prototype.disconnect = function() {
- var otherConnection = this.targetConnection;
- goog.asserts.assert(otherConnection, 'Source connection not connected.');
- goog.asserts.assert(otherConnection.targetConnection == this,
- 'Target connection not connected to source connection.');
-
- var parentBlock, childBlock, parentConnection;
- if (this.isSuperior()) {
- // Superior block.
- parentBlock = this.sourceBlock_;
- childBlock = otherConnection.getSourceBlock();
- parentConnection = this;
- } else {
- // Inferior block.
- parentBlock = otherConnection.getSourceBlock();
- childBlock = this.sourceBlock_;
- parentConnection = otherConnection;
- }
- this.disconnectInternal_(parentBlock, childBlock);
- parentConnection.respawnShadow_();
-};
-
-/**
- * Disconnect two blocks that are connected by this connection.
- * @param {!Blockly.Block} parentBlock The superior block.
- * @param {!Blockly.Block} childBlock The inferior block.
- * @private
- */
-Blockly.Connection.prototype.disconnectInternal_ = function(parentBlock,
- childBlock) {
- var event;
- if (Blockly.Events.isEnabled()) {
- event = new Blockly.Events.Move(childBlock);
- }
- var otherConnection = this.targetConnection;
- otherConnection.targetConnection = null;
- this.targetConnection = null;
- childBlock.setParent(null);
- if (event) {
- event.recordNew();
- Blockly.Events.fire(event);
- }
-};
-
-/**
- * Respawn the shadow block if there was one connected to the this connection.
- * @private
- */
-Blockly.Connection.prototype.respawnShadow_ = function() {
- var parentBlock = this.getSourceBlock();
- var shadow = this.getShadowDom();
- if (parentBlock.workspace && shadow && Blockly.Events.recordUndo) {
- var blockShadow =
- Blockly.Xml.domToBlock(shadow, parentBlock.workspace);
- if (blockShadow.outputConnection) {
- this.connect(blockShadow.outputConnection);
- } else if (blockShadow.previousConnection) {
- this.connect(blockShadow.previousConnection);
- } else {
- throw 'Child block does not have output or previous statement.';
- }
- }
-};
-
-/**
- * Returns the block that this connection connects to.
- * @return {Blockly.Block} The connected block or null if none is connected.
- */
-Blockly.Connection.prototype.targetBlock = function() {
- if (this.isConnected()) {
- return this.targetConnection.getSourceBlock();
- }
- return null;
-};
-
-/**
- * Is this connection compatible with another connection with respect to the
- * value type system. E.g. square_root("Hello") is not compatible.
- * @param {!Blockly.Connection} otherConnection Connection to compare against.
- * @return {boolean} True if the connections share a type.
- * @private
- */
-Blockly.Connection.prototype.checkType_ = function(otherConnection) {
- if (!this.check_ || !otherConnection.check_) {
- // One or both sides are promiscuous enough that anything will fit.
- return true;
- }
- // Find any intersection in the check lists.
- for (var i = 0; i < this.check_.length; i++) {
- if (otherConnection.check_.indexOf(this.check_[i]) != -1) {
- return true;
- }
- }
- // No intersection.
- return false;
-};
-
-/**
- * Change a connection's compatibility.
- * @param {*} check Compatible value type or list of value types.
- * Null if all types are compatible.
- * @return {!Blockly.Connection} The connection being modified
- * (to allow chaining).
- */
-Blockly.Connection.prototype.setCheck = function(check) {
- if (check) {
- // Ensure that check is in an array.
- if (!goog.isArray(check)) {
- check = [check];
- }
- this.check_ = check;
- // The new value type may not be compatible with the existing connection.
- if (this.isConnected() && !this.checkType_(this.targetConnection)) {
- var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
- child.unplug();
- // Bump away.
- this.sourceBlock_.bumpNeighbours_();
- }
- } else {
- this.check_ = null;
- }
- return this;
-};
-
-/**
- * Change a connection's shadow block.
- * @param {Element} shadow DOM representation of a block or null.
- */
-Blockly.Connection.prototype.setShadowDom = function(shadow) {
- this.shadowDom_ = shadow;
-};
-
-/**
- * Return a connection's shadow block.
- * @return {Element} shadow DOM representation of a block or null.
- */
-Blockly.Connection.prototype.getShadowDom = function() {
- return this.shadowDom_;
-};
diff --git a/core/css.js.orig b/core/css.js.orig
deleted file mode 100644
index 0871a4fae..000000000
--- a/core/css.js.orig
+++ /dev/null
@@ -1,786 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2013 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 Inject Blockly's CSS synchronously.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Css');
-
-
-/**
- * List of cursors.
- * @enum {string}
- */
-Blockly.Css.Cursor = {
- OPEN: 'handopen',
- CLOSED: 'handclosed',
- DELETE: 'handdelete'
-};
-
-/**
- * Current cursor (cached value).
- * @type {string}
- * @private
- */
-Blockly.Css.currentCursor_ = '';
-
-/**
- * Large stylesheet added by Blockly.Css.inject.
- * @type {Element}
- * @private
- */
-Blockly.Css.styleSheet_ = null;
-
-/**
- * Path to media directory, with any trailing slash removed.
- * @type {string}
- * @private
- */
-Blockly.Css.mediaPath_ = '';
-
-/**
- * Inject the CSS into the DOM. This is preferable over using a regular CSS
- * file since:
- * a) It loads synchronously and doesn't force a redraw later.
- * b) It speeds up loading by not blocking on a separate HTTP transfer.
- * c) The CSS content may be made dynamic depending on init options.
- * @param {boolean} hasCss If false, don't inject CSS
- * (providing CSS becomes the document's responsibility).
- * @param {string} pathToMedia Path from page to the Blockly media directory.
- */
-Blockly.Css.inject = function(hasCss, pathToMedia) {
- // Only inject the CSS once.
- if (Blockly.Css.styleSheet_) {
- return;
- }
- // Placeholder for cursor rule. Must be first rule (index 0).
- var text = '.blocklyDraggable {}\n';
- if (hasCss) {
- text += Blockly.Css.CONTENT.join('\n');
- if (Blockly.FieldDate) {
- text += Blockly.FieldDate.CSS.join('\n');
- }
- }
- // Strip off any trailing slash (either Unix or Windows).
- Blockly.Css.mediaPath_ = pathToMedia.replace(/[\\\/]$/, '');
- text = text.replace(/<<>>/g, Blockly.Css.mediaPath_);
- // Inject CSS tag.
- var cssNode = document.createElement('style');
- document.head.appendChild(cssNode);
- var cssTextNode = document.createTextNode(text);
- cssNode.appendChild(cssTextNode);
- Blockly.Css.styleSheet_ = cssNode.sheet;
- Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
-};
-
-/**
- * Set the cursor to be displayed when over something draggable.
- * @param {Blockly.Css.Cursor} cursor Enum.
- */
-Blockly.Css.setCursor = function(cursor) {
- if (Blockly.Css.currentCursor_ == cursor) {
- return;
- }
- Blockly.Css.currentCursor_ = cursor;
- var url = 'url(' + Blockly.Css.mediaPath_ + '/' + cursor + '.cur), auto';
- // There are potentially hundreds of draggable objects. Changing their style
- // properties individually is too slow, so change the CSS rule instead.
- var rule = '.blocklyDraggable {\n cursor: ' + url + ';\n}\n';
- Blockly.Css.styleSheet_.deleteRule(0);
- Blockly.Css.styleSheet_.insertRule(rule, 0);
- // There is probably only one toolbox, so just change its style property.
- var toolboxen = document.getElementsByClassName('blocklyToolboxDiv');
- for (var i = 0, toolbox; toolbox = toolboxen[i]; i++) {
- if (cursor == Blockly.Css.Cursor.DELETE) {
- toolbox.style.cursor = url;
- } else {
- toolbox.style.cursor = '';
- }
- }
- // Set cursor on the whole document, so that rapid movements
- // don't result in cursor changing to an arrow momentarily.
- var html = document.body.parentNode;
- if (cursor == Blockly.Css.Cursor.OPEN) {
- html.style.cursor = '';
- } else {
- html.style.cursor = url;
- }
-};
-
-/**
- * Array making up the CSS content for Blockly.
- */
-Blockly.Css.CONTENT = [
- '.blocklySvg {',
- 'background-color: #fff;',
- 'outline: none;',
- 'overflow: hidden;', /* IE overflows by default. */
- 'display: block;',
- '}',
-
- '.blocklyWidgetDiv {',
- 'display: none;',
- 'position: absolute;',
- 'z-index: 99999;', /* big value for bootstrap3 compatibility */
- '}',
-
- '.injectionDiv {',
- 'height: 100%;',
- 'position: relative;',
- '}',
-
- '.blocklyNonSelectable {',
- 'user-select: none;',
- '-moz-user-select: none;',
- '-webkit-user-select: none;',
- '-ms-user-select: none;',
- '}',
-
- '.blocklyTooltipDiv {',
- 'background-color: #ffffc7;',
- 'border: 1px solid #ddc;',
- 'box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15);',
- 'color: #000;',
- 'display: none;',
- 'font-family: sans-serif;',
- 'font-size: 9pt;',
- 'opacity: 0.9;',
- 'padding: 2px;',
- 'position: absolute;',
- 'z-index: 100000;', /* big value for bootstrap3 compatibility */
- '}',
-
- '.blocklyResizeSE {',
- 'cursor: se-resize;',
- 'fill: #aaa;',
- '}',
-
- '.blocklyResizeSW {',
- 'cursor: sw-resize;',
- 'fill: #aaa;',
- '}',
-
- '.blocklyResizeLine {',
- 'stroke: #888;',
- 'stroke-width: 1;',
- '}',
-
- '.blocklyHighlightedConnectionPath {',
- 'fill: none;',
- 'stroke: #fc3;',
- 'stroke-width: 4px;',
- '}',
-
- '.blocklyPathLight {',
- 'fill: none;',
- 'stroke-linecap: round;',
- 'stroke-width: 1;',
- '}',
-
- '.blocklySelected>.blocklyPath {',
- 'stroke: #fc3;',
- 'stroke-width: 3px;',
- '}',
-
- '.blocklySelected>.blocklyPathLight {',
- 'display: none;',
- '}',
-
- '.blocklyDragging>.blocklyPath,',
- '.blocklyDragging>.blocklyPathLight {',
- 'fill-opacity: .8;',
- 'stroke-opacity: .8;',
- '}',
-
- '.blocklyDragging>.blocklyPathDark {',
- 'display: none;',
- '}',
-
- '.blocklyDisabled>.blocklyPath {',
- 'fill-opacity: .5;',
- 'stroke-opacity: .5;',
- '}',
-
- '.blocklyDisabled>.blocklyPathLight,',
- '.blocklyDisabled>.blocklyPathDark {',
- 'display: none;',
- '}',
-
- '.blocklyText {',
- 'cursor: default;',
- 'fill: #fff;',
- 'font-family: sans-serif;',
- 'font-size: 11pt;',
- '}',
-
- '.blocklyNonEditableText>text {',
- 'pointer-events: none;',
- '}',
-
- '.blocklyNonEditableText>rect,',
- '.blocklyEditableText>rect {',
- 'fill: #fff;',
- 'fill-opacity: .6;',
- '}',
-
- '.blocklyNonEditableText>text,',
- '.blocklyEditableText>text {',
- 'fill: #000;',
- '}',
-
- '.blocklyEditableText:hover>rect {',
- 'stroke: #fff;',
- 'stroke-width: 2;',
- '}',
-
- '.blocklyBubbleText {',
- 'fill: #000;',
- '}',
-
- '.blocklyFlyoutButton {',
- 'fill: #888;',
- 'cursor: default;',
- '}',
-
- '.blocklyFlyoutButtonShadow {',
- 'fill: #444;',
- '}',
-
- '.blocklyFlyoutButton:hover {',
- 'fill: #aaa;',
- '}',
-
- /*
- Don't allow users to select text. It gets annoying when trying to
- drag a block and selected text moves instead.
- */
- '.blocklySvg text {',
- 'user-select: none;',
- '-moz-user-select: none;',
- '-webkit-user-select: none;',
- 'cursor: inherit;',
- '}',
-
- '.blocklyHidden {',
- 'display: none;',
- '}',
-
- '.blocklyFieldDropdown:not(.blocklyHidden) {',
- 'display: block;',
- '}',
-
- '.blocklyIconGroup {',
- 'cursor: default;',
- '}',
-
- '.blocklyIconGroup:not(:hover),',
- '.blocklyIconGroupReadonly {',
- 'opacity: .6;',
- '}',
-
- '.blocklyIconShape {',
- 'fill: #00f;',
- 'stroke: #fff;',
- 'stroke-width: 1px;',
- '}',
-
- '.blocklyIconSymbol {',
- 'fill: #fff;',
- '}',
-
- '.blocklyMinimalBody {',
- 'margin: 0;',
- 'padding: 0;',
- '}',
-
- '.blocklyCommentTextarea {',
- 'background-color: #ffc;',
- 'border: 0;',
- 'margin: 0;',
- 'padding: 2px;',
- 'resize: none;',
- '}',
-
- '.blocklyHtmlInput {',
- 'border: none;',
- 'border-radius: 4px;',
- 'font-family: sans-serif;',
- 'height: 100%;',
- 'margin: 0;',
- 'outline: none;',
- 'padding: 0 1px;',
- 'width: 100%',
- '}',
-
- '.blocklyMainBackground {',
- 'stroke-width: 1;',
- 'stroke: #c6c6c6;', /* Equates to #ddd due to border being off-pixel. */
- '}',
-
- '.blocklyMutatorBackground {',
- 'fill: #fff;',
- 'stroke: #ddd;',
- 'stroke-width: 1;',
- '}',
-
- '.blocklyFlyoutBackground {',
- 'fill: #ddd;',
- 'fill-opacity: .8;',
- '}',
-
- '.blocklyScrollbarBackground {',
- 'opacity: 0;',
- '}',
-
- '.blocklyScrollbarHandle {',
- 'fill: #ccc;',
- '}',
-
- '.blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,',
- '.blocklyScrollbarHandle:hover {',
- 'fill: #bbb;',
- '}',
-
- '.blocklyZoom>image {',
- 'opacity: .4;',
- '}',
-
- '.blocklyZoom>image:hover {',
- 'opacity: .6;',
- '}',
-
- '.blocklyZoom>image:active {',
- 'opacity: .8;',
- '}',
-
- /* Darken flyout scrollbars due to being on a grey background. */
- /* By contrast, workspace scrollbars are on a white background. */
- '.blocklyFlyout .blocklyScrollbarHandle {',
- 'fill: #bbb;',
- '}',
-
- '.blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,',
- '.blocklyFlyout .blocklyScrollbarHandle:hover {',
- 'fill: #aaa;',
- '}',
-
- '.blocklyInvalidInput {',
- 'background: #faa;',
- '}',
-
- '.blocklyAngleCircle {',
- 'stroke: #444;',
- 'stroke-width: 1;',
- 'fill: #ddd;',
- 'fill-opacity: .8;',
- '}',
-
- '.blocklyAngleMarks {',
- 'stroke: #444;',
- 'stroke-width: 1;',
- '}',
-
- '.blocklyAngleGauge {',
- 'fill: #f88;',
- 'fill-opacity: .8;',
- '}',
-
- '.blocklyAngleLine {',
- 'stroke: #f00;',
- 'stroke-width: 2;',
- 'stroke-linecap: round;',
- '}',
-
- '.blocklyContextMenu {',
- 'border-radius: 4px;',
- '}',
-
- '.blocklyDropdownMenu {',
- 'padding: 0 !important;',
- '}',
-
- /* Override the default Closure URL. */
- '.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,',
- '.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {',
- 'background: url(<<>>/sprites.png) no-repeat -48px -16px !important;',
- '}',
-
- /* Category tree in Toolbox. */
- '.blocklyToolboxDiv {',
- 'background-color: #ddd;',
- 'overflow-x: visible;',
- 'overflow-y: auto;',
- 'position: absolute;',
- '}',
-
- '.blocklyTreeRoot {',
- 'padding: 4px 0;',
- '}',
-
- '.blocklyTreeRoot:focus {',
- 'outline: none;',
- '}',
-
- '.blocklyTreeRow {',
- 'height: 22px;',
- 'line-height: 22px;',
- 'margin-bottom: 3px;',
- 'padding-right: 8px;',
- 'white-space: nowrap;',
- '}',
-
- '.blocklyHorizontalTree {',
- 'float: left;',
- 'margin: 1px 5px 8px 0;',
- '}',
-
- '.blocklyHorizontalTreeRtl {',
- 'float: right;',
- 'margin: 1px 0 8px 5px;',
- '}',
-
- '.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {',
- 'margin-left: 8px;',
- '}',
-
- '.blocklyTreeRow:not(.blocklyTreeSelected):hover {',
- 'background-color: #e4e4e4;',
- '}',
-
- '.blocklyTreeSeparator {',
- 'border-bottom: solid #e5e5e5 1px;',
- 'height: 0;',
- 'margin: 5px 0;',
- '}',
-
- '.blocklyTreeSeparatorHorizontal {',
- 'border-right: solid #e5e5e5 1px;',
- 'width: 0;',
- 'padding: 5px 0;',
- 'margin: 0 5px;',
- '}',
-
-
- '.blocklyTreeIcon {',
- 'background-image: url(<<>>/sprites.png);',
- 'height: 16px;',
- 'vertical-align: middle;',
- 'width: 16px;',
- '}',
-
- '.blocklyTreeIconClosedLtr {',
- 'background-position: -32px -1px;',
- '}',
-
- '.blocklyTreeIconClosedRtl {',
- 'background-position: 0px -1px;',
- '}',
-
- '.blocklyTreeIconOpen {',
- 'background-position: -16px -1px;',
- '}',
-
- '.blocklyTreeSelected>.blocklyTreeIconClosedLtr {',
- 'background-position: -32px -17px;',
- '}',
-
- '.blocklyTreeSelected>.blocklyTreeIconClosedRtl {',
- 'background-position: 0px -17px;',
- '}',
-
- '.blocklyTreeSelected>.blocklyTreeIconOpen {',
- 'background-position: -16px -17px;',
- '}',
-
- '.blocklyTreeIconNone,',
- '.blocklyTreeSelected>.blocklyTreeIconNone {',
- 'background-position: -48px -1px;',
- '}',
-
- '.blocklyTreeLabel {',
- 'cursor: default;',
- 'font-family: sans-serif;',
- 'font-size: 16px;',
- 'padding: 0 3px;',
- 'vertical-align: middle;',
- '}',
-
- '.blocklyTreeSelected .blocklyTreeLabel {',
- 'color: #fff;',
- '}',
-
- /* Copied from: goog/css/colorpicker-simplegrid.css */
- /*
- * Copyright 2007 The Closure Library Authors. All Rights Reserved.
- *
- * Use of this source code is governed by the Apache License, Version 2.0.
- * See the COPYING file for details.
- */
-
- /* Author: pupius@google.com (Daniel Pupius) */
-
- /*
- Styles to make the colorpicker look like the old gmail color picker
- NOTE: without CSS scoping this will override styles defined in palette.css
- */
- '.blocklyWidgetDiv .goog-palette {',
- 'outline: none;',
- 'cursor: default;',
- '}',
-
- '.blocklyWidgetDiv .goog-palette-table {',
- 'border: 1px solid #666;',
- 'border-collapse: collapse;',
- '}',
-
- '.blocklyWidgetDiv .goog-palette-cell {',
- 'height: 13px;',
- 'width: 15px;',
- 'margin: 0;',
- 'border: 0;',
- 'text-align: center;',
- 'vertical-align: middle;',
- 'border-right: 1px solid #666;',
- 'font-size: 1px;',
- '}',
-
- '.blocklyWidgetDiv .goog-palette-colorswatch {',
- 'position: relative;',
- 'height: 13px;',
- 'width: 15px;',
- 'border: 1px solid #666;',
- '}',
-
- '.blocklyWidgetDiv .goog-palette-cell-hover .goog-palette-colorswatch {',
- 'border: 1px solid #FFF;',
- '}',
-
- '.blocklyWidgetDiv .goog-palette-cell-selected .goog-palette-colorswatch {',
- 'border: 1px solid #000;',
- 'color: #fff;',
- '}',
-
- /* Copied from: goog/css/menu.css */
- /*
- * Copyright 2009 The Closure Library Authors. All Rights Reserved.
- *
- * Use of this source code is governed by the Apache License, Version 2.0.
- * See the COPYING file for details.
- */
-
- /**
- * Standard styling for menus created by goog.ui.MenuRenderer.
- *
- * @author attila@google.com (Attila Bodis)
- */
-
- '.blocklyWidgetDiv .goog-menu {',
- 'background: #fff;',
- 'border-color: #ccc #666 #666 #ccc;',
- 'border-style: solid;',
- 'border-width: 1px;',
- 'cursor: default;',
- 'font: normal 13px Arial, sans-serif;',
- 'margin: 0;',
- 'outline: none;',
- 'padding: 4px 0;',
- 'position: absolute;',
- 'overflow-y: auto;',
- 'overflow-x: hidden;',
- 'max-height: 100%;',
- 'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
- '}',
-
- /* Copied from: goog/css/menuitem.css */
- /*
- * Copyright 2009 The Closure Library Authors. All Rights Reserved.
- *
- * Use of this source code is governed by the Apache License, Version 2.0.
- * See the COPYING file for details.
- */
-
- /**
- * Standard styling for menus created by goog.ui.MenuItemRenderer.
- *
- * @author attila@google.com (Attila Bodis)
- */
-
- /**
- * State: resting.
- *
- * NOTE(mleibman,chrishenry):
- * The RTL support in Closure is provided via two mechanisms -- "rtl" CSS
- * classes and BiDi flipping done by the CSS compiler. Closure supports RTL
- * with or without the use of the CSS compiler. In order for them not
- * to conflict with each other, the "rtl" CSS classes need to have the #noflip
- * annotation. The non-rtl counterparts should ideally have them as well, but,
- * since .goog-menuitem existed without .goog-menuitem-rtl for so long before
- * being added, there is a risk of people having templates where they are not
- * rendering the .goog-menuitem-rtl class when in RTL and instead rely solely
- * on the BiDi flipping by the CSS compiler. That's why we're not adding the
- * #noflip to .goog-menuitem.
- */
- '.blocklyWidgetDiv .goog-menuitem {',
- 'color: #000;',
- 'font: normal 13px Arial, sans-serif;',
- 'list-style: none;',
- 'margin: 0;',
- /* 28px on the left for icon or checkbox; 7em on the right for shortcut. */
- 'padding: 4px 7em 4px 28px;',
- 'white-space: nowrap;',
- '}',
-
- /* BiDi override for the resting state. */
- /* #noflip */
- '.blocklyWidgetDiv .goog-menuitem.goog-menuitem-rtl {',
- /* Flip left/right padding for BiDi. */
- 'padding-left: 7em;',
- 'padding-right: 28px;',
- '}',
-
- /* If a menu doesn't have checkable items or items with icons, remove padding. */
- '.blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem,',
- '.blocklyWidgetDiv .goog-menu-noicon .goog-menuitem {',
- 'padding-left: 12px;',
- '}',
-
- /*
- * If a menu doesn't have items with shortcuts, leave just enough room for
- * submenu arrows, if they are rendered.
- */
- '.blocklyWidgetDiv .goog-menu-noaccel .goog-menuitem {',
- 'padding-right: 20px;',
- '}',
-
- '.blocklyWidgetDiv .goog-menuitem-content {',
- 'color: #000;',
- 'font: normal 13px Arial, sans-serif;',
- '}',
-
- /* State: disabled. */
- '.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-accel,',
- '.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content {',
- 'color: #ccc !important;',
- '}',
-
- '.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon {',
- 'opacity: 0.3;',
- '-moz-opacity: 0.3;',
- 'filter: alpha(opacity=30);',
- '}',
-
- /* State: hover. */
- '.blocklyWidgetDiv .goog-menuitem-highlight,',
- '.blocklyWidgetDiv .goog-menuitem-hover {',
- 'background-color: #d6e9f8;',
- /* Use an explicit top and bottom border so that the selection is visible',
- * in high contrast mode. */
- 'border-color: #d6e9f8;',
- 'border-style: dotted;',
- 'border-width: 1px 0;',
- 'padding-bottom: 3px;',
- 'padding-top: 3px;',
- '}',
-
- /* State: selected/checked. */
- '.blocklyWidgetDiv .goog-menuitem-checkbox,',
- '.blocklyWidgetDiv .goog-menuitem-icon {',
- 'background-repeat: no-repeat;',
- 'height: 16px;',
- 'left: 6px;',
- 'position: absolute;',
- 'right: auto;',
- 'vertical-align: middle;',
- 'width: 16px;',
- '}',
-
- /* BiDi override for the selected/checked state. */
- /* #noflip */
- '.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,',
- '.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon {',
- /* Flip left/right positioning. */
- 'left: auto;',
- 'right: 6px;',
- '}',
-
- '.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,',
- '.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {',
- /* Client apps may override the URL at which they serve the sprite. */
- 'background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -512px 0;',
- '}',
-
- /* Keyboard shortcut ("accelerator") style. */
- '.blocklyWidgetDiv .goog-menuitem-accel {',
- 'color: #999;',
- /* Keyboard shortcuts are untranslated; always left-to-right. */
- /* #noflip */
- 'direction: ltr;',
- 'left: auto;',
- 'padding: 0 6px;',
- 'position: absolute;',
- 'right: 0;',
- 'text-align: right;',
- '}',
-
- /* BiDi override for shortcut style. */
- /* #noflip */
- '.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-accel {',
- /* Flip left/right positioning and text alignment. */
- 'left: 0;',
- 'right: auto;',
- 'text-align: left;',
- '}',
-
- /* Mnemonic styles. */
- '.blocklyWidgetDiv .goog-menuitem-mnemonic-hint {',
- 'text-decoration: underline;',
- '}',
-
- '.blocklyWidgetDiv .goog-menuitem-mnemonic-separator {',
- 'color: #999;',
- 'font-size: 12px;',
- 'padding-left: 4px;',
- '}',
-
- /* Copied from: goog/css/menuseparator.css */
- /*
- * Copyright 2009 The Closure Library Authors. All Rights Reserved.
- *
- * Use of this source code is governed by the Apache License, Version 2.0.
- * See the COPYING file for details.
- */
-
- /**
- * Standard styling for menus created by goog.ui.MenuSeparatorRenderer.
- *
- * @author attila@google.com (Attila Bodis)
- */
-
- '.blocklyWidgetDiv .goog-menuseparator {',
- 'border-top: 1px solid #ccc;',
- 'margin: 4px 0;',
- 'padding: 0;',
- '}',
-
- ''
-];
diff --git a/core/field.js.orig b/core/field.js.orig
deleted file mode 100644
index 131a62679..000000000
--- a/core/field.js.orig
+++ /dev/null
@@ -1,495 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Field. Used for editable titles, variables, etc.
- * This is an abstract class that defines the UI on the block. Actual
- * instances would be Blockly.FieldTextInput, Blockly.FieldDropdown, etc.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Field');
-
-goog.require('goog.asserts');
-goog.require('goog.dom');
-goog.require('goog.math.Size');
-goog.require('goog.style');
-goog.require('goog.userAgent');
-
-
-/**
- * Abstract class for an editable field.
- * @param {string} text The initial content of the field.
- * @param {Function=} opt_validator An optional function that is called
- * to validate any constraints on what the user entered. Takes the new
- * text as an argument and returns either the accepted text, a replacement
- * text, or null to abort the change.
- * @constructor
- */
-Blockly.Field = function(text, opt_validator) {
- this.size_ = new goog.math.Size(0, 25);
- this.setValue(text);
- this.setValidator(opt_validator);
-};
-
-/**
- * Temporary cache of text widths.
- * @type {Object}
- * @private
- */
-Blockly.Field.cacheWidths_ = null;
-
-/**
- * Number of current references to cache.
- * @type {number}
- * @private
- */
-Blockly.Field.cacheReference_ = 0;
-
-
-/**
- * Name of field. Unique within each block.
- * Static labels are usually unnamed.
- * @type {string=}
- */
-Blockly.Field.prototype.name = undefined;
-
-/**
- * Maximum characters of text to display before adding an ellipsis.
- * @type {number}
- */
-Blockly.Field.prototype.maxDisplayLength = 50;
-
-/**
- * Visible text to display.
- * @type {string}
- * @private
- */
-Blockly.Field.prototype.text_ = '';
-
-/**
- * Block this field is attached to. Starts as null, then in set in init.
- * @type {Blockly.Block}
- * @private
- */
-Blockly.Field.prototype.sourceBlock_ = null;
-
-/**
- * Is the field visible, or hidden due to the block being collapsed?
- * @type {boolean}
- * @private
- */
-Blockly.Field.prototype.visible_ = true;
-
-/**
- * Validation function called when user edits an editable field.
- * @type {Function}
- * @private
- */
-Blockly.Field.prototype.validator_ = null;
-
-/**
- * Non-breaking space.
- * @const
- */
-Blockly.Field.NBSP = '\u00A0';
-
-/**
- * Editable fields are saved by the XML renderer, non-editable fields are not.
- */
-Blockly.Field.prototype.EDITABLE = true;
-
-/**
- * Attach this field to a block.
- * @param {!Blockly.Block} block The block containing this field.
- */
-Blockly.Field.prototype.setSourceBlock = function(block) {
- goog.asserts.assert(!this.sourceBlock_, 'Field already bound to a block.');
- this.sourceBlock_ = block;
-};
-
-/**
- * Install this field on a block.
- */
-Blockly.Field.prototype.init = function() {
- if (this.fieldGroup_) {
- // Field has already been initialized once.
- return;
- }
- // Build the DOM.
- this.fieldGroup_ = Blockly.createSvgElement('g', {}, null);
- if (!this.visible_) {
- this.fieldGroup_.style.display = 'none';
- }
- this.borderRect_ = Blockly.createSvgElement('rect',
- {'rx': 4,
- 'ry': 4,
- 'x': -Blockly.BlockSvg.SEP_SPACE_X / 2,
- 'y': 0,
- 'height': 16}, this.fieldGroup_, this.sourceBlock_.workspace);
- /** @type {!Element} */
- this.textElement_ = Blockly.createSvgElement('text',
- {'class': 'blocklyText', 'y': this.size_.height - 12.5},
- this.fieldGroup_);
-
- this.updateEditable();
- this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
- this.mouseUpWrapper_ =
- Blockly.bindEvent_(this.fieldGroup_, 'mouseup', this, this.onMouseUp_);
- // Force a render.
- this.updateTextNode_();
-};
-
-/**
- * Dispose of all DOM objects belonging to this editable field.
- */
-Blockly.Field.prototype.dispose = function() {
- if (this.mouseUpWrapper_) {
- Blockly.unbindEvent_(this.mouseUpWrapper_);
- this.mouseUpWrapper_ = null;
- }
- this.sourceBlock_ = null;
- goog.dom.removeNode(this.fieldGroup_);
- this.fieldGroup_ = null;
- this.textElement_ = null;
- this.borderRect_ = null;
- this.validator_ = null;
-};
-
-/**
- * Add or remove the UI indicating if this field is editable or not.
- */
-Blockly.Field.prototype.updateEditable = function() {
- var group = this.fieldGroup_;
- if (!this.EDITABLE || !group) {
- return;
- }
- if (this.sourceBlock_.isEditable()) {
- Blockly.addClass_(group, 'blocklyEditableText');
- Blockly.removeClass_(group, 'blocklyNonEditableText');
- this.fieldGroup_.style.cursor = this.CURSOR;
- } else {
- Blockly.addClass_(group, 'blocklyNonEditableText');
- Blockly.removeClass_(group, 'blocklyEditableText');
- this.fieldGroup_.style.cursor = '';
- }
-};
-
-/**
- * Gets whether this editable field is visible or not.
- * @return {boolean} True if visible.
- */
-Blockly.Field.prototype.isVisible = function() {
- return this.visible_;
-};
-
-/**
- * Sets whether this editable field is visible or not.
- * @param {boolean} visible True if visible.
- */
-Blockly.Field.prototype.setVisible = function(visible) {
- if (this.visible_ == visible) {
- return;
- }
- this.visible_ = visible;
- var root = this.getSvgRoot();
- if (root) {
- root.style.display = visible ? 'block' : 'none';
- this.render_();
- }
-};
-
-/**
- * Sets a new validation function for editable fields.
- * @param {Function} handler New validation function, or null.
- */
-Blockly.Field.prototype.setValidator = function(handler) {
- this.validator_ = handler;
-};
-
-/**
- * Gets the validation function for editable fields.
- * @return {Function} Validation function, or null.
- */
-Blockly.Field.prototype.getValidator = function() {
- return this.validator_;
-};
-
-/**
- * Validates a change. Does nothing. Subclasses may override this.
- * @param {string} text The user's text.
- * @return {string} No change needed.
- */
-Blockly.Field.prototype.classValidator = function(text) {
- return text;
-};
-
-/**
- * Calls the validation function for this field, as well as all the validation
- * function for the field's class and its parents.
- * @param {string} text Proposed text.
- * @return {?string} Revised text, or null if invalid.
- */
-Blockly.Field.prototype.callValidator = function(text) {
- var classResult = this.classValidator(text);
- if (classResult === null) {
- // Class validator rejects value. Game over.
- return null;
- } else if (classResult !== undefined) {
- text = classResult;
- }
- var userValidator = this.getValidator();
- if (userValidator) {
- var userResult = userValidator.call(this, text);
- if (userResult === null) {
- // User validator rejects value. Game over.
- return null;
- } else if (userResult !== undefined) {
- text = userResult;
- }
- }
- return text;
-};
-
-/**
- * Gets the group element for this editable field.
- * Used for measuring the size and for positioning.
- * @return {!Element} The group element.
- */
-Blockly.Field.prototype.getSvgRoot = function() {
- return /** @type {!Element} */ (this.fieldGroup_);
-};
-
-/**
- * Draws the border with the correct width.
- * Saves the computed width in a property.
- * @private
- */
-Blockly.Field.prototype.render_ = function() {
- if (this.visible_ && this.textElement_) {
- var key = this.textElement_.textContent + '\n' +
- this.textElement_.className.baseVal;
- if (Blockly.Field.cacheWidths_ && Blockly.Field.cacheWidths_[key]) {
- var width = Blockly.Field.cacheWidths_[key];
- } else {
- try {
- var width = this.textElement_.getComputedTextLength();
- } catch (e) {
- // MSIE 11 is known to throw "Unexpected call to method or property
- // access." if Blockly is hidden.
- var width = this.textElement_.textContent.length * 8;
- }
- if (Blockly.Field.cacheWidths_) {
- Blockly.Field.cacheWidths_[key] = width;
- }
- }
- if (this.borderRect_) {
- this.borderRect_.setAttribute('width',
- width + Blockly.BlockSvg.SEP_SPACE_X);
- }
- } else {
- var width = 0;
- }
- this.size_.width = width;
-};
-
-/**
- * Start caching field widths. Every call to this function MUST also call
- * stopCache. Caches must not survive between execution threads.
- */
-Blockly.Field.startCache = function() {
- Blockly.Field.cacheReference_++;
- if (!Blockly.Field.cacheWidths_) {
- Blockly.Field.cacheWidths_ = {};
- }
-};
-
-/**
- * Stop caching field widths. Unless caching was already on when the
- * corresponding call to startCache was made.
- */
-Blockly.Field.stopCache = function() {
- Blockly.Field.cacheReference_--;
- if (!Blockly.Field.cacheReference_) {
- Blockly.Field.cacheWidths_ = null;
- }
-};
-
-/**
- * Returns the height and width of the field.
- * @return {!goog.math.Size} Height and width.
- */
-Blockly.Field.prototype.getSize = function() {
- if (!this.size_.width) {
- this.render_();
- }
- return this.size_;
-};
-
-/**
- * Returns the height and width of the field,
- * accounting for the workspace scaling.
- * @return {!goog.math.Size} Height and width.
- * @private
- */
-Blockly.Field.prototype.getScaledBBox_ = function() {
- var bBox = this.borderRect_.getBBox();
- // Create new object, as getBBox can return an uneditable SVGRect in IE.
- return new goog.math.Size(bBox.width * this.sourceBlock_.workspace.scale,
- bBox.height * this.sourceBlock_.workspace.scale);
-};
-
-/**
- * Get the text from this field.
- * @return {string} Current text.
- */
-Blockly.Field.prototype.getText = function() {
- return this.text_;
-};
-
-/**
- * Set the text in this field. Trigger a rerender of the source block.
- * @param {*} text New text.
- */
-Blockly.Field.prototype.setText = function(text) {
- if (text === null) {
- // No change if null.
- return;
- }
- text = String(text);
- if (text === this.text_) {
- // No change.
- return;
- }
- this.text_ = text;
- this.updateTextNode_();
-
- if (this.sourceBlock_ && this.sourceBlock_.rendered) {
- this.sourceBlock_.render();
- this.sourceBlock_.bumpNeighbours_();
- }
-};
-
-/**
- * Update the text node of this field to display the current text.
- * @private
- */
-Blockly.Field.prototype.updateTextNode_ = function() {
- if (!this.textElement_) {
- // Not rendered yet.
- return;
- }
- var text = this.text_;
- if (text.length > this.maxDisplayLength) {
- // Truncate displayed string and add an ellipsis ('...').
- text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
- }
- // Empty the text element.
- goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_));
- // Replace whitespace with non-breaking spaces so the text doesn't collapse.
- text = text.replace(/\s/g, Blockly.Field.NBSP);
- if (this.sourceBlock_.RTL && text) {
- // The SVG is LTR, force text to be RTL.
- text += '\u200F';
- }
- if (!text) {
- // Prevent the field from disappearing if empty.
- text = Blockly.Field.NBSP;
- }
- var textNode = document.createTextNode(text);
- this.textElement_.appendChild(textNode);
-
- // Cached width is obsolete. Clear it.
- this.size_.width = 0;
-};
-
-/**
- * By default there is no difference between the human-readable text and
- * the language-neutral values. Subclasses (such as dropdown) may define this.
- * @return {string} Current text.
- */
-Blockly.Field.prototype.getValue = function() {
- return this.getText();
-};
-
-/**
- * By default there is no difference between the human-readable text and
- * the language-neutral values. Subclasses (such as dropdown) may define this.
- * @param {string} newText New text.
- */
-Blockly.Field.prototype.setValue = function(newText) {
- if (newText === null) {
- // No change if null.
- return;
- }
- var oldText = this.getValue();
- if (oldText == newText) {
- return;
- }
- if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
- Blockly.Events.fire(new Blockly.Events.Change(
- this.sourceBlock_, 'field', this.name, oldText, newText));
- }
- this.setText(newText);
-};
-
-/**
- * Handle a mouse up event on an editable field.
- * @param {!Event} e Mouse up event.
- * @private
- */
-Blockly.Field.prototype.onMouseUp_ = function(e) {
- if ((goog.userAgent.IPHONE || goog.userAgent.IPAD) &&
- !goog.userAgent.isVersionOrHigher('537.51.2') &&
- e.layerX !== 0 && e.layerY !== 0) {
- // Old iOS spawns a bogus event on the next touch after a 'prompt()' edit.
- // Unlike the real events, these have a layerX and layerY set.
- return;
- } else if (Blockly.isRightButton(e)) {
- // Right-click.
- return;
- } else if (this.sourceBlock_.workspace.isDragging()) {
- // Drag operation is concluding. Don't open the editor.
- return;
- } else if (this.sourceBlock_.isEditable()) {
- // Non-abstract sub-classes must define a showEditor_ method.
- this.showEditor_();
- }
-};
-
-/**
- * Change the tooltip text for this field.
- * @param {string|!Element} newTip Text for tooltip or a parent element to
- * link to for its tooltip.
- */
-Blockly.Field.prototype.setTooltip = function(newTip) {
- // Non-abstract sub-classes may wish to implement this. See FieldLabel.
-};
-
-/**
- * Return the absolute coordinates of the top-left corner of this field.
- * The origin (0,0) is the top-left corner of the page body.
- * @return {!goog.math.Coordinate} Object with .x and .y properties.
- * @private
- */
-Blockly.Field.prototype.getAbsoluteXY_ = function() {
- return goog.style.getPageOffset(this.borderRect_);
-};
diff --git a/core/field_dropdown.js.orig b/core/field_dropdown.js.orig
deleted file mode 100644
index ec3dd4f5a..000000000
--- a/core/field_dropdown.js.orig
+++ /dev/null
@@ -1,320 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Dropdown input field. Used for editable titles and variables.
- * In the interests of a consistent UI, the toolbox shares some functions and
- * properties with the context menu.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.FieldDropdown');
-
-goog.require('Blockly.Field');
-goog.require('goog.dom');
-goog.require('goog.events');
-goog.require('goog.style');
-goog.require('goog.ui.Menu');
-goog.require('goog.ui.MenuItem');
-goog.require('goog.userAgent');
-
-
-/**
- * Class for an editable dropdown field.
- * @param {(!Array.>|!Function)} menuGenerator An array of
- * options for a dropdown list, or a function which generates these options.
- * @param {Function=} opt_validator A function that is executed when a new
- * option is selected, with the newly selected value as its sole argument.
- * If it returns a value, that value (which must be one of the options) will
- * become selected in place of the newly selected option, unless the return
- * value is null, in which case the change is aborted.
- * @extends {Blockly.Field}
- * @constructor
- */
-Blockly.FieldDropdown = function(menuGenerator, opt_validator) {
- this.menuGenerator_ = menuGenerator;
- this.trimOptions_();
- var firstTuple = this.getOptions_()[0];
-
- // Call parent's constructor.
- Blockly.FieldDropdown.superClass_.constructor.call(this, firstTuple[1],
- opt_validator);
-};
-goog.inherits(Blockly.FieldDropdown, Blockly.Field);
-
-/**
- * Horizontal distance that a checkmark ovehangs the dropdown.
- */
-Blockly.FieldDropdown.CHECKMARK_OVERHANG = 25;
-
-/**
- * Android can't (in 2014) display "▾", so use "▼" instead.
- */
-Blockly.FieldDropdown.ARROW_CHAR = goog.userAgent.ANDROID ? '\u25BC' : '\u25BE';
-
-/**
- * Mouse cursor style when over the hotspot that initiates the editor.
- */
-Blockly.FieldDropdown.prototype.CURSOR = 'default';
-
-/**
- * Install this dropdown on a block.
- */
-Blockly.FieldDropdown.prototype.init = function() {
- if (this.fieldGroup_) {
- // Dropdown has already been initialized once.
- return;
- }
- // Add dropdown arrow: "option ▾" (LTR) or "▾ אופציה" (RTL)
- this.arrow_ = Blockly.createSvgElement('tspan', {}, null);
- this.arrow_.appendChild(document.createTextNode(
- this.sourceBlock_.RTL ? Blockly.FieldDropdown.ARROW_CHAR + ' ' :
- ' ' + Blockly.FieldDropdown.ARROW_CHAR));
-
- Blockly.FieldDropdown.superClass_.init.call(this);
- // Force a reset of the text to add the arrow.
- var text = this.text_;
- this.text_ = null;
- this.setText(text);
-};
-
-/**
- * Create a dropdown menu under the text.
- * @private
- */
-Blockly.FieldDropdown.prototype.showEditor_ = function() {
- Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, null);
- var thisField = this;
-
- function callback(e) {
- var menuItem = e.target;
- if (menuItem) {
- var value = menuItem.getValue();
- if (thisField.sourceBlock_) {
- // Call any validation function, and allow it to override.
- value = thisField.callValidator(value);
- }
- if (value !== null) {
- thisField.setValue(value);
- }
- }
- Blockly.WidgetDiv.hideIfOwner(thisField);
- }
-
- var menu = new goog.ui.Menu();
- menu.setRightToLeft(this.sourceBlock_.RTL);
- var options = this.getOptions_();
- for (var i = 0; i < options.length; i++) {
- var text = options[i][0]; // Human-readable text.
- var value = options[i][1]; // Language-neutral value.
- var menuItem = new goog.ui.MenuItem(text);
- menuItem.setRightToLeft(this.sourceBlock_.RTL);
- menuItem.setValue(value);
- menuItem.setCheckable(true);
- menu.addChild(menuItem, true);
- menuItem.setChecked(value == this.value_);
- }
- // Listen for mouse/keyboard events.
- goog.events.listen(menu, goog.ui.Component.EventType.ACTION, callback);
- // Listen for touch events (why doesn't Closure handle this already?).
- function callbackTouchStart(e) {
- var control = this.getOwnerControl(/** @type {Node} */ (e.target));
- // Highlight the menu item.
- control.handleMouseDown(e);
- }
- function callbackTouchEnd(e) {
- var control = this.getOwnerControl(/** @type {Node} */ (e.target));
- // Activate the menu item.
- control.performActionInternal(e);
- }
- menu.getHandler().listen(menu.getElement(), goog.events.EventType.TOUCHSTART,
- callbackTouchStart);
- menu.getHandler().listen(menu.getElement(), goog.events.EventType.TOUCHEND,
- callbackTouchEnd);
-
- // Record windowSize and scrollOffset before adding menu.
- var windowSize = goog.dom.getViewportSize();
- var scrollOffset = goog.style.getViewportPageOffset(document);
- var xy = this.getAbsoluteXY_();
- var borderBBox = this.getScaledBBox_();
- var div = Blockly.WidgetDiv.DIV;
- menu.render(div);
- var menuDom = menu.getElement();
- Blockly.addClass_(menuDom, 'blocklyDropdownMenu');
- // Record menuSize after adding menu.
- var menuSize = goog.style.getSize(menuDom);
- // Recalculate height for the total content, not only box height.
- menuSize.height = menuDom.scrollHeight;
-
- // Position the menu.
- // Flip menu vertically if off the bottom.
- if (xy.y + menuSize.height + borderBBox.height >=
- windowSize.height + scrollOffset.y) {
- xy.y -= menuSize.height + 2;
- } else {
- xy.y += borderBBox.height;
- }
- if (this.sourceBlock_.RTL) {
- xy.x += borderBBox.width;
- xy.x += Blockly.FieldDropdown.CHECKMARK_OVERHANG;
- // Don't go offscreen left.
- if (xy.x < scrollOffset.x + menuSize.width) {
- xy.x = scrollOffset.x + menuSize.width;
- }
- } else {
- xy.x -= Blockly.FieldDropdown.CHECKMARK_OVERHANG;
- // Don't go offscreen right.
- if (xy.x > windowSize.width + scrollOffset.x - menuSize.width) {
- xy.x = windowSize.width + scrollOffset.x - menuSize.width;
- }
- }
- Blockly.WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset,
- this.sourceBlock_.RTL);
- menu.setAllowAutoFocus(true);
- menuDom.focus();
-};
-
-/**
- * Factor out common words in statically defined options.
- * Create prefix and/or suffix labels.
- * @private
- */
-Blockly.FieldDropdown.prototype.trimOptions_ = function() {
- this.prefixField = null;
- this.suffixField = null;
- var options = this.menuGenerator_;
- if (!goog.isArray(options) || options.length < 2) {
- return;
- }
- var strings = options.map(function(t) {return t[0];});
- var shortest = Blockly.shortestStringLength(strings);
- var prefixLength = Blockly.commonWordPrefix(strings, shortest);
- var suffixLength = Blockly.commonWordSuffix(strings, shortest);
- if (!prefixLength && !suffixLength) {
- return;
- }
- if (shortest <= prefixLength + suffixLength) {
- // One or more strings will entirely vanish if we proceed. Abort.
- return;
- }
- if (prefixLength) {
- this.prefixField = strings[0].substring(0, prefixLength - 1);
- }
- if (suffixLength) {
- this.suffixField = strings[0].substr(1 - suffixLength);
- }
- // Remove the prefix and suffix from the options.
- var newOptions = [];
- for (var i = 0; i < options.length; i++) {
- var text = options[i][0];
- var value = options[i][1];
- text = text.substring(prefixLength, text.length - suffixLength);
- newOptions[i] = [text, value];
- }
- this.menuGenerator_ = newOptions;
-};
-
-/**
- * Return a list of the options for this dropdown.
- * @return {!Array.>} Array of option tuples:
- * (human-readable text, language-neutral name).
- * @private
- */
-Blockly.FieldDropdown.prototype.getOptions_ = function() {
- if (goog.isFunction(this.menuGenerator_)) {
- return this.menuGenerator_.call(this);
- }
- return /** @type {!Array.>} */ (this.menuGenerator_);
-};
-
-/**
- * Get the language-neutral value from this dropdown menu.
- * @return {string} Current text.
- */
-Blockly.FieldDropdown.prototype.getValue = function() {
- return this.value_;
-};
-
-/**
- * Set the language-neutral value for this dropdown menu.
- * @param {string} newValue New value to set.
- */
-Blockly.FieldDropdown.prototype.setValue = function(newValue) {
- if (newValue === null || newValue === this.value_) {
- return; // No change if null.
- }
- if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
- Blockly.Events.fire(new Blockly.Events.Change(
- this.sourceBlock_, 'field', this.name, this.value_, newValue));
- }
- this.value_ = newValue;
- // Look up and display the human-readable text.
- var options = this.getOptions_();
- for (var i = 0; i < options.length; i++) {
- // Options are tuples of human-readable text and language-neutral values.
- if (options[i][1] == newValue) {
- this.setText(options[i][0]);
- return;
- }
- }
- // Value not found. Add it, maybe it will become valid once set
- // (like variable names).
- this.setText(newValue);
-};
-
-/**
- * Set the text in this field. Trigger a rerender of the source block.
- * @param {?string} text New text.
- */
-Blockly.FieldDropdown.prototype.setText = function(text) {
- if (this.sourceBlock_ && this.arrow_) {
- // Update arrow's colour.
- this.arrow_.style.fill = this.sourceBlock_.getColour();
- }
- if (text === null || text === this.text_) {
- // No change if null.
- return;
- }
- this.text_ = text;
- this.updateTextNode_();
-
- if (this.textElement_) {
- // Insert dropdown arrow.
- if (this.sourceBlock_.RTL) {
- this.textElement_.insertBefore(this.arrow_, this.textElement_.firstChild);
- } else {
- this.textElement_.appendChild(this.arrow_);
- }
- }
-
- if (this.sourceBlock_ && this.sourceBlock_.rendered) {
- this.sourceBlock_.render();
- this.sourceBlock_.bumpNeighbours_();
- }
-};
-
-/**
- * Close the dropdown menu if this input is being deleted.
- */
-Blockly.FieldDropdown.prototype.dispose = function() {
- Blockly.WidgetDiv.hideIfOwner(this);
- Blockly.FieldDropdown.superClass_.dispose.call(this);
-};
diff --git a/core/flyout.js.orig b/core/flyout.js.orig
deleted file mode 100644
index 03ea15059..000000000
--- a/core/flyout.js.orig
+++ /dev/null
@@ -1,1364 +0,0 @@
-/**
- * @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 Flyout tray containing blocks which may be created.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Flyout');
-
-goog.require('Blockly.Block');
-goog.require('Blockly.Comment');
-goog.require('Blockly.Events');
-goog.require('Blockly.FlyoutButton');
-goog.require('Blockly.WorkspaceSvg');
-goog.require('goog.dom');
-goog.require('goog.events');
-goog.require('goog.math.Rect');
-goog.require('goog.userAgent');
-
-
-/**
- * Class for a flyout.
- * @param {!Object} workspaceOptions Dictionary of options for the workspace.
- * @constructor
- */
-Blockly.Flyout = function(workspaceOptions) {
- workspaceOptions.getMetrics = this.getMetrics_.bind(this);
- workspaceOptions.setMetrics = this.setMetrics_.bind(this);
- /**
- * @type {!Blockly.Workspace}
- * @private
- */
- this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions);
- this.workspace_.isFlyout = true;
-
- /**
- * Is RTL vs LTR.
- * @type {boolean}
- */
- this.RTL = !!workspaceOptions.RTL;
-
- /**
- * Flyout should be laid out horizontally vs vertically.
- * @type {boolean}
- * @private
- */
- this.horizontalLayout_ = workspaceOptions.horizontalLayout;
-
- /**
- * Position of the toolbox and flyout relative to the workspace.
- * @type {number}
- * @private
- */
- this.toolboxPosition_ = workspaceOptions.toolboxPosition;
-
- /**
- * Opaque data that can be passed to Blockly.unbindEvent_.
- * @type {!Array.}
- * @private
- */
- this.eventWrappers_ = [];
-
- /**
- * List of background buttons that lurk behind each block to catch clicks
- * landing in the blocks' lakes and bays.
- * @type {!Array.}
- * @private
- */
- this.backgroundButtons_ = [];
-
- /**
- * List of visible buttons.
- * @type {!Array.}
- * @private
- */
- this.buttons_ = [];
-
- /**
- * List of event listeners.
- * @type {!Array.}
- * @private
- */
- this.listeners_ = [];
-
- /**
- * List of blocks that should always be disabled.
- * @type {!Array.}
- * @private
- */
- this.permanentlyDisabled_ = [];
-
- /**
- * y coordinate of mousedown - used to calculate scroll distances.
- * @private {number}
- */
- this.startDragMouseY_ = 0;
-
- /**
- * x coordinate of mousedown - used to calculate scroll distances.
- * @private {number}
- */
- this.startDragMouseX_ = 0;
-};
-
-/**
- * When a flyout drag is in progress, this is a reference to the flyout being
- * dragged. This is used by Flyout.terminateDrag_ to reset dragMode_.
- * @private {Blockly.Flyout}
- */
-Blockly.Flyout.startFlyout_ = null;
-
-/**
- * Event that started a drag. Used to determine the drag distance/direction and
- * also passed to BlockSvg.onMouseDown_() after creating a new block.
- * @private {Event}
- */
-Blockly.Flyout.startDownEvent_ = null;
-
-/**
- * Flyout block where the drag/click was initiated. Used to fire click events or
- * create a new block.
- * @private {Event}
- */
-Blockly.Flyout.startBlock_ = null;
-
-/**
- * Wrapper function called when a mouseup occurs during a background or block
- * drag operation.
- * @private {Array.}
- */
-Blockly.Flyout.onMouseUpWrapper_ = null;
-
-/**
- * Wrapper function called when a mousemove occurs during a background drag.
- * @private {Array.}
- */
-Blockly.Flyout.onMouseMoveWrapper_ = null;
-
-/**
- * Wrapper function called when a mousemove occurs during a block drag.
- * @private {Array.}
- */
-Blockly.Flyout.onMouseMoveBlockWrapper_ = null;
-
-/**
- * Does the flyout automatically close when a block is created?
- * @type {boolean}
- */
-Blockly.Flyout.prototype.autoClose = true;
-
-/**
- * Corner radius of the flyout background.
- * @type {number}
- * @const
- */
-Blockly.Flyout.prototype.CORNER_RADIUS = 8;
-
-/**
- * Number of pixels the mouse must move before a drag/scroll starts. Because the
- * drag-intention is determined when this is reached, it is larger than
- * Blockly.DRAG_RADIUS so that the drag-direction is clearer.
- */
-Blockly.Flyout.prototype.DRAG_RADIUS = 10;
-
-/**
- * Margin around the edges of the blocks in the flyout.
- * @type {number}
- * @const
- */
-Blockly.Flyout.prototype.MARGIN = Blockly.Flyout.prototype.CORNER_RADIUS;
-
-/**
- * Gap between items in horizontal flyouts. Can be overridden with the "sep"
- * element.
- * @const {number}
- */
-Blockly.Flyout.prototype.GAP_X = Blockly.Flyout.prototype.MARGIN * 3;
-
-/**
- * Gap between items in vertical flyouts. Can be overridden with the "sep"
- * element.
- * @const {number}
- */
-Blockly.Flyout.prototype.GAP_Y = Blockly.Flyout.prototype.MARGIN * 3;
-
-/**
- * Top/bottom padding between scrollbar and edge of flyout background.
- * @type {number}
- * @const
- */
-Blockly.Flyout.prototype.SCROLLBAR_PADDING = 2;
-
-/**
- * Width of flyout.
- * @type {number}
- * @private
- */
-Blockly.Flyout.prototype.width_ = 0;
-
-/**
- * Height of flyout.
- * @type {number}
- * @private
- */
-Blockly.Flyout.prototype.height_ = 0;
-
-/**
- * Is the flyout dragging (scrolling)?
- * DRAG_NONE - no drag is ongoing or state is undetermined.
- * DRAG_STICKY - still within the sticky drag radius.
- * DRAG_FREE - in scroll mode (never create a new block).
- * @private
- */
-Blockly.Flyout.prototype.dragMode_ = Blockly.DRAG_NONE;
-
-/**
- * Range of a drag angle from a flyout considered "dragging toward workspace".
- * Drags that are within the bounds of this many degrees from the orthogonal
- * line to the flyout edge are considered to be "drags toward the workspace".
- * Example:
- * Flyout Edge Workspace
- * [block] / <-within this angle, drags "toward workspace" |
- * [block] ---- orthogonal to flyout boundary ---- |
- * [block] \ |
- * The angle is given in degrees from the orthogonal.
- *
- * This is used to know when to create a new block and when to scroll the
- * flyout. Setting it to 360 means that all drags create a new block.
- * @type {number}
- * @private
-*/
-Blockly.Flyout.prototype.dragAngleRange_ = 70;
-
-/**
- * Creates the flyout's DOM. Only needs to be called once.
- * @return {!Element} The flyout's SVG group.
- */
-Blockly.Flyout.prototype.createDom = function() {
- /*
-
-
-
-
- */
- this.svgGroup_ = Blockly.createSvgElement('g',
- {'class': 'blocklyFlyout'}, null);
- this.svgBackground_ = Blockly.createSvgElement('path',
- {'class': 'blocklyFlyoutBackground'}, this.svgGroup_);
- this.svgGroup_.appendChild(this.workspace_.createDom());
- return this.svgGroup_;
-};
-
-/**
- * Initializes the flyout.
- * @param {!Blockly.Workspace} targetWorkspace The workspace in which to create
- * new blocks.
- */
-Blockly.Flyout.prototype.init = function(targetWorkspace) {
- this.targetWorkspace_ = targetWorkspace;
- this.workspace_.targetWorkspace = targetWorkspace;
- // Add scrollbar.
- this.scrollbar_ = new Blockly.Scrollbar(this.workspace_,
- this.horizontalLayout_, false);
-
- this.hide();
-
- Array.prototype.push.apply(this.eventWrappers_,
- Blockly.bindEvent_(this.svgGroup_, 'wheel', this, this.wheel_));
- if (!this.autoClose) {
- this.filterWrapper_ = this.filterForCapacity_.bind(this);
- this.targetWorkspace_.addChangeListener(this.filterWrapper_);
- }
- // Dragging the flyout up and down.
- Array.prototype.push.apply(this.eventWrappers_,
- Blockly.bindEvent_(this.svgGroup_, 'mousedown', this, this.onMouseDown_));
-};
-
-/**
- * Dispose of this flyout.
- * Unlink from all DOM elements to prevent memory leaks.
- */
-Blockly.Flyout.prototype.dispose = function() {
- this.hide();
- Blockly.unbindEvent_(this.eventWrappers_);
- if (this.filterWrapper_) {
- this.targetWorkspace_.removeChangeListener(this.filterWrapper_);
- this.filterWrapper_ = null;
- }
- if (this.scrollbar_) {
- this.scrollbar_.dispose();
- this.scrollbar_ = null;
- }
- if (this.workspace_) {
- this.workspace_.targetWorkspace = null;
- this.workspace_.dispose();
- this.workspace_ = null;
- }
- if (this.svgGroup_) {
- goog.dom.removeNode(this.svgGroup_);
- this.svgGroup_ = null;
- }
- this.svgBackground_ = null;
- this.targetWorkspace_ = null;
-};
-
-/**
- * Get the width of the flyout.
- * @return {number} The width of the flyout.
- */
-Blockly.Flyout.prototype.getWidth = function() {
- return this.width_;
-};
-
-/**
- * Get the height of the flyout.
- * @return {number} The width of the flyout.
- */
-Blockly.Flyout.prototype.getHeight = function() {
- return this.height_;
-};
-
-/**
- * Return an object with all the metrics required to size scrollbars for the
- * flyout. 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 contents,
- * .viewTop: Offset of top edge of visible rectangle from parent,
- * .contentTop: Offset of the top-most content from the y=0 coordinate,
- * .absoluteTop: Top-edge of view.
- * .viewLeft: Offset of the left edge of visible rectangle from parent,
- * .contentLeft: Offset of the left-most content from the x=0 coordinate,
- * .absoluteLeft: Left-edge of view.
- * @return {Object} Contains size and position metrics of the flyout.
- * @private
- */
-Blockly.Flyout.prototype.getMetrics_ = function() {
- if (!this.isVisible()) {
- // Flyout is hidden.
- return null;
- }
-
- try {
- var optionBox = this.workspace_.getCanvas().getBBox();
- } catch (e) {
- // Firefox has trouble with hidden elements (Bug 528969).
- var optionBox = {height: 0, y: 0, width: 0, x: 0};
- }
-
- var absoluteTop = this.SCROLLBAR_PADDING;
- var absoluteLeft = this.SCROLLBAR_PADDING;
- if (this.horizontalLayout_) {
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
- absoluteTop = 0;
- }
- var viewHeight = this.height_;
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) {
- viewHeight += this.MARGIN - this.SCROLLBAR_PADDING;
- }
- var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING;
- } else {
- absoluteLeft = 0;
- var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING;
- var viewWidth = this.width_;
- if (!this.RTL) {
- viewWidth -= this.SCROLLBAR_PADDING;
- }
- }
-
- var metrics = {
- viewHeight: viewHeight,
- viewWidth: viewWidth,
- contentHeight: (optionBox.height + 2 * this.MARGIN) * this.workspace_.scale,
- contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale,
- viewTop: -this.workspace_.scrollY,
- viewLeft: -this.workspace_.scrollX,
- contentTop: optionBox.y,
- contentLeft: optionBox.x,
- absoluteTop: absoluteTop,
- absoluteLeft: absoluteLeft
- };
- return metrics;
-};
-
-/**
- * Sets the translation of the flyout to match the scrollbars.
- * @param {!Object} xyRatio Contains a y property which is a float
- * between 0 and 1 specifying the degree of scrolling and a
- * similar x property.
- * @private
- */
-Blockly.Flyout.prototype.setMetrics_ = function(xyRatio) {
- var metrics = this.getMetrics_();
- // This is a fix to an apparent race condition.
- if (!metrics) {
- return;
- }
- if (!this.horizontalLayout_ && goog.isNumber(xyRatio.y)) {
- this.workspace_.scrollY = -metrics.contentHeight * xyRatio.y;
- } else if (this.horizontalLayout_ && goog.isNumber(xyRatio.x)) {
- this.workspace_.scrollX = -metrics.contentWidth * xyRatio.x;
- }
-
- this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft,
- this.workspace_.scrollY + metrics.absoluteTop);
-};
-
-/**
- * Move the flyout to the edge of the workspace.
- */
-Blockly.Flyout.prototype.position = function() {
- if (!this.isVisible()) {
- return;
- }
- var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics();
- if (!targetWorkspaceMetrics) {
- // Hidden components will return null.
- return;
- }
- var edgeWidth = this.horizontalLayout_ ?
- targetWorkspaceMetrics.viewWidth : this.width_;
- edgeWidth -= this.CORNER_RADIUS;
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) {
- edgeWidth *= -1;
- }
-
- this.setBackgroundPath_(edgeWidth,
- this.horizontalLayout_ ? this.height_ :
- targetWorkspaceMetrics.viewHeight);
-
- var x = targetWorkspaceMetrics.absoluteLeft;
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) {
- x += targetWorkspaceMetrics.viewWidth;
- x -= this.width_;
- }
-
- var y = targetWorkspaceMetrics.absoluteTop;
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
- y += targetWorkspaceMetrics.viewHeight;
- y -= this.height_;
- }
-
- this.svgGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')');
-
- // Record the height for Blockly.Flyout.getMetrics_, or width if the layout is
- // horizontal.
- if (this.horizontalLayout_) {
- this.width_ = targetWorkspaceMetrics.viewWidth;
- } else {
- this.height_ = targetWorkspaceMetrics.viewHeight;
- }
-
- // Update the scrollbar (if one exists).
- if (this.scrollbar_) {
- this.scrollbar_.resize();
- }
-};
-
-/**
- * Create and set the path for the visible boundaries of the flyout.
- * @param {number} width The width of the flyout, not including the
- * rounded corners.
- * @param {number} height The height of the flyout, not including
- * rounded corners.
- * @private
- */
-Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) {
- if (this.horizontalLayout_) {
- this.setBackgroundPathHorizontal_(width, height);
- } else {
- this.setBackgroundPathVertical_(width, height);
- }
-};
-
-/**
- * Create and set the path for the visible boundaries of the flyout in vertical
- * mode.
- * @param {number} width The width of the flyout, not including the
- * rounded corners.
- * @param {number} height The height of the flyout, not including
- * rounded corners.
- * @private
- */
-Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) {
- var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT;
- // Decide whether to start on the left or right.
- var path = ['M ' + (atRight ? this.width_ : 0) + ',0'];
- // Top.
- path.push('h', width);
- // Rounded corner.
- path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
- atRight ? 0 : 1,
- atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS,
- this.CORNER_RADIUS);
- // Side closest to workspace.
- path.push('v', Math.max(0, height - this.CORNER_RADIUS * 2));
- // Rounded corner.
- path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
- atRight ? 0 : 1,
- atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS,
- this.CORNER_RADIUS);
- // Bottom.
- path.push('h', -width);
- path.push('z');
- this.svgBackground_.setAttribute('d', path.join(' '));
-};
-
-/**
- * Create and set the path for the visible boundaries of the flyout in
- * horizontal mode.
- * @param {number} width The width of the flyout, not including the
- * rounded corners.
- * @param {number} height The height of the flyout, not including
- * rounded corners.
- * @private
- */
-Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = function(width,
- height) {
- var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP;
- // Start at top left.
- var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)];
-
- if (atTop) {
- // Top.
- path.push('h', width + this.CORNER_RADIUS);
- // Right.
- path.push('v', height);
- // Bottom.
- path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
- -this.CORNER_RADIUS, this.CORNER_RADIUS);
- path.push('h', -1 * (width - this.CORNER_RADIUS));
- // Left.
- path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
- -this.CORNER_RADIUS, -this.CORNER_RADIUS);
- path.push('z');
- } else {
- // Top.
- path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
- this.CORNER_RADIUS, -this.CORNER_RADIUS);
- path.push('h', width - this.CORNER_RADIUS);
- // Right.
- path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
- this.CORNER_RADIUS, this.CORNER_RADIUS);
- path.push('v', height - this.CORNER_RADIUS);
- // Bottom.
- path.push('h', -width - this.CORNER_RADIUS);
- // Left.
- path.push('z');
- }
- this.svgBackground_.setAttribute('d', path.join(' '));
-};
-
-/**
- * Scroll the flyout to the top.
- */
-Blockly.Flyout.prototype.scrollToStart = function() {
- this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? Infinity : 0);
-};
-
-/**
- * Scroll the flyout.
- * @param {!Event} e Mouse wheel scroll event.
- * @private
- */
-Blockly.Flyout.prototype.wheel_ = function(e) {
- var delta = this.horizontalLayout_ ? e.deltaX : e.deltaY;
-
- if (delta) {
- if (goog.userAgent.GECKO) {
- // Firefox's deltas are a tenth that of Chrome/Safari.
- delta *= 10;
- }
- var metrics = this.getMetrics_();
- var pos = this.horizontalLayout_ ? metrics.viewLeft + delta :
- metrics.viewTop + delta;
- var limit = this.horizontalLayout_ ?
- metrics.contentWidth - metrics.viewWidth :
- metrics.contentHeight - metrics.viewHeight;
- pos = Math.min(pos, limit);
- pos = Math.max(pos, 0);
- this.scrollbar_.set(pos);
- }
-
- // Don't scroll the page.
- e.preventDefault();
- // Don't propagate mousewheel event (zooming).
- e.stopPropagation();
-};
-
-/**
- * Is the flyout visible?
- * @return {boolean} True if visible.
- */
-Blockly.Flyout.prototype.isVisible = function() {
- return this.svgGroup_ && this.svgGroup_.style.display == 'block';
-};
-
-/**
- * Hide and empty the flyout.
- */
-Blockly.Flyout.prototype.hide = function() {
- if (!this.isVisible()) {
- return;
- }
- this.svgGroup_.style.display = 'none';
- // Delete all the event listeners.
- for (var x = 0, listen; listen = this.listeners_[x]; x++) {
- Blockly.unbindEvent_(listen);
- }
- this.listeners_.length = 0;
- if (this.reflowWrapper_) {
- this.workspace_.removeChangeListener(this.reflowWrapper_);
- this.reflowWrapper_ = null;
- }
- // Do NOT delete the blocks here. Wait until Flyout.show.
- // https://neil.fraser.name/news/2014/08/09/
-};
-
-/**
- * Show and populate the flyout.
- * @param {!Array|string} xmlList List of blocks to show.
- * Variables and procedures have a custom set of blocks.
- */
-Blockly.Flyout.prototype.show = function(xmlList) {
- this.hide();
- this.clearOldBlocks_();
-
- if (xmlList == Blockly.Variables.NAME_TYPE) {
- // Special category for variables.
- xmlList =
- Blockly.Variables.flyoutCategory(this.workspace_.targetWorkspace);
- } else if (xmlList == Blockly.Procedures.NAME_TYPE) {
- // Special category for procedures.
- xmlList =
- Blockly.Procedures.flyoutCategory(this.workspace_.targetWorkspace);
- }
-
- this.svgGroup_.style.display = 'block';
- // Create the blocks to be shown in this flyout.
- var contents = [];
- var gaps = [];
- this.permanentlyDisabled_.length = 0;
- for (var i = 0, xml; xml = xmlList[i]; i++) {
- if (xml.tagName) {
- var tagName = xml.tagName.toUpperCase();
- var default_gap = this.horizontalLayout_ ? this.GAP_X : this.GAP_Y;
- if (tagName == 'BLOCK') {
- var curBlock = Blockly.Xml.domToBlock(xml, this.workspace_);
- if (curBlock.disabled) {
- // Record blocks that were initially disabled.
- // Do not enable these blocks as a result of capacity filtering.
- this.permanentlyDisabled_.push(curBlock);
- }
- contents.push({type: 'block', block: curBlock});
- var gap = parseInt(xml.getAttribute('gap'), 10);
- gaps.push(isNaN(gap) ? default_gap : gap);
- } else if (xml.tagName.toUpperCase() == 'SEP') {
- // Change the gap between two blocks.
- //
- // The default gap is 24, can be set larger or smaller.
- // This overwrites the gap attribute on the previous block.
- // Note that a deprecated method is to add a gap to a block.
- //
- var newGap = parseInt(xml.getAttribute('gap'), 10);
- // Ignore gaps before the first block.
- if (!isNaN(newGap) && gaps.length > 0) {
- gaps[gaps.length - 1] = newGap;
- } else {
- gaps.push(default_gap);
- }
- } else if (tagName == 'BUTTON') {
- var label = xml.getAttribute('text');
- var curButton = new Blockly.FlyoutButton(this.workspace_,
- this.targetWorkspace_, label);
- contents.push({type: 'button', button: curButton});
- gaps.push(default_gap);
- }
- }
- }
-
- this.layout_(contents, gaps);
-
- // IE 11 is an incompetent browser that fails to fire mouseout events.
- // When the mouse is over the background, deselect all blocks.
- var deselectAll = function() {
- var topBlocks = this.workspace_.getTopBlocks(false);
- for (var i = 0, block; block = topBlocks[i]; i++) {
- block.removeSelect();
- }
- };
-
- this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover',
- this, deselectAll));
-
- if (this.horizontalLayout_) {
- this.height_ = 0;
- } else {
- this.width_ = 0;
- }
- this.reflow();
-
- this.filterForCapacity_();
-
- // Correctly position the flyout's scrollbar when it opens.
- this.position();
-
- this.reflowWrapper_ = this.reflow.bind(this);
- this.workspace_.addChangeListener(this.reflowWrapper_);
-};
-
-/**
- * Lay out the blocks in the flyout.
- * @param {!Array.} contents The blocks and buttons to lay out.
- * @param {!Array.} gaps The visible gaps between blocks.
- * @private
- */
-Blockly.Flyout.prototype.layout_ = function(contents, gaps) {
- this.workspace_.scale = this.targetWorkspace_.scale;
- var margin = this.MARGIN;
- var cursorX = this.RTL ? margin : margin + Blockly.BlockSvg.TAB_WIDTH;
- var cursorY = margin;
- if (this.horizontalLayout_ && this.RTL) {
- contents = contents.reverse();
- }
-
- for (var i = 0, item; item = contents[i]; i++) {
- if (item.type == 'block') {
- var block = item.block;
- var allBlocks = block.getDescendants();
- for (var j = 0, child; child = allBlocks[j]; j++) {
- // Mark blocks as being inside a flyout. This is used to detect and
- // prevent the closure of the flyout if the user right-clicks on such a
- // block.
- child.isInFlyout = true;
- }
- block.render();
- var root = block.getSvgRoot();
- var blockHW = block.getHeightWidth();
- var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0;
- if (this.horizontalLayout_) {
- cursorX += tab;
- }
- block.moveBy((this.horizontalLayout_ && this.RTL) ?
- cursorX + blockHW.width - tab : cursorX,
- cursorY);
- if (this.horizontalLayout_) {
- cursorX += (blockHW.width + gaps[i] - tab);
- } else {
- cursorY += blockHW.height + gaps[i];
- }
-
- // Create an invisible rectangle under the block to act as a button. Just
- // using the block as a button is poor, since blocks have holes in them.
- var rect = Blockly.createSvgElement('rect', {'fill-opacity': 0}, null);
- rect.tooltip = block;
- Blockly.Tooltip.bindMouseEvents(rect);
- // Add the rectangles under the blocks, so that the blocks' tooltips work.
- this.workspace_.getCanvas().insertBefore(rect, block.getSvgRoot());
- block.flyoutRect_ = rect;
- this.backgroundButtons_[i] = rect;
-
- this.addBlockListeners_(root, block, rect);
- } else if (item.type == 'button') {
- var button = item.button;
- var buttonSvg = button.createDom();
- button.moveTo(cursorX, cursorY);
- button.show();
- Blockly.bindEvent_(buttonSvg, 'mouseup', button, button.onMouseUp);
-
- this.buttons_.push(button);
- if (this.horizontalLayout_) {
- cursorX += (button.width + gaps[i]);
- } else {
- cursorY += button.height + gaps[i];
- }
- }
- }
-};
-
-/**
- * Delete blocks and background buttons from a previous showing of the flyout.
- * @private
- */
-Blockly.Flyout.prototype.clearOldBlocks_ = function() {
- // Delete any blocks from a previous showing.
- var oldBlocks = this.workspace_.getTopBlocks(false);
- for (var i = 0, block; block = oldBlocks[i]; i++) {
- if (block.workspace == this.workspace_) {
- block.dispose(false, false);
- }
- }
- // Delete any background buttons from a previous showing.
- for (var j = 0, rect; rect = this.backgroundButtons_[j]; j++) {
- goog.dom.removeNode(rect);
- }
- this.backgroundButtons_.length = 0;
-
- for (var i = 0, button; button = this.buttons_[i]; i++) {
- button.dispose();
- }
- this.buttons_.length = 0;
-};
-
-/**
- * Add listeners to a block that has been added to the flyout.
- * @param {!Element} root The root node of the SVG group the block is in.
- * @param {!Blockly.Block} block The block to add listeners for.
- * @param {!Element} rect The invisible rectangle under the block that acts as
- * a button for that block.
- * @private
- */
-Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) {
- this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null,
- this.blockMouseDown_(block)));
- this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null,
- this.blockMouseDown_(block)));
- this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block,
- block.addSelect));
- this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block,
- block.removeSelect));
- this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block,
- block.addSelect));
- this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block,
- block.removeSelect));
-};
-
-/**
- * Handle a mouse-down on an SVG block in a non-closing flyout.
- * @param {!Blockly.Block} block The flyout block to copy.
- * @return {!Function} Function to call when block is clicked.
- * @private
- */
-Blockly.Flyout.prototype.blockMouseDown_ = function(block) {
- var flyout = this;
- return function(e) {
- Blockly.terminateDrag_();
- Blockly.hideChaff(true);
- if (Blockly.isRightButton(e)) {
- // Right-click.
- block.showContextMenu_(e);
- } else {
- // Left-click (or middle click)
- Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);
- // Record the current mouse position.
- flyout.startDragMouseY_ = e.clientY;
- flyout.startDragMouseX_ = e.clientX;
- Blockly.Flyout.startDownEvent_ = e;
- Blockly.Flyout.startBlock_ = block;
- Blockly.Flyout.startFlyout_ = flyout;
- Blockly.Flyout.onMouseUpWrapper_ = Blockly.bindEvent_(document,
- 'mouseup', flyout, flyout.onMouseUp_);
- Blockly.Flyout.onMouseMoveBlockWrapper_ = Blockly.bindEvent_(document,
- 'mousemove', flyout, flyout.onMouseMoveBlock_);
- }
- // This event has been handled. No need to bubble up to the document.
- e.stopPropagation();
- e.preventDefault();
- };
-};
-
-/**
- * Mouse down on the flyout background. Start a vertical scroll drag.
- * @param {!Event} e Mouse down event.
- * @private
- */
-Blockly.Flyout.prototype.onMouseDown_ = function(e) {
- if (Blockly.isRightButton(e)) {
- return;
- }
- Blockly.hideChaff(true);
- this.dragMode_ = Blockly.DRAG_FREE;
- this.startDragMouseY_ = e.clientY;
- this.startDragMouseX_ = e.clientX;
- Blockly.Flyout.startFlyout_ = this;
- Blockly.Flyout.onMouseMoveWrapper_ = Blockly.bindEvent_(document, 'mousemove',
- this, this.onMouseMove_);
- Blockly.Flyout.onMouseUpWrapper_ = Blockly.bindEvent_(document, 'mouseup',
- this, Blockly.Flyout.terminateDrag_);
- // This event has been handled. No need to bubble up to the document.
- e.preventDefault();
- 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.Flyout.prototype.onMouseUp_ = function(e) {
- if (!this.workspace_.isDragging()) {
- if (this.autoClose) {
- this.createBlockFunc_(Blockly.Flyout.startBlock_)(
- Blockly.Flyout.startDownEvent_);
- } else if (!Blockly.WidgetDiv.isVisible()) {
- Blockly.Events.fire(
- new Blockly.Events.Ui(Blockly.Flyout.startBlock_, 'click',
- undefined, undefined));
- }
- }
- Blockly.terminateDrag_();
-};
-
-/**
- * Handle a mouse-move to vertically drag the flyout.
- * @param {!Event} e Mouse move event.
- * @private
- */
-Blockly.Flyout.prototype.onMouseMove_ = function(e) {
- var metrics = this.getMetrics_();
- if (this.horizontalLayout_) {
- if (metrics.contentWidth - metrics.viewWidth < 0) {
- return;
- }
- var dx = e.clientX - this.startDragMouseX_;
- this.startDragMouseX_ = e.clientX;
- var x = metrics.viewLeft - dx;
- x = goog.math.clamp(x, 0, metrics.contentWidth - metrics.viewWidth);
- this.scrollbar_.set(x);
- } else {
- if (metrics.contentHeight - metrics.viewHeight < 0) {
- return;
- }
- var dy = e.clientY - this.startDragMouseY_;
- this.startDragMouseY_ = e.clientY;
- var y = metrics.viewTop - dy;
- y = goog.math.clamp(y, 0, metrics.contentHeight - metrics.viewHeight);
- this.scrollbar_.set(y);
- }
-};
-
-/**
- * Mouse button is down on a block in a non-closing flyout. Create the block
- * if the mouse moves beyond a small radius. This allows one to play with
- * fields without instantiating blocks that instantly self-destruct.
- * @param {!Event} e Mouse move event.
- * @private
- */
-Blockly.Flyout.prototype.onMouseMoveBlock_ = function(e) {
- 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 dx = e.clientX - Blockly.Flyout.startDownEvent_.clientX;
- var dy = e.clientY - Blockly.Flyout.startDownEvent_.clientY;
-
- var createBlock = this.determineDragIntention_(dx, dy);
- if (createBlock) {
- this.createBlockFunc_(Blockly.Flyout.startBlock_)(
- Blockly.Flyout.startDownEvent_);
- } else if (this.dragMode_ == Blockly.DRAG_FREE) {
- // Do a scroll.
- this.onMouseMove_(e);
- }
- e.stopPropagation();
-};
-
-/**
- * Determine the intention of a drag.
- * Updates dragMode_ based on a drag delta and the current mode,
- * and returns true if we should create a new block.
- * @param {number} dx X delta of the drag.
- * @param {number} dy Y delta of the drag.
- * @return {boolean} True if a new block should be created.
- * @private
- */
-Blockly.Flyout.prototype.determineDragIntention_ = function(dx, dy) {
- if (this.dragMode_ == Blockly.DRAG_FREE) {
- // Once in free mode, always stay in free mode and never create a block.
- return false;
- }
- var dragDistance = Math.sqrt(dx * dx + dy * dy);
- if (dragDistance < this.DRAG_RADIUS) {
- // Still within the sticky drag radius.
- this.dragMode_ = Blockly.DRAG_STICKY;
- return false;
- } else {
- if (this.isDragTowardWorkspace_(dx, dy) || !this.scrollbar_.isVisible()) {
- // Immediately create a block.
- return true;
- } else {
- // Immediately move to free mode - the drag is away from the workspace.
- this.dragMode_ = Blockly.DRAG_FREE;
- return false;
- }
- }
-};
-
-/**
- * Determine if a drag delta is toward the workspace, based on the position
- * and orientation of the flyout. This is used in determineDragIntention_ to
- * determine if a new block should be created or if the flyout should scroll.
- * @param {number} dx X delta of the drag.
- * @param {number} dy Y delta of the drag.
- * @return {boolean} true if the drag is toward the workspace.
- * @private
- */
-Blockly.Flyout.prototype.isDragTowardWorkspace_ = function(dx, dy) {
- // Direction goes from -180 to 180, with 0 toward the right and 90 on top.
- var dragDirection = Math.atan2(dy, dx) / Math.PI * 180;
-
- var draggingTowardWorkspace = false;
- var range = this.dragAngleRange_;
- if (this.horizontalLayout_) {
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) {
- // Horizontal at top.
- if (dragDirection < 90 + range && dragDirection > 90 - range) {
- draggingTowardWorkspace = true;
- }
- } else {
- // Horizontal at bottom.
- if (dragDirection > -90 - range && dragDirection < -90 + range) {
- draggingTowardWorkspace = true;
- }
- }
- } else {
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) {
- // Vertical at left.
- if (dragDirection < range && dragDirection > -range) {
- draggingTowardWorkspace = true;
- }
- } else {
- // Vertical at right.
- if (dragDirection < -180 + range || dragDirection > 180 - range) {
- draggingTowardWorkspace = true;
- }
- }
- }
- return draggingTowardWorkspace;
-};
-
-/**
- * Create a copy of this block on the workspace.
- * @param {!Blockly.Block} originBlock The flyout block to copy.
- * @return {!Function} Function to call when block is clicked.
- * @private
- */
-Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) {
- var flyout = this;
- return function(e) {
- if (Blockly.isRightButton(e)) {
- // Right-click. Don't create a block, let the context menu show.
- return;
- }
- if (originBlock.disabled) {
- // Beyond capacity.
- return;
- }
- Blockly.Events.disable();
- try {
- var block = flyout.placeNewBlock_(originBlock);
- } finally {
- Blockly.Events.enable();
- }
- if (Blockly.Events.isEnabled()) {
- Blockly.Events.setGroup(true);
- Blockly.Events.fire(new Blockly.Events.Create(block));
- }
- if (flyout.autoClose) {
- flyout.hide();
- } else {
- flyout.filterForCapacity_();
- }
- // Start a dragging operation on the new block.
- block.onMouseDown_(e);
- Blockly.dragMode_ = Blockly.DRAG_FREE;
- block.setDragging_(true);
- };
-};
-
-/**
- * Copy a block from the flyout to the workspace and position it correctly.
- * @param {!Blockly.Block} originBlock The flyout block to copy..
- * @return {!Blockly.Block} The new block in the main workspace.
- * @private
- */
-Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock) {
- var targetWorkspace = this.targetWorkspace_;
- var svgRootOld = originBlock.getSvgRoot();
- if (!svgRootOld) {
- throw 'originBlock is not rendered.';
- }
- // Figure out where the original block is on the screen, relative to the upper
- // left corner of the main workspace.
- var xyOld = Blockly.getSvgXY_(svgRootOld, targetWorkspace);
- // Take into account that the flyout might have been scrolled horizontally
- // (separately from the main workspace).
- // Generally a no-op in vertical mode but likely to happen in horizontal
- // mode.
- var scrollX = this.workspace_.scrollX;
- var scale = this.workspace_.scale;
- xyOld.x += scrollX / scale - scrollX;
- // If the flyout is on the right side, (0, 0) in the flyout is offset to
- // the right of (0, 0) in the main workspace. Add an offset to take that
- // into account.
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) {
- scrollX = targetWorkspace.getMetrics().viewWidth - this.width_;
- scale = targetWorkspace.scale;
- // Scale the scroll (getSvgXY_ did not do this).
- xyOld.x += scrollX / scale - scrollX;
- }
-
- // Take into account that the flyout might have been scrolled vertically
- // (separately from the main workspace).
- // Generally a no-op in horizontal mode but likely to happen in vertical
- // mode.
- var scrollY = this.workspace_.scrollY;
- scale = this.workspace_.scale;
- xyOld.y += scrollY / scale - scrollY;
- // If the flyout is on the bottom, (0, 0) in the flyout is offset to be below
- // (0, 0) in the main workspace. Add an offset to take that into account.
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
- scrollY = targetWorkspace.getMetrics().viewHeight - this.height_;
- scale = targetWorkspace.scale;
- xyOld.y += scrollY / scale - scrollY;
- }
-
- // Create the new block by cloning the block in the flyout (via XML).
- var xml = Blockly.Xml.blockToDom(originBlock);
- var block = Blockly.Xml.domToBlock(xml, targetWorkspace);
- var svgRootNew = block.getSvgRoot();
- if (!svgRootNew) {
- throw 'block is not rendered.';
- }
- // Figure out where the new block got placed on the screen, relative to the
- // upper left corner of the workspace. This may not be the same as the
- // original block because the flyout's origin may not be the same as the
- // main workspace's origin.
- var xyNew = Blockly.getSvgXY_(svgRootNew, targetWorkspace);
- // Scale the scroll (getSvgXY_ did not do this).
- xyNew.x +=
- targetWorkspace.scrollX / targetWorkspace.scale - targetWorkspace.scrollX;
- xyNew.y +=
- targetWorkspace.scrollY / targetWorkspace.scale - targetWorkspace.scrollY;
- // If the flyout is collapsible and the workspace can't be scrolled.
- if (targetWorkspace.toolbox_ && !targetWorkspace.scrollbar) {
- xyNew.x += targetWorkspace.toolbox_.getWidth() / targetWorkspace.scale;
- xyNew.y += targetWorkspace.toolbox_.getHeight() / targetWorkspace.scale;
- }
-
- // Move the new block to where the old block is.
- block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y);
- return block;
-};
-
-/**
- * Filter the blocks on the flyout to disable the ones that are above the
- * capacity limit.
- * @private
- */
-Blockly.Flyout.prototype.filterForCapacity_ = function() {
- var remainingCapacity = this.targetWorkspace_.remainingCapacity();
- var blocks = this.workspace_.getTopBlocks(false);
- for (var i = 0, block; block = blocks[i]; i++) {
- if (this.permanentlyDisabled_.indexOf(block) == -1) {
- var allBlocks = block.getDescendants();
- block.setDisabled(allBlocks.length > remainingCapacity);
- }
- }
-};
-
-/**
- * Return the deletion rectangle for this flyout.
- * @return {goog.math.Rect} Rectangle in which to delete.
- */
-Blockly.Flyout.prototype.getClientRect = function() {
- if (!this.svgGroup_) {
- return null;
- }
-
- var flyoutRect = this.svgGroup_.getBoundingClientRect();
- // BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout
- // area are still deleted. Must be larger than the largest screen size,
- // but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
- var BIG_NUM = 1000000000;
- var x = flyoutRect.left;
- var y = flyoutRect.top;
- var width = flyoutRect.width;
- var height = flyoutRect.height;
-
- if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) {
- return new goog.math.Rect(-BIG_NUM, y - BIG_NUM, BIG_NUM * 2,
- BIG_NUM + height);
- } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
- return new goog.math.Rect(-BIG_NUM, y, BIG_NUM * 2,
- BIG_NUM + height);
- } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) {
- return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + width,
- BIG_NUM * 2);
- } else { // Right
- return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, BIG_NUM * 2);
- }
-};
-
-/**
- * Stop binding to the global mouseup and mousemove events.
- * @private
- */
-Blockly.Flyout.terminateDrag_ = function() {
- if (Blockly.Flyout.startFlyout_) {
- Blockly.Flyout.startFlyout_.dragMode_ = Blockly.DRAG_NONE;
- }
- if (Blockly.Flyout.onMouseUpWrapper_) {
- Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_);
- Blockly.Flyout.onMouseUpWrapper_ = null;
- }
- if (Blockly.Flyout.onMouseMoveBlockWrapper_) {
- Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveBlockWrapper_);
- Blockly.Flyout.onMouseMoveBlockWrapper_ = null;
- }
- if (Blockly.Flyout.onMouseMoveWrapper_) {
- Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveWrapper_);
- Blockly.Flyout.onMouseMoveWrapper_ = null;
- }
- Blockly.Flyout.startDownEvent_ = null;
- Blockly.Flyout.startBlock_ = null;
- Blockly.Flyout.startFlyout_ = null;
-};
-
-/**
- * Compute height of flyout. Position button under each block.
- * For RTL: Lay out the blocks right-aligned.
- * @param {!Array} blocks The blocks to reflow.
- */
-Blockly.Flyout.prototype.reflowHorizontal = function(blocks) {
- this.workspace_.scale = this.targetWorkspace_.scale;
- var flyoutHeight = 0;
- for (var i = 0, block; block = blocks[i]; i++) {
- flyoutHeight = Math.max(flyoutHeight, block.getHeightWidth().height);
- }
- flyoutHeight += this.MARGIN * 1.5;
- flyoutHeight *= this.workspace_.scale;
- flyoutHeight += Blockly.Scrollbar.scrollbarThickness;
- if (this.height_ != flyoutHeight) {
- for (var i = 0, block; block = blocks[i]; i++) {
- var blockHW = block.getHeightWidth();
- if (block.flyoutRect_) {
- block.flyoutRect_.setAttribute('width', blockHW.width);
- block.flyoutRect_.setAttribute('height', blockHW.height);
- // Rectangles behind blocks with output tabs are shifted a bit.
- var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0;
- var blockXY = block.getRelativeToSurfaceXY();
- block.flyoutRect_.setAttribute('y', blockXY.y);
- block.flyoutRect_.setAttribute('x',
- this.RTL ? blockXY.x - blockHW.width + tab : blockXY.x - tab);
- // For hat blocks we want to shift them down by the hat height
- // since the y coordinate is the corner, not the top of the hat.
- var hatOffset =
- block.startHat_ ? Blockly.BlockSvg.START_HAT_HEIGHT : 0;
- if (hatOffset) {
- block.moveBy(0, hatOffset);
- }
- block.flyoutRect_.setAttribute('y', blockXY.y);
- }
- }
- // Record the height for .getMetrics_ and .position.
- this.height_ = flyoutHeight;
- // Call this since it is possible the trash and zoom buttons need
- // to move. e.g. on a bottom positioned flyout when zoom is clicked.
- this.targetWorkspace_.resize();
- }
-};
-
-/**
- * Compute width of flyout. Position button under each block.
- * For RTL: Lay out the blocks right-aligned.
- * @param {!Array} blocks The blocks to reflow.
- */
-Blockly.Flyout.prototype.reflowVertical = function(blocks) {
- this.workspace_.scale = this.targetWorkspace_.scale;
- var flyoutWidth = 0;
- for (var i = 0, block; block = blocks[i]; i++) {
- var width = block.getHeightWidth().width;
- if (block.outputConnection) {
- width -= Blockly.BlockSvg.TAB_WIDTH;
- }
- flyoutWidth = Math.max(flyoutWidth, width);
- }
- for (var i = 0, button; button = this.buttons_[i]; i++) {
- flyoutWidth = Math.max(flyoutWidth, button.width);
- }
- flyoutWidth += this.MARGIN * 1.5 + Blockly.BlockSvg.TAB_WIDTH;
- flyoutWidth *= this.workspace_.scale;
- flyoutWidth += Blockly.Scrollbar.scrollbarThickness;
- if (this.width_ != flyoutWidth) {
- for (var i = 0, block; block = blocks[i]; i++) {
- var blockHW = block.getHeightWidth();
- if (this.RTL) {
- // With the flyoutWidth known, right-align the blocks.
- var oldX = block.getRelativeToSurfaceXY().x;
- var newX = flyoutWidth / this.workspace_.scale - this.MARGIN;
- newX -= Blockly.BlockSvg.TAB_WIDTH;
- block.moveBy(newX - oldX, 0);
- }
- if (block.flyoutRect_) {
- block.flyoutRect_.setAttribute('width', blockHW.width);
- block.flyoutRect_.setAttribute('height', blockHW.height);
- // Blocks with output tabs are shifted a bit.
- var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0;
- var blockXY = block.getRelativeToSurfaceXY();
- block.flyoutRect_.setAttribute('x',
- this.RTL ? blockXY.x - blockHW.width + tab : blockXY.x - tab);
- // For hat blocks we want to shift them down by the hat height
- // since the y coordinate is the corner, not the top of the hat.
- var hatOffset =
- block.startHat_ ? Blockly.BlockSvg.START_HAT_HEIGHT : 0;
- if (hatOffset) {
- block.moveBy(0, hatOffset);
- }
- block.flyoutRect_.setAttribute('y', blockXY.y);
- }
- }
- // Record the width for .getMetrics_ and .position.
- this.width_ = flyoutWidth;
- // Call this since it is possible the trash and zoom buttons need
- // to move. e.g. on a bottom positioned flyout when zoom is clicked.
- this.targetWorkspace_.resize();
- }
-};
-
-/**
- * Reflow blocks and their buttons.
- */
-Blockly.Flyout.prototype.reflow = function() {
- if (this.reflowWrapper_) {
- this.workspace_.removeChangeListener(this.reflowWrapper_);
- }
- var blocks = this.workspace_.getTopBlocks(false);
- if (this.horizontalLayout_) {
- this.reflowHorizontal(blocks);
- } else {
- this.reflowVertical(blocks);
- }
- if (this.reflowWrapper_) {
- this.workspace_.addChangeListener(this.reflowWrapper_);
- }
-};
diff --git a/core/inject.js.orig b/core/inject.js.orig
deleted file mode 100644
index 57e3e51f4..000000000
--- a/core/inject.js.orig
+++ /dev/null
@@ -1,378 +0,0 @@
-/**
- * @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 Functions for injecting Blockly into a web page.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.inject');
-
-goog.require('Blockly.Css');
-goog.require('Blockly.Options');
-goog.require('Blockly.WorkspaceSvg');
-goog.require('goog.dom');
-goog.require('goog.ui.Component');
-goog.require('goog.userAgent');
-
-
-/**
- * Inject a Blockly editor into the specified container element (usually a div).
- * @param {!Element|string} container Containing element, or its ID,
- * or a CSS selector.
- * @param {Object=} opt_options Optional dictionary of options.
- * @return {!Blockly.Workspace} Newly created main workspace.
- */
-Blockly.inject = function(container, opt_options) {
- if (goog.isString(container)) {
- container = document.getElementById(container) ||
- document.querySelector(container);
- }
- // Verify that the container is in document.
- if (!goog.dom.contains(document, container)) {
- throw 'Error: container is not in current document.';
- }
- var options = new Blockly.Options(opt_options || {});
- var subContainer = goog.dom.createDom('div', 'injectionDiv');
- container.appendChild(subContainer);
- var svg = Blockly.createDom_(subContainer, options);
- var workspace = Blockly.createMainWorkspace_(svg, options);
- Blockly.init_(workspace);
- workspace.markFocused();
- Blockly.bindEvent_(svg, 'focus', workspace, workspace.markFocused);
- Blockly.svgResize(workspace);
- return workspace;
-};
-
-/**
- * Create the SVG image.
- * @param {!Element} container Containing element.
- * @param {!Blockly.Options} options Dictionary of options.
- * @return {!Element} Newly created SVG image.
- * @private
- */
-Blockly.createDom_ = function(container, options) {
- // Sadly browsers (Chrome vs Firefox) are currently inconsistent in laying
- // out content in RTL mode. Therefore Blockly forces the use of LTR,
- // then manually positions content in RTL as needed.
- container.setAttribute('dir', 'LTR');
- // Closure can be trusted to create HTML widgets with the proper direction.
- goog.ui.Component.setDefaultRightToLeft(options.RTL);
-
- // Load CSS.
- Blockly.Css.inject(options.hasCss, options.pathToMedia);
-
- // Build the SVG DOM.
- /*
-
- */
- var svg = Blockly.createSvgElement('svg', {
- 'xmlns': 'http://www.w3.org/2000/svg',
- 'xmlns:html': 'http://www.w3.org/1999/xhtml',
- 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
- 'version': '1.1',
- 'class': 'blocklySvg'
- }, container);
- /*
-
- ... filters go here ...
-
- */
- var defs = Blockly.createSvgElement('defs', {}, svg);
- var rnd = String(Math.random()).substring(2);
- /*
-
-
-
-
-
-
-
-
- */
- var embossFilter = Blockly.createSvgElement('filter',
- {'id': 'blocklyEmbossFilter' + rnd}, defs);
- Blockly.createSvgElement('feGaussianBlur',
- {'in': 'SourceAlpha', 'stdDeviation': 1, 'result': 'blur'}, embossFilter);
- var feSpecularLighting = Blockly.createSvgElement('feSpecularLighting',
- {'in': 'blur', 'surfaceScale': 1, 'specularConstant': 0.5,
- 'specularExponent': 10, 'lighting-color': 'white', 'result': 'specOut'},
- embossFilter);
- Blockly.createSvgElement('fePointLight',
- {'x': -5000, 'y': -10000, 'z': 20000}, feSpecularLighting);
- Blockly.createSvgElement('feComposite',
- {'in': 'specOut', 'in2': 'SourceAlpha', 'operator': 'in',
- 'result': 'specOut'}, embossFilter);
- Blockly.createSvgElement('feComposite',
- {'in': 'SourceGraphic', 'in2': 'specOut', 'operator': 'arithmetic',
- 'k1': 0, 'k2': 1, 'k3': 1, 'k4': 0}, embossFilter);
- options.embossFilterId = embossFilter.id;
- /*
-
-
-
-
- */
- var disabledPattern = Blockly.createSvgElement('pattern',
- {'id': 'blocklyDisabledPattern' + rnd,
- 'patternUnits': 'userSpaceOnUse',
- 'width': 10, 'height': 10}, defs);
- Blockly.createSvgElement('rect',
- {'width': 10, 'height': 10, 'fill': '#aaa'}, disabledPattern);
- Blockly.createSvgElement('path',
- {'d': 'M 0 0 L 10 10 M 10 0 L 0 10', 'stroke': '#cc0'}, disabledPattern);
- options.disabledPatternId = disabledPattern.id;
- /*
-
-
-
-
- */
- var gridPattern = Blockly.createSvgElement('pattern',
- {'id': 'blocklyGridPattern' + rnd,
- 'patternUnits': 'userSpaceOnUse'}, defs);
- if (options.gridOptions['length'] > 0 && options.gridOptions['spacing'] > 0) {
- Blockly.createSvgElement('line',
- {'stroke': options.gridOptions['colour']},
- gridPattern);
- if (options.gridOptions['length'] > 1) {
- Blockly.createSvgElement('line',
- {'stroke': options.gridOptions['colour']},
- gridPattern);
- }
- // x1, y1, x1, x2 properties will be set later in updateGridPattern_.
- }
- options.gridPattern = gridPattern;
- return svg;
-};
-
-/**
- * Create a main workspace and add it to the SVG.
- * @param {!Element} svg SVG element with pattern defined.
- * @param {!Blockly.Options} options Dictionary of options.
- * @return {!Blockly.Workspace} Newly created main workspace.
- * @private
- */
-Blockly.createMainWorkspace_ = function(svg, options) {
- options.parentWorkspace = null;
- var mainWorkspace = new Blockly.WorkspaceSvg(options);
- mainWorkspace.scale = options.zoomOptions.startScale;
- svg.appendChild(mainWorkspace.createDom('blocklyMainBackground'));
- // A null translation will also apply the correct initial scale.
- mainWorkspace.translate(0, 0);
- mainWorkspace.markFocused();
-
- if (!options.readOnly && !options.hasScrollbars) {
- var workspaceChanged = function() {
- if (Blockly.dragMode_ == Blockly.DRAG_NONE) {
- var metrics = mainWorkspace.getMetrics();
- var edgeLeft = metrics.viewLeft + metrics.absoluteLeft;
- var edgeTop = metrics.viewTop + metrics.absoluteTop;
- if (metrics.contentTop < edgeTop ||
- metrics.contentTop + metrics.contentHeight >
- metrics.viewHeight + edgeTop ||
- metrics.contentLeft <
- (options.RTL ? metrics.viewLeft : edgeLeft) ||
- metrics.contentLeft + metrics.contentWidth > (options.RTL ?
- metrics.viewWidth : metrics.viewWidth + edgeLeft)) {
- // One or more blocks may be out of bounds. Bump them back in.
- var MARGIN = 25;
- var blocks = mainWorkspace.getTopBlocks(false);
- for (var b = 0, block; block = blocks[b]; b++) {
- var blockXY = block.getRelativeToSurfaceXY();
- var blockHW = block.getHeightWidth();
- // Bump any block that's above the top back inside.
- var overflowTop = edgeTop + MARGIN - blockHW.height - blockXY.y;
- if (overflowTop > 0) {
- block.moveBy(0, overflowTop);
- }
- // Bump any block that's below the bottom back inside.
- var overflowBottom =
- edgeTop + metrics.viewHeight - MARGIN - blockXY.y;
- if (overflowBottom < 0) {
- block.moveBy(0, overflowBottom);
- }
- // Bump any block that's off the left back inside.
- var overflowLeft = MARGIN + edgeLeft -
- blockXY.x - (options.RTL ? 0 : blockHW.width);
- if (overflowLeft > 0) {
- block.moveBy(overflowLeft, 0);
- }
- // Bump any block that's off the right back inside.
- var overflowRight = edgeLeft + metrics.viewWidth - MARGIN -
- blockXY.x + (options.RTL ? blockHW.width : 0);
- if (overflowRight < 0) {
- block.moveBy(overflowRight, 0);
- }
- }
- }
- }
- };
- mainWorkspace.addChangeListener(workspaceChanged);
- }
- // The SVG is now fully assembled.
- Blockly.svgResize(mainWorkspace);
- Blockly.WidgetDiv.createDom();
- Blockly.Tooltip.createDom();
- return mainWorkspace;
-};
-
-/**
- * Initialize Blockly with various handlers.
- * @param {!Blockly.Workspace} mainWorkspace Newly created main workspace.
- * @private
- */
-Blockly.init_ = function(mainWorkspace) {
- var options = mainWorkspace.options;
- var svg = mainWorkspace.getParentSvg();
-
- // Supress the browser's context menu.
- Blockly.bindEvent_(svg, 'contextmenu', null,
- function(e) {
- if (!Blockly.isTargetInput_(e)) {
- e.preventDefault();
- }
- });
-
- var workspaceResizeHandler = Blockly.bindEvent_(window, 'resize', null,
- function() {
- Blockly.hideChaff(true);
- Blockly.svgResize(mainWorkspace);
- });
- mainWorkspace.setResizeHandlerWrapper(workspaceResizeHandler);
-
- Blockly.inject.bindDocumentEvents_();
-
- if (options.languageTree) {
- if (mainWorkspace.toolbox_) {
- mainWorkspace.toolbox_.init(mainWorkspace);
- } else if (mainWorkspace.flyout_) {
- // Build a fixed flyout with the root blocks.
- mainWorkspace.flyout_.init(mainWorkspace);
- mainWorkspace.flyout_.show(options.languageTree.childNodes);
- mainWorkspace.flyout_.scrollToStart();
- // Translate the workspace sideways to avoid the fixed flyout.
- mainWorkspace.scrollX = mainWorkspace.flyout_.width_;
- if (options.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) {
- mainWorkspace.scrollX *= -1;
- }
- mainWorkspace.translate(mainWorkspace.scrollX, 0);
- }
- }
-
- if (options.hasScrollbars) {
- mainWorkspace.scrollbar = new Blockly.ScrollbarPair(mainWorkspace);
- mainWorkspace.scrollbar.resize();
- }
-
- // Load the sounds.
- if (options.hasSounds) {
- Blockly.inject.loadSounds_(options.pathToMedia, mainWorkspace);
- }
-};
-
-/**
- * Bind document events, but only once. Destroying and reinjecting Blockly
- * should not bind again.
- * Bind events for scrolling the workspace.
- * Most of these events should be bound to the SVG's surface.
- * However, 'mouseup' has to be on the whole document so that a block dragged
- * out of bounds and released will know that it has been released.
- * Also, 'keydown' has to be on the whole document since the browser doesn't
- * understand a concept of focus on the SVG image.
- * @private
- */
-Blockly.inject.bindDocumentEvents_ = function() {
- if (!Blockly.documentEventsBound_) {
- Blockly.bindEvent_(document, 'keydown', null, Blockly.onKeyDown_);
- Blockly.bindEvent_(document, 'touchend', null, Blockly.longStop_);
- Blockly.bindEvent_(document, 'touchcancel', null, Blockly.longStop_);
- // Don't use bindEvent_ for document's mouseup since that would create a
- // corresponding touch handler that would squeltch the ability to interact
- // with non-Blockly elements.
- document.addEventListener('mouseup', Blockly.onMouseUp_, false);
- // Some iPad versions don't fire resize after portrait to landscape change.
- if (goog.userAgent.IPAD) {
- Blockly.bindEvent_(window, 'orientationchange', document, function() {
- // TODO(#397): Fix for multiple blockly workspaces.
- Blockly.svgResize(Blockly.getMainWorkspace());
- });
- }
- }
- Blockly.documentEventsBound_ = true;
-};
-
-/**
- * Load sounds for the given workspace.
- * @param {string} pathToMedia The path to the media directory.
- * @param {!Blockly.Workspace} workspace The workspace to load sounds for.
- * @private
- */
-Blockly.inject.loadSounds_ = function(pathToMedia, workspace) {
- workspace.loadAudio_(
- [pathToMedia + 'click.mp3',
- pathToMedia + 'click.wav',
- pathToMedia + 'click.ogg'], 'click');
- workspace.loadAudio_(
- [pathToMedia + 'disconnect.wav',
- pathToMedia + 'disconnect.mp3',
- pathToMedia + 'disconnect.ogg'], 'disconnect');
- workspace.loadAudio_(
- [pathToMedia + 'delete.mp3',
- pathToMedia + 'delete.ogg',
- pathToMedia + 'delete.wav'], 'delete');
-
- // Bind temporary hooks that preload the sounds.
- var soundBinds = [];
- var unbindSounds = function() {
- while (soundBinds.length) {
- Blockly.unbindEvent_(soundBinds.pop());
- }
- workspace.preloadAudio_();
- };
- // Android ignores any sound not loaded as a result of a user action.
- soundBinds.push(
- Blockly.bindEvent_(document, 'mousemove', null, unbindSounds));
- soundBinds.push(
- Blockly.bindEvent_(document, 'touchstart', null, unbindSounds));
-};
-
-/**
- * Modify the block tree on the existing toolbox.
- * @param {Node|string} tree DOM tree of blocks, or text representation of same.
- */
-Blockly.updateToolbox = function(tree) {
- console.warn('Deprecated call to Blockly.updateToolbox, ' +
- 'use workspace.updateToolbox instead.');
- Blockly.getMainWorkspace().updateToolbox(tree);
-};
diff --git a/core/input.js.orig b/core/input.js.orig
deleted file mode 100644
index 4b4eb1df9..000000000
--- a/core/input.js.orig
+++ /dev/null
@@ -1,241 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Object representing an input (value, statement, or dummy).
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Input');
-
-goog.require('Blockly.Connection');
-goog.require('Blockly.FieldLabel');
-goog.require('goog.asserts');
-
-
-/**
- * Class for an input with an optional field.
- * @param {number} type The type of the input.
- * @param {string} name Language-neutral identifier which may used to find this
- * input again.
- * @param {!Blockly.Block} block The block containing this input.
- * @param {Blockly.Connection} connection Optional connection for this input.
- * @constructor
- */
-Blockly.Input = function(type, name, block, connection) {
- /** @type {number} */
- this.type = type;
- /** @type {string} */
- this.name = name;
- /**
- * @type {!Blockly.Block}
- * @private
- */
- this.sourceBlock_ = block;
- /** @type {Blockly.Connection} */
- this.connection = connection;
- /** @type {!Array.} */
- this.fieldRow = [];
-};
-
-/**
- * Alignment of input's fields (left, right or centre).
- * @type {number}
- */
-Blockly.Input.prototype.align = Blockly.ALIGN_LEFT;
-
-/**
- * Is the input visible?
- * @type {boolean}
- * @private
- */
-Blockly.Input.prototype.visible_ = true;
-
-/**
- * Add an item to the end of the input's field row.
- * @param {string|!Blockly.Field} field Something to add as a field.
- * @param {string=} opt_name Language-neutral identifier which may used to find
- * this field again. Should be unique to the host block.
- * @return {!Blockly.Input} The input being append to (to allow chaining).
- */
-Blockly.Input.prototype.appendField = function(field, opt_name) {
- // Empty string, Null or undefined generates no field, unless field is named.
- if (!field && !opt_name) {
- return this;
- }
- // Generate a FieldLabel when given a plain text field.
- if (goog.isString(field)) {
- field = new Blockly.FieldLabel(/** @type {string} */ (field));
- }
- field.setSourceBlock(this.sourceBlock_);
- if (this.sourceBlock_.rendered) {
- field.init();
- }
- field.name = opt_name;
-
- if (field.prefixField) {
- // Add any prefix.
- this.appendField(field.prefixField);
- }
- // Add the field to the field row.
- this.fieldRow.push(field);
- if (field.suffixField) {
- // Add any suffix.
- this.appendField(field.suffixField);
- }
-
- if (this.sourceBlock_.rendered) {
- this.sourceBlock_.render();
- // Adding a field will cause the block to change shape.
- this.sourceBlock_.bumpNeighbours_();
- }
- return this;
-};
-
-/**
- * Add an item to the end of the input's field row.
- * @param {*} field Something to add as a field.
- * @param {string=} opt_name Language-neutral identifier which may used to find
- * this field again. Should be unique to the host block.
- * @return {!Blockly.Input} The input being append to (to allow chaining).
- * @deprecated December 2013
- */
-Blockly.Input.prototype.appendTitle = function(field, opt_name) {
- console.warn('Deprecated call to appendTitle, use appendField instead.');
- return this.appendField(field, opt_name);
-};
-
-/**
- * Remove a field from this input.
- * @param {string} name The name of the field.
- * @throws {goog.asserts.AssertionError} if the field is not present.
- */
-Blockly.Input.prototype.removeField = function(name) {
- for (var i = 0, field; field = this.fieldRow[i]; i++) {
- if (field.name === name) {
- field.dispose();
- this.fieldRow.splice(i, 1);
- if (this.sourceBlock_.rendered) {
- this.sourceBlock_.render();
- // Removing a field will cause the block to change shape.
- this.sourceBlock_.bumpNeighbours_();
- }
- return;
- }
- }
- goog.asserts.fail('Field "%s" not found.', name);
-};
-
-/**
- * Gets whether this input is visible or not.
- * @return {boolean} True if visible.
- */
-Blockly.Input.prototype.isVisible = function() {
- return this.visible_;
-};
-
-/**
- * Sets whether this input is visible or not.
- * Used to collapse/uncollapse a block.
- * @param {boolean} visible True if visible.
- * @return {!Array.} List of blocks to render.
- */
-Blockly.Input.prototype.setVisible = function(visible) {
- var renderList = [];
- if (this.visible_ == visible) {
- return renderList;
- }
- this.visible_ = visible;
-
- var display = visible ? 'block' : 'none';
- for (var y = 0, field; field = this.fieldRow[y]; y++) {
- field.setVisible(visible);
- }
- if (this.connection) {
- // Has a connection.
- if (visible) {
- renderList = this.connection.unhideAll();
- } else {
- this.connection.hideAll();
- }
- var child = this.connection.targetBlock();
- if (child) {
- child.getSvgRoot().style.display = display;
- if (!visible) {
- child.rendered = false;
- }
- }
- }
- return renderList;
-};
-
-/**
- * Change a connection's compatibility.
- * @param {string|Array.|null} check Compatible value type or
- * list of value types. Null if all types are compatible.
- * @return {!Blockly.Input} The input being modified (to allow chaining).
- */
-Blockly.Input.prototype.setCheck = function(check) {
- if (!this.connection) {
- throw 'This input does not have a connection.';
- }
- this.connection.setCheck(check);
- return this;
-};
-
-/**
- * Change the alignment of the connection's field(s).
- * @param {number} align One of Blockly.ALIGN_LEFT, ALIGN_CENTRE, ALIGN_RIGHT.
- * In RTL mode directions are reversed, and ALIGN_RIGHT aligns to the left.
- * @return {!Blockly.Input} The input being modified (to allow chaining).
- */
-Blockly.Input.prototype.setAlign = function(align) {
- this.align = align;
- if (this.sourceBlock_.rendered) {
- this.sourceBlock_.render();
- }
- return this;
-};
-
-/**
- * Initialize the fields on this input.
- */
-Blockly.Input.prototype.init = function() {
- if (!this.sourceBlock_.workspace.rendered) {
- return; // Headless blocks don't need fields initialized.
- }
- for (var i = 0; i < this.fieldRow.length; i++) {
- this.fieldRow[i].init();
- }
-};
-
-/**
- * Sever all links to this input.
- */
-Blockly.Input.prototype.dispose = function() {
- for (var i = 0, field; field = this.fieldRow[i]; i++) {
- field.dispose();
- }
- if (this.connection) {
- this.connection.dispose();
- }
- this.sourceBlock_ = null;
-};
diff --git a/core/mutator.js.orig b/core/mutator.js.orig
deleted file mode 100644
index 19a0fe86b..000000000
--- a/core/mutator.js.orig
+++ /dev/null
@@ -1,389 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Object representing a mutator dialog. A mutator allows the
- * user to change the shape of a block using a nested blocks editor.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Mutator');
-
-goog.require('Blockly.Bubble');
-goog.require('Blockly.Icon');
-goog.require('Blockly.WorkspaceSvg');
-goog.require('goog.Timer');
-goog.require('goog.dom');
-
-
-/**
- * Class for a mutator dialog.
- * @param {!Array.} quarkNames List of names of sub-blocks for flyout.
- * @extends {Blockly.Icon}
- * @constructor
- */
-Blockly.Mutator = function(quarkNames) {
- Blockly.Mutator.superClass_.constructor.call(this, null);
- this.quarkNames_ = quarkNames;
-};
-goog.inherits(Blockly.Mutator, Blockly.Icon);
-
-/**
- * Width of workspace.
- * @private
- */
-Blockly.Mutator.prototype.workspaceWidth_ = 0;
-
-/**
- * Height of workspace.
- * @private
- */
-Blockly.Mutator.prototype.workspaceHeight_ = 0;
-
-/**
- * Draw the mutator icon.
- * @param {!Element} group The icon group.
- * @private
- */
-Blockly.Mutator.prototype.drawIcon_ = function(group) {
- // Square with rounded corners.
- Blockly.createSvgElement('rect',
- {'class': 'blocklyIconShape',
- 'rx': '4', 'ry': '4',
- 'height': '16', 'width': '16'},
- group);
- // Gear teeth.
- Blockly.createSvgElement('path',
- {'class': 'blocklyIconSymbol',
- 'd': 'm4.203,7.296 0,1.368 -0.92,0.677 -0.11,0.41 0.9,1.559 0.41,0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 1.8,0 0.3,-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 0.9,-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,-0.41 -0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 -0.127,-1.138 -0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,0.682 -1.043,-0.457 -0.41,0.11 -0.899,1.559 0.108,0.409z'},
- group);
- // Axle hole.
- Blockly.createSvgElement('circle',
- {'class': 'blocklyIconShape', 'r': '2.7', 'cx': '8', 'cy': '8'},
- group);
-};
-
-/**
- * Clicking on the icon toggles if the mutator bubble is visible.
- * Disable if block is uneditable.
- * @param {!Event} e Mouse click event.
- * @private
- * @override
- */
-Blockly.Mutator.prototype.iconClick_ = function(e) {
- if (this.block_.isEditable()) {
- Blockly.Icon.prototype.iconClick_.call(this, e);
- }
-};
-
-/**
- * Create the editor for the mutator's bubble.
- * @return {!Element} The top-level node of the editor.
- * @private
- */
-Blockly.Mutator.prototype.createEditor_ = function() {
- /* Create the editor. Here's the markup that will be generated:
-
- */
- this.svgDialog_ = Blockly.createSvgElement('svg',
- {'x': Blockly.Bubble.BORDER_WIDTH, 'y': Blockly.Bubble.BORDER_WIDTH},
- null);
- // Convert the list of names into a list of XML objects for the flyout.
- if (this.quarkNames_.length) {
- var quarkXml = goog.dom.createDom('xml');
- for (var i = 0, quarkName; quarkName = this.quarkNames_[i]; i++) {
- quarkXml.appendChild(goog.dom.createDom('block', {'type': quarkName}));
- }
- } else {
- var quarkXml = null;
- }
- var workspaceOptions = {
- languageTree: quarkXml,
- parentWorkspace: this.block_.workspace,
- pathToMedia: this.block_.workspace.options.pathToMedia,
- RTL: this.block_.RTL,
- toolboxPosition: this.block_.RTL ? Blockly.TOOLBOX_AT_RIGHT :
- Blockly.TOOLBOX_AT_LEFT,
- horizontalLayout: false,
- getMetrics: this.getFlyoutMetrics_.bind(this),
- setMetrics: null
- };
- this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions);
- this.workspace_.isMutator = true;
- this.svgDialog_.appendChild(
- this.workspace_.createDom('blocklyMutatorBackground'));
- return this.svgDialog_;
-};
-
-/**
- * Add or remove the UI indicating if this icon may be clicked or not.
- */
-Blockly.Mutator.prototype.updateEditable = function() {
- if (!this.block_.isInFlyout) {
- if (this.block_.isEditable()) {
- if (this.iconGroup_) {
- Blockly.removeClass_(/** @type {!Element} */ (this.iconGroup_),
- 'blocklyIconGroupReadonly');
- }
- } else {
- // Close any mutator bubble. Icon is not clickable.
- this.setVisible(false);
- if (this.iconGroup_) {
- Blockly.addClass_(/** @type {!Element} */ (this.iconGroup_),
- 'blocklyIconGroupReadonly');
- }
- }
- }
- // Default behaviour for an icon.
- Blockly.Icon.prototype.updateEditable.call(this);
-};
-
-/**
- * Callback function triggered when the bubble has resized.
- * Resize the workspace accordingly.
- * @private
- */
-Blockly.Mutator.prototype.resizeBubble_ = function() {
- var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH;
- var workspaceSize = this.workspace_.getCanvas().getBBox();
- var width;
- if (this.block_.RTL) {
- width = -workspaceSize.x;
- } else {
- width = workspaceSize.width + workspaceSize.x;
- }
- var height = workspaceSize.height + doubleBorderWidth * 3;
- if (this.workspace_.flyout_) {
- var flyoutMetrics = this.workspace_.flyout_.getMetrics_();
- height = Math.max(height, flyoutMetrics.contentHeight + 20);
- }
- width += doubleBorderWidth * 3;
- // Only resize if the size difference is significant. Eliminates shuddering.
- if (Math.abs(this.workspaceWidth_ - width) > doubleBorderWidth ||
- Math.abs(this.workspaceHeight_ - height) > doubleBorderWidth) {
- // Record some layout information for getFlyoutMetrics_.
- this.workspaceWidth_ = width;
- this.workspaceHeight_ = height;
- // Resize the bubble.
- this.bubble_.setBubbleSize(width + doubleBorderWidth,
- height + doubleBorderWidth);
- this.svgDialog_.setAttribute('width', this.workspaceWidth_);
- this.svgDialog_.setAttribute('height', this.workspaceHeight_);
- }
-
- if (this.block_.RTL) {
- // Scroll the workspace to always left-align.
- var translation = 'translate(' + this.workspaceWidth_ + ',0)';
- this.workspace_.getCanvas().setAttribute('transform', translation);
- }
- this.workspace_.resize();
-};
-
-/**
- * Show or hide the mutator bubble.
- * @param {boolean} visible True if the bubble should be visible.
- */
-Blockly.Mutator.prototype.setVisible = function(visible) {
- if (visible == this.isVisible()) {
- // No change.
- return;
- }
- Blockly.Events.fire(
- new Blockly.Events.Ui(this.block_, 'mutatorOpen', !visible, visible));
- if (visible) {
- // Create the bubble.
- this.bubble_ = new Blockly.Bubble(
- /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace),
- this.createEditor_(), this.block_.svgPath_, this.iconXY_, null, null);
- var tree = this.workspace_.options.languageTree;
- if (tree) {
- this.workspace_.flyout_.init(this.workspace_);
- this.workspace_.flyout_.show(tree.childNodes);
- }
-
- this.rootBlock_ = this.block_.decompose(this.workspace_);
- var blocks = this.rootBlock_.getDescendants();
- for (var i = 0, child; child = blocks[i]; i++) {
- child.render();
- }
- // The root block should not be dragable or deletable.
- this.rootBlock_.setMovable(false);
- this.rootBlock_.setDeletable(false);
- if (this.workspace_.flyout_) {
- var margin = this.workspace_.flyout_.CORNER_RADIUS * 2;
- var x = this.workspace_.flyout_.width_ + margin;
- } else {
- var margin = 16;
- var x = margin;
- }
- if (this.block_.RTL) {
- x = -x;
- }
- this.rootBlock_.moveBy(x, margin);
- // Save the initial connections, then listen for further changes.
- if (this.block_.saveConnections) {
- var thisMutator = this;
- this.block_.saveConnections(this.rootBlock_);
- this.sourceListener_ = function() {
- thisMutator.block_.saveConnections(thisMutator.rootBlock_);
- };
- this.block_.workspace.addChangeListener(this.sourceListener_);
- }
- this.resizeBubble_();
- // When the mutator's workspace changes, update the source block.
- this.workspace_.addChangeListener(this.workspaceChanged_.bind(this));
- this.updateColour();
- } else {
- // Dispose of the bubble.
- this.svgDialog_ = null;
- this.workspace_.dispose();
- this.workspace_ = null;
- this.rootBlock_ = null;
- this.bubble_.dispose();
- this.bubble_ = null;
- this.workspaceWidth_ = 0;
- this.workspaceHeight_ = 0;
- if (this.sourceListener_) {
- this.block_.workspace.removeChangeListener(this.sourceListener_);
- this.sourceListener_ = null;
- }
- }
-};
-
-/**
- * Update the source block when the mutator's blocks are changed.
- * Bump down any block that's too high.
- * Fired whenever a change is made to the mutator's workspace.
- * @private
- */
-Blockly.Mutator.prototype.workspaceChanged_ = function() {
- if (Blockly.dragMode_ == Blockly.DRAG_NONE) {
- var blocks = this.workspace_.getTopBlocks(false);
- var MARGIN = 20;
- for (var b = 0, block; block = blocks[b]; b++) {
- var blockXY = block.getRelativeToSurfaceXY();
- var blockHW = block.getHeightWidth();
- if (blockXY.y + blockHW.height < MARGIN) {
- // Bump any block that's above the top back inside.
- block.moveBy(0, MARGIN - blockHW.height - blockXY.y);
- }
- }
- }
-
- // When the mutator's workspace changes, update the source block.
- if (this.rootBlock_.workspace == this.workspace_) {
- Blockly.Events.setGroup(true);
- var block = this.block_;
- var oldMutationDom = block.mutationToDom();
- var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
- // Switch off rendering while the source block is rebuilt.
- var savedRendered = block.rendered;
- block.rendered = false;
- // Allow the source block to rebuild itself.
- block.compose(this.rootBlock_);
- // Restore rendering and show the changes.
- block.rendered = savedRendered;
- // Mutation may have added some elements that need initalizing.
- block.initSvg();
- var newMutationDom = block.mutationToDom();
- var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom);
- if (oldMutation != newMutation) {
- Blockly.Events.fire(new Blockly.Events.Change(
- block, 'mutation', null, oldMutation, newMutation));
- // Ensure that any bump is part of this mutation's event group.
- var group = Blockly.Events.getGroup();
- setTimeout(function() {
- Blockly.Events.setGroup(group);
- block.bumpNeighbours_();
- Blockly.Events.setGroup(false);
- }, Blockly.BUMP_DELAY);
- }
- if (block.rendered) {
- block.render();
- }
- this.resizeBubble_();
- Blockly.Events.setGroup(false);
- }
-};
-
-/**
- * Return an object with all the metrics required to size scrollbars for the
- * mutator flyout. The following properties are computed:
- * .viewHeight: Height of the visible rectangle,
- * .viewWidth: Width of the visible rectangle,
- * .absoluteTop: Top-edge of view.
- * .absoluteLeft: Left-edge of view.
- * @return {!Object} Contains size and position metrics of mutator dialog's
- * workspace.
- * @private
- */
-Blockly.Mutator.prototype.getFlyoutMetrics_ = function() {
- return {
- viewHeight: this.workspaceHeight_,
- viewWidth: this.workspaceWidth_,
- absoluteTop: 0,
- absoluteLeft: 0
- };
-};
-
-/**
- * Dispose of this mutator.
- */
-Blockly.Mutator.prototype.dispose = function() {
- this.block_.mutator = null;
- Blockly.Icon.prototype.dispose.call(this);
-};
-
-/**
- * Reconnect an block to a mutated input.
- * @param {Blockly.Connection} connectionChild Connection on child block.
- * @param {!Blockly.Block} block Parent block.
- * @param {string} inputName Name of input on parent block.
- * @return {boolean} True iff a reconnection was made, false otherwise.
- */
-Blockly.Mutator.reconnect = function(connectionChild, block, inputName) {
- if (!connectionChild || !connectionChild.getSourceBlock().workspace) {
- return false; // No connection or block has been deleted.
- }
- var connectionParent = block.getInput(inputName).connection;
- var currentParent = connectionChild.targetBlock();
- if ((!currentParent || currentParent == block) &&
- connectionParent.targetConnection != connectionChild) {
- if (connectionParent.isConnected()) {
- // There's already something connected here. Get rid of it.
- connectionParent.disconnect();
- }
- connectionParent.connect(connectionChild);
- return true;
- }
- return false;
-};
-
-// Export symbols that would otherwise be renamed by Closure compiler.
-if (!goog.global['Blockly']) {
- goog.global['Blockly'] = {};
-}
-if (!goog.global['Blockly']['Mutator']) {
- goog.global['Blockly']['Mutator'] = {};
-}
-goog.global['Blockly']['Mutator']['reconnect'] = Blockly.Mutator.reconnect;
diff --git a/core/procedures.js.orig b/core/procedures.js.orig
deleted file mode 100644
index beb4a17b8..000000000
--- a/core/procedures.js.orig
+++ /dev/null
@@ -1,287 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Utility functions for handling procedures.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Procedures');
-
-goog.require('Blockly.Blocks');
-goog.require('Blockly.Field');
-goog.require('Blockly.Names');
-goog.require('Blockly.Workspace');
-
-
-/**
- * Category to separate procedure names from variables and generated functions.
- */
-Blockly.Procedures.NAME_TYPE = 'PROCEDURE';
-
-/**
- * Find all user-created procedure definitions in a workspace.
- * @param {!Blockly.Workspace} root Root workspace.
- * @return {!Array.>} Pair of arrays, the
- * first contains procedures without return variables, the second with.
- * Each procedure is defined by a three-element list of name, parameter
- * list, and return value boolean.
- */
-Blockly.Procedures.allProcedures = function(root) {
- var blocks = root.getAllBlocks();
- var proceduresReturn = [];
- var proceduresNoReturn = [];
- for (var i = 0; i < blocks.length; i++) {
- if (blocks[i].getProcedureDef) {
- var tuple = blocks[i].getProcedureDef();
- if (tuple) {
- if (tuple[2]) {
- proceduresReturn.push(tuple);
- } else {
- proceduresNoReturn.push(tuple);
- }
- }
- }
- }
- proceduresNoReturn.sort(Blockly.Procedures.procTupleComparator_);
- proceduresReturn.sort(Blockly.Procedures.procTupleComparator_);
- return [proceduresNoReturn, proceduresReturn];
-};
-
-/**
- * Comparison function for case-insensitive sorting of the first element of
- * a tuple.
- * @param {!Array} ta First tuple.
- * @param {!Array} tb Second tuple.
- * @return {number} -1, 0, or 1 to signify greater than, equality, or less than.
- * @private
- */
-Blockly.Procedures.procTupleComparator_ = function(ta, tb) {
- return ta[0].toLowerCase().localeCompare(tb[0].toLowerCase());
-};
-
-/**
- * Ensure two identically-named procedures don't exist.
- * @param {string} name Proposed procedure name.
- * @param {!Blockly.Block} block Block to disambiguate.
- * @return {string} Non-colliding name.
- */
-Blockly.Procedures.findLegalName = function(name, block) {
- if (block.isInFlyout) {
- // Flyouts can have multiple procedures called 'do something'.
- return name;
- }
- while (!Blockly.Procedures.isLegalName_(name, block.workspace, block)) {
- // Collision with another procedure.
- var r = name.match(/^(.*?)(\d+)$/);
- if (!r) {
- name += '2';
- } else {
- name = r[1] + (parseInt(r[2], 10) + 1);
- }
- }
- return name;
-};
-
-/**
- * Does this procedure have a legal name? Illegal names include names of
- * procedures already defined.
- * @param {string} name The questionable name.
- * @param {!Blockly.Workspace} workspace The workspace to scan for collisions.
- * @param {Blockly.Block=} opt_exclude Optional block to exclude from
- * comparisons (one doesn't want to collide with oneself).
- * @return {boolean} True if the name is legal.
- * @private
- */
-Blockly.Procedures.isLegalName_ = function(name, workspace, opt_exclude) {
- var blocks = workspace.getAllBlocks();
- // Iterate through every block and check the name.
- for (var i = 0; i < blocks.length; i++) {
- if (blocks[i] == opt_exclude) {
- continue;
- }
- if (blocks[i].getProcedureDef) {
- var procName = blocks[i].getProcedureDef();
- if (Blockly.Names.equals(procName[0], name)) {
- return false;
- }
- }
- }
- return true;
-};
-
-/**
- * Rename a procedure. Called by the editable field.
- * @param {string} name The proposed new name.
- * @return {string} The accepted name.
- * @this {!Blockly.Field}
- */
-Blockly.Procedures.rename = function(name) {
- // Strip leading and trailing whitespace. Beyond this, all names are legal.
- name = name.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
-
- // Ensure two identically-named procedures don't exist.
- var legalName = Blockly.Procedures.findLegalName(name, this.sourceBlock_);
- var oldName = this.text_;
- if (oldName != name && oldName != legalName) {
- // Rename any callers.
- var blocks = this.sourceBlock_.workspace.getAllBlocks();
- for (var i = 0; i < blocks.length; i++) {
- if (blocks[i].renameProcedure) {
- blocks[i].renameProcedure(oldName, legalName);
- }
- }
- }
- return legalName;
-};
-
-/**
- * Construct the blocks required by the flyout for the procedure category.
- * @param {!Blockly.Workspace} workspace The workspace contianing procedures.
- * @return {!Array.} Array of XML block elements.
- */
-Blockly.Procedures.flyoutCategory = function(workspace) {
- var xmlList = [];
- if (Blockly.Blocks['procedures_defnoreturn']) {
- //
- var block = goog.dom.createDom('block');
- block.setAttribute('type', 'procedures_defnoreturn');
- block.setAttribute('gap', 16);
- xmlList.push(block);
- }
- if (Blockly.Blocks['procedures_defreturn']) {
- //
- var block = goog.dom.createDom('block');
- block.setAttribute('type', 'procedures_defreturn');
- block.setAttribute('gap', 16);
- xmlList.push(block);
- }
- if (Blockly.Blocks['procedures_ifreturn']) {
- //
- var block = goog.dom.createDom('block');
- block.setAttribute('type', 'procedures_ifreturn');
- block.setAttribute('gap', 16);
- xmlList.push(block);
- }
- if (xmlList.length) {
- // Add slightly larger gap between system blocks and user calls.
- xmlList[xmlList.length - 1].setAttribute('gap', 24);
- }
-
- function populateProcedures(procedureList, templateName) {
- for (var i = 0; i < procedureList.length; i++) {
- var name = procedureList[i][0];
- var args = procedureList[i][1];
- //
- //
- //
- //
- //
- var block = goog.dom.createDom('block');
- block.setAttribute('type', templateName);
- block.setAttribute('gap', 16);
- var mutation = goog.dom.createDom('mutation');
- mutation.setAttribute('name', name);
- block.appendChild(mutation);
- for (var j = 0; j < args.length; j++) {
- var arg = goog.dom.createDom('arg');
- arg.setAttribute('name', args[j]);
- mutation.appendChild(arg);
- }
- xmlList.push(block);
- }
- }
-
- var tuple = Blockly.Procedures.allProcedures(workspace);
- populateProcedures(tuple[0], 'procedures_callnoreturn');
- populateProcedures(tuple[1], 'procedures_callreturn');
- return xmlList;
-};
-
-/**
- * Find all the callers of a named procedure.
- * @param {string} name Name of procedure.
- * @param {!Blockly.Workspace} workspace The workspace to find callers in.
- * @return {!Array.} Array of caller blocks.
- */
-Blockly.Procedures.getCallers = function(name, workspace) {
- var callers = [];
- var blocks = workspace.getAllBlocks();
- // Iterate through every block and check the name.
- for (var i = 0; i < blocks.length; i++) {
- if (blocks[i].getProcedureCall) {
- var procName = blocks[i].getProcedureCall();
- // Procedure name may be null if the block is only half-built.
- if (procName && Blockly.Names.equals(procName, name)) {
- callers.push(blocks[i]);
- }
- }
- }
- return callers;
-};
-
-/**
- * When a procedure definition changes its parameters, find and edit all its
- * callers.
- * @param {!Blockly.Block} defBlock Procedure definition block.
- */
-Blockly.Procedures.mutateCallers = function(defBlock) {
- var oldRecordUndo = Blockly.Events.recordUndo;
- var name = defBlock.getProcedureDef()[0];
- var xmlElement = defBlock.mutationToDom(true);
- var callers = Blockly.Procedures.getCallers(name, defBlock.workspace);
- for (var i = 0, caller; caller = callers[i]; i++) {
- var oldMutationDom = caller.mutationToDom();
- var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
- caller.domToMutation(xmlElement);
- var newMutationDom = caller.mutationToDom();
- var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom);
- if (oldMutation != newMutation) {
- // Fire a mutation on every caller block. But don't record this as an
- // undo action since it is deterministically tied to the procedure's
- // definition mutation.
- Blockly.Events.recordUndo = false;
- Blockly.Events.fire(new Blockly.Events.Change(
- caller, 'mutation', null, oldMutation, newMutation));
- Blockly.Events.recordUndo = oldRecordUndo;
- }
- }
-};
-
-/**
- * Find the definition block for the named procedure.
- * @param {string} name Name of procedure.
- * @param {!Blockly.Workspace} workspace The workspace to search.
- * @return {Blockly.Block} The procedure definition block, or null not found.
- */
-Blockly.Procedures.getDefinition = function(name, workspace) {
- // Assume that a procedure definition is a top block.
- var blocks = workspace.getTopBlocks(false);
- for (var i = 0; i < blocks.length; i++) {
- if (blocks[i].getProcedureDef) {
- var tuple = blocks[i].getProcedureDef();
- if (tuple && Blockly.Names.equals(tuple[0], name)) {
- return blocks[i];
- }
- }
- }
- return null;
-};
diff --git a/core/scrollbar.js.orig b/core/scrollbar.js.orig
deleted file mode 100644
index da2dd942d..000000000
--- a/core/scrollbar.js.orig
+++ /dev/null
@@ -1,750 +0,0 @@
-/**
- * @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 Library for creating scrollbars.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Scrollbar');
-goog.provide('Blockly.ScrollbarPair');
-
-goog.require('goog.dom');
-goog.require('goog.events');
-
-
-/**
- * Class for a pair of scrollbars. Horizontal and vertical.
- * @param {!Blockly.Workspace} workspace Workspace to bind the scrollbars to.
- * @constructor
- */
-Blockly.ScrollbarPair = function(workspace) {
- this.workspace_ = workspace;
- this.hScroll = new Blockly.Scrollbar(workspace, true, true);
- this.vScroll = new Blockly.Scrollbar(workspace, false, true);
- this.corner_ = Blockly.createSvgElement('rect',
- {'height': Blockly.Scrollbar.scrollbarThickness,
- 'width': Blockly.Scrollbar.scrollbarThickness,
- 'class': 'blocklyScrollbarBackground'}, null);
- Blockly.Scrollbar.insertAfter_(this.corner_, workspace.getBubbleCanvas());
-};
-
-/**
- * Previously recorded metrics from the workspace.
- * @type {Object}
- * @private
- */
-Blockly.ScrollbarPair.prototype.oldHostMetrics_ = null;
-
-/**
- * Dispose of this pair of scrollbars.
- * Unlink from all DOM elements to prevent memory leaks.
- */
-Blockly.ScrollbarPair.prototype.dispose = function() {
- goog.dom.removeNode(this.corner_);
- this.corner_ = null;
- this.workspace_ = null;
- this.oldHostMetrics_ = null;
- this.hScroll.dispose();
- this.hScroll = null;
- this.vScroll.dispose();
- this.vScroll = null;
-};
-
-/**
- * Recalculate both of the scrollbars' locations and lengths.
- * Also reposition the corner rectangle.
- */
-Blockly.ScrollbarPair.prototype.resize = function() {
- // Look up the host metrics once, and use for both scrollbars.
- var hostMetrics = this.workspace_.getMetrics();
- if (!hostMetrics) {
- // Host element is likely not visible.
- return;
- }
-
- // Only change the scrollbars if there has been a change in metrics.
- var resizeH = false;
- var resizeV = false;
- if (!this.oldHostMetrics_ ||
- this.oldHostMetrics_.viewWidth != hostMetrics.viewWidth ||
- this.oldHostMetrics_.viewHeight != hostMetrics.viewHeight ||
- this.oldHostMetrics_.absoluteTop != hostMetrics.absoluteTop ||
- this.oldHostMetrics_.absoluteLeft != hostMetrics.absoluteLeft) {
- // The window has been resized or repositioned.
- resizeH = true;
- resizeV = true;
- } else {
- // Has the content been resized or moved?
- if (!this.oldHostMetrics_ ||
- this.oldHostMetrics_.contentWidth != hostMetrics.contentWidth ||
- this.oldHostMetrics_.viewLeft != hostMetrics.viewLeft ||
- this.oldHostMetrics_.contentLeft != hostMetrics.contentLeft) {
- resizeH = true;
- }
- if (!this.oldHostMetrics_ ||
- this.oldHostMetrics_.contentHeight != hostMetrics.contentHeight ||
- this.oldHostMetrics_.viewTop != hostMetrics.viewTop ||
- this.oldHostMetrics_.contentTop != hostMetrics.contentTop) {
- resizeV = true;
- }
- }
- if (resizeH) {
- this.hScroll.resize(hostMetrics);
- }
- if (resizeV) {
- this.vScroll.resize(hostMetrics);
- }
-
- // Reposition the corner square.
- if (!this.oldHostMetrics_ ||
- this.oldHostMetrics_.viewWidth != hostMetrics.viewWidth ||
- this.oldHostMetrics_.absoluteLeft != hostMetrics.absoluteLeft) {
- this.corner_.setAttribute('x', this.vScroll.position_.x);
- }
- if (!this.oldHostMetrics_ ||
- this.oldHostMetrics_.viewHeight != hostMetrics.viewHeight ||
- this.oldHostMetrics_.absoluteTop != hostMetrics.absoluteTop) {
- this.corner_.setAttribute('y', this.hScroll.position_.y);
- }
-
- // Cache the current metrics to potentially short-cut the next resize event.
- this.oldHostMetrics_ = hostMetrics;
-};
-
-/**
- * Set the sliders of both scrollbars to be at a certain position.
- * @param {number} x Horizontal scroll value.
- * @param {number} y Vertical scroll value.
- */
-Blockly.ScrollbarPair.prototype.set = function(x, y) {
- // This function is equivalent to:
- // this.hScroll.set(x);
- // this.vScroll.set(y);
- // However, that calls setMetrics twice which causes a chain of
- // getAttribute->setAttribute->getAttribute resulting in an extra layout pass.
- // Combining them speeds up rendering.
- var xyRatio = {};
-
- var hHandlePosition = x * this.hScroll.ratio_;
- var vHandlePosition = y * this.vScroll.ratio_;
-
- var hBarLength = this.hScroll.scrollViewSize_;
- var vBarLength = this.vScroll.scrollViewSize_;
-
- xyRatio.x = this.getRatio_(hHandlePosition, hBarLength);
- xyRatio.y = this.getRatio_(vHandlePosition, vBarLength);
- this.workspace_.setMetrics(xyRatio);
-
- this.hScroll.setHandlePosition(hHandlePosition);
- this.vScroll.setHandlePosition(vHandlePosition);
-};
-
-/**
- * Helper to calculate the ratio of handle position to scrollbar view size.
- * @param {number} handlePosition The value of the handle.
- * @param {number} viewSize The total size of the scrollbar's view.
- * @return {number} Ratio.
- * @private
- */
-Blockly.ScrollbarPair.prototype.getRatio_ = function(handlePosition, viewSize) {
- var ratio = handlePosition / viewSize;
- if (isNaN(ratio)) {
- return 0;
- }
- return ratio;
-};
-
-// --------------------------------------------------------------------
-
-/**
- * Class for a pure SVG scrollbar.
- * This technique offers a scrollbar that is guaranteed to work, but may not
- * look or behave like the system's scrollbars.
- * @param {!Blockly.Workspace} workspace Workspace to bind the scrollbar to.
- * @param {boolean} horizontal True if horizontal, false if vertical.
- * @param {boolean=} opt_pair True if scrollbar is part of a horiz/vert pair.
- * @constructor
- */
-Blockly.Scrollbar = function(workspace, horizontal, opt_pair) {
- this.workspace_ = workspace;
- this.pair_ = opt_pair || false;
- this.horizontal_ = horizontal;
- this.oldHostMetrics_ = null;
-
- this.createDom_();
-
- /**
- * The upper left corner of the scrollbar's svg group.
- * @type {goog.math.Coordinate}
- * @private
- */
- this.position_ = new goog.math.Coordinate(0, 0);
-
- if (horizontal) {
- this.svgBackground_.setAttribute('height',
- Blockly.Scrollbar.scrollbarThickness);
- this.svgHandle_.setAttribute('height',
- Blockly.Scrollbar.scrollbarThickness - 5);
- this.svgHandle_.setAttribute('y', 2.5);
-
- this.lengthAttribute_ = 'width';
- this.positionAttribute_ = 'x';
- } else {
- this.svgBackground_.setAttribute('width',
- Blockly.Scrollbar.scrollbarThickness);
- this.svgHandle_.setAttribute('width',
- Blockly.Scrollbar.scrollbarThickness - 5);
- this.svgHandle_.setAttribute('x', 2.5);
-
- this.lengthAttribute_ = 'height';
- this.positionAttribute_ = 'y';
- }
- var scrollbar = this;
- this.onMouseDownBarWrapper_ = Blockly.bindEvent_(this.svgBackground_,
- 'mousedown', scrollbar, scrollbar.onMouseDownBar_);
- this.onMouseDownHandleWrapper_ = Blockly.bindEvent_(this.svgHandle_,
- 'mousedown', scrollbar, scrollbar.onMouseDownHandle_);
-};
-
-/**
- * The size of the area within which the scrollbar handle can move.
- * @type {number}
- * @private
- */
-Blockly.Scrollbar.prototype.scrollViewSize_ = 0;
-
-/**
- * The length of the scrollbar handle.
- * @type {number}
- * @private
- */
-Blockly.Scrollbar.prototype.handleLength_ = 0;
-
-/**
- * The offset of the start of the handle from the start of the scrollbar range.
- * @type {number}
- * @private
- */
-Blockly.Scrollbar.prototype.handlePosition_ = 0;
-
-/**
- * Whether the scrollbar handle is visible.
- * @type {boolean}
- * @private
- */
-Blockly.Scrollbar.prototype.isVisible_ = true;
-
-/**
- * Width of vertical scrollbar or height of horizontal scrollbar.
- * Increase the size of scrollbars on touch devices.
- * Don't define if there is no document object (e.g. node.js).
- */
-Blockly.Scrollbar.scrollbarThickness = 15;
-if (goog.events.BrowserFeature.TOUCH_ENABLED) {
- Blockly.Scrollbar.scrollbarThickness = 25;
-}
-
-/**
- * @param {!Object} first An object containing computed measurements of a
- * workspace.
- * @param {!Object} second Another object containing computed measurements of a
- * workspace.
- * @return {boolean} Whether the two sets of metrics are equivalent.
- * @private
- */
-Blockly.Scrollbar.metricsAreEquivalent_ = function(first, second) {
- if (!(first && second)) {
- return false;
- }
-
- if (first.viewWidth != second.viewWidth ||
- first.viewHeight != second.viewHeight ||
- first.viewLeft != second.viewLeft ||
- first.viewTop != second.viewTop ||
- first.absoluteTop != second.absoluteTop ||
- first.absoluteLeft != second.absoluteLeft ||
- first.contentWidth != second.contentWidth ||
- first.contentHeight != second.contentHeight ||
- first.contentLeft != second.contentLeft ||
- first.contentTop != second.contentTop) {
- return false;
- }
-
- return true;
-};
-
-/**
- * Dispose of this scrollbar.
- * Unlink from all DOM elements to prevent memory leaks.
- */
-Blockly.Scrollbar.prototype.dispose = function() {
- this.onMouseUpHandle_();
- Blockly.unbindEvent_(this.onMouseDownBarWrapper_);
- this.onMouseDownBarWrapper_ = null;
- Blockly.unbindEvent_(this.onMouseDownHandleWrapper_);
- this.onMouseDownHandleWrapper_ = null;
-
- goog.dom.removeNode(this.svgGroup_);
- this.svgGroup_ = null;
- this.svgBackground_ = null;
- this.svgHandle_ = null;
- this.workspace_ = null;
-};
-
-/**
- * Set the length of the scrollbar's handle and change the SVG attribute
- * accordingly.
- * @param {number} newLength The new scrollbar handle length.
- */
-Blockly.Scrollbar.prototype.setHandleLength_ = function(newLength) {
- this.handleLength_ = newLength;
- this.svgHandle_.setAttribute(this.lengthAttribute_, this.handleLength_);
-};
-
-/**
- * Set the offset of the scrollbar's handle and change the SVG attribute
- * accordingly.
- * @param {number} newPosition The new scrollbar handle offset.
- */
-Blockly.Scrollbar.prototype.setHandlePosition = function(newPosition) {
- this.handlePosition_ = newPosition;
- this.svgHandle_.setAttribute(this.positionAttribute_, this.handlePosition_);
-};
-
-/**
- * Set the size of the scrollbar's background and change the SVG attribute
- * accordingly.
- * @param {number} newSize The new scrollbar background length.
- * @private
- */
-Blockly.Scrollbar.prototype.setScrollViewSize_ = function(newSize) {
- this.scrollViewSize_ = newSize;
- this.svgBackground_.setAttribute(this.lengthAttribute_, this.scrollViewSize_);
-};
-
-/**
- * Set the position of the scrollbar's svg group.
- * @param {number} x The new x coordinate.
- * @param {number} y The new y coordinate.
- */
-Blockly.Scrollbar.prototype.setPosition = function(x, y) {
- this.position_.x = x;
- this.position_.y = y;
-
- this.svgGroup_.setAttribute('transform',
- 'translate(' + this.position_.x + ',' + this.position_.y + ')');
-};
-
-/**
- * Recalculate the scrollbar's location and its length.
- * @param {Object=} opt_metrics A data structure of from the describing all the
- * required dimensions. If not provided, it will be fetched from the host
- * object.
- */
-Blockly.Scrollbar.prototype.resize = function(opt_metrics) {
- // Determine the location, height and width of the host element.
- var hostMetrics = opt_metrics;
- if (!hostMetrics) {
- hostMetrics = this.workspace_.getMetrics();
- if (!hostMetrics) {
- // Host element is likely not visible.
- return;
- }
- }
-
- if (Blockly.Scrollbar.metricsAreEquivalent_(hostMetrics,
- this.oldHostMetrics_)) {
- return;
- }
- this.oldHostMetrics_ = hostMetrics;
-
- /* hostMetrics is an object with the following properties.
- * .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.
- */
- if (this.horizontal_) {
- this.resizeHorizontal_(hostMetrics);
- } else {
- this.resizeVertical_(hostMetrics);
- }
- // Resizing may have caused some scrolling.
- this.onScroll_();
-};
-
-/**
- * Recalculate a horizontal scrollbar's location and length.
- * @param {!Object} hostMetrics A data structure describing all the
- * required dimensions, possibly fetched from the host object.
- * @private
- */
-Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) {
- // TODO: Inspect metrics to determine if we can get away with just a content
- // resize.
- this.resizeViewHorizontal(hostMetrics);
-};
-
-/**
- * Recalculate a horizontal scrollbar's location on the screen and path length.
- * This should be called when the layout or size of the window has changed.
- * @param {!Object} hostMetrics A data structure describing all the
- * required dimensions, possibly fetched from the host object.
- */
-Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) {
- var viewSize = hostMetrics.viewWidth - 1;
- if (this.pair_) {
- // Shorten the scrollbar to make room for the corner square.
- viewSize -= Blockly.Scrollbar.scrollbarThickness;
- }
- this.setScrollViewSize_(Math.max(0, viewSize));
-
- var xCoordinate = hostMetrics.absoluteLeft + 0.5;
- if (this.pair_ && this.workspace_.RTL) {
- xCoordinate += Blockly.Scrollbar.scrollbarThickness;
- }
-
- // Horizontal toolbar should always be just above the bottom of the workspace.
- var yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight -
- Blockly.Scrollbar.scrollbarThickness - 0.5;
- this.setPosition(xCoordinate, yCoordinate);
-
- // If the view has been resized, a content resize will also be necessary. The
- // reverse is not true.
- this.resizeContentHorizontal(hostMetrics);
-};
-
-/**
- * Recalculate a horizontal scrollbar's location within its path and length.
- * This should be called when the contents of the workspace have changed.
- * @param {!Object} hostMetrics A data structure describing all the
- * required dimensions, possibly fetched from the host object.
- */
-Blockly.Scrollbar.prototype.resizeContentHorizontal = function(hostMetrics) {
- if (!this.pair_) {
- // Only show the scrollbar if needed.
- // Ideally this would also apply to scrollbar pairs, but that's a bigger
- // headache (due to interactions with the corner square).
- this.setVisible(this.scrollViewSize_ < hostMetrics.contentWidth);
- }
-
- this.ratio_ = this.scrollViewSize_ / hostMetrics.contentWidth;
- if (this.ratio_ == -Infinity || this.ratio_ == Infinity ||
- isNaN(this.ratio_)) {
- this.ratio_ = 0;
- }
-
- var handleLength = hostMetrics.viewWidth * this.ratio_;
- this.setHandleLength_(Math.max(0, handleLength));
-
- var handlePosition = (hostMetrics.viewLeft - hostMetrics.contentLeft) *
- this.ratio_;
- this.setHandlePosition(this.constrainHandle_(handlePosition));
-};
-
-/**
- * Recalculate a vertical scrollbar's location and length.
- * @param {!Object} hostMetrics A data structure describing all the
- * required dimensions, possibly fetched from the host object.
- * @private
- */
-Blockly.Scrollbar.prototype.resizeVertical_ = function(hostMetrics) {
- // TODO: Inspect metrics to determine if we can get away with just a content
- // resize.
- this.resizeViewVertical(hostMetrics);
-};
-
-/**
- * Recalculate a vertical scrollbar's location on the screen and path length.
- * This should be called when the layout or size of the window has changed.
- * @param {!Object} hostMetrics A data structure describing all the
- * required dimensions, possibly fetched from the host object.
- */
-Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) {
- var viewSize = hostMetrics.viewHeight - 1;
- if (this.pair_) {
- // Shorten the scrollbar to make room for the corner square.
- viewSize -= Blockly.Scrollbar.scrollbarThickness;
- }
- this.setScrollViewSize_(Math.max(0, viewSize));
-
- var xCoordinate = hostMetrics.absoluteLeft + 0.5;
- if (!this.workspace_.RTL) {
- xCoordinate += hostMetrics.viewWidth -
- Blockly.Scrollbar.scrollbarThickness - 1;
- }
- var yCoordinate = hostMetrics.absoluteTop + 0.5;
- this.setPosition(xCoordinate, yCoordinate);
-
- // If the view has been resized, a content resize will also be necessary. The
- // reverse is not true.
- this.resizeContentVertical(hostMetrics);
-};
-
-/**
- * Recalculate a vertical scrollbar's location within its path and length.
- * This should be called when the contents of the workspace have changed.
- * @param {!Object} hostMetrics A data structure describing all the
- * required dimensions, possibly fetched from the host object.
- */
-Blockly.Scrollbar.prototype.resizeContentVertical = function(hostMetrics) {
- if (!this.pair_) {
- // Only show the scrollbar if needed.
- this.setVisible(this.scrollViewSize_ < hostMetrics.contentHeight);
- }
-
- this.ratio_ = this.scrollViewSize_ / hostMetrics.contentHeight;
- if (this.ratio_ == -Infinity || this.ratio_ == Infinity ||
- isNaN(this.ratio_)) {
- this.ratio_ = 0;
- }
-
- var handleLength = hostMetrics.viewHeight * this.ratio_;
- this.setHandleLength_(Math.max(0, handleLength));
-
- var handlePosition = (hostMetrics.viewTop - hostMetrics.contentTop) *
- this.ratio_;
- this.setHandlePosition(this.constrainHandle_(handlePosition));
-};
-
-/**
- * Create all the DOM elements required for a scrollbar.
- * The resulting widget is not sized.
- * @private
- */
-Blockly.Scrollbar.prototype.createDom_ = function() {
- /* Create the following DOM:
-
-
-
-
- */
- var className = 'blocklyScrollbar' +
- (this.horizontal_ ? 'Horizontal' : 'Vertical');
- this.svgGroup_ = Blockly.createSvgElement('g', {'class': className}, null);
- this.svgBackground_ = Blockly.createSvgElement('rect',
- {'class': 'blocklyScrollbarBackground'}, this.svgGroup_);
- var radius = Math.floor((Blockly.Scrollbar.scrollbarThickness - 5) / 2);
- this.svgHandle_ = Blockly.createSvgElement('rect',
- {'class': 'blocklyScrollbarHandle', 'rx': radius, 'ry': radius},
- this.svgGroup_);
- Blockly.Scrollbar.insertAfter_(this.svgGroup_,
- this.workspace_.getBubbleCanvas());
-};
-
-/**
- * Is the scrollbar visible. Non-paired scrollbars disappear when they aren't
- * needed.
- * @return {boolean} True if visible.
- */
-Blockly.Scrollbar.prototype.isVisible = function() {
- return this.isVisible_;
-};
-
-/**
- * Set whether the scrollbar is visible.
- * Only applies to non-paired scrollbars.
- * @param {boolean} visible True if visible.
- */
-Blockly.Scrollbar.prototype.setVisible = function(visible) {
- if (visible == this.isVisible()) {
- return;
- }
- // Ideally this would also apply to scrollbar pairs, but that's a bigger
- // headache (due to interactions with the corner square).
- if (this.pair_) {
- throw 'Unable to toggle visibility of paired scrollbars.';
- }
-
- this.isVisible_ = visible;
-
- if (visible) {
- this.svgGroup_.setAttribute('display', 'block');
- } else {
- // Hide the scrollbar.
- this.workspace_.setMetrics({x: 0, y: 0});
- this.svgGroup_.setAttribute('display', 'none');
- }
-};
-
-/**
- * Scroll by one pageful.
- * Called when scrollbar background is clicked.
- * @param {!Event} e Mouse down event.
- * @private
- */
-Blockly.Scrollbar.prototype.onMouseDownBar_ = function(e) {
- this.onMouseUpHandle_();
- if (Blockly.isRightButton(e)) {
- // Right-click.
- // Scrollbars have no context menu.
- e.stopPropagation();
- return;
- }
- var mouseXY = Blockly.mouseToSvg(e, this.workspace_.getParentSvg(),
- this.workspace_.getInverseScreenCTM());
- var mouseLocation = this.horizontal_ ? mouseXY.x : mouseXY.y;
-
- var handleXY = Blockly.getSvgXY_(this.svgHandle_, this.workspace_);
- var handleStart = this.horizontal_ ? handleXY.x : handleXY.y;
- var handlePosition = this.handlePosition_;
-
- var pageLength = this.handleLength_ * 0.95;
- if (mouseLocation <= handleStart) {
- // Decrease the scrollbar's value by a page.
- handlePosition -= pageLength;
- } else if (mouseLocation >= handleStart + this.handleLength_) {
- // Increase the scrollbar's value by a page.
- handlePosition += pageLength;
- }
-
- this.setHandlePosition(this.constrainHandle_(handlePosition));
-
- this.onScroll_();
- e.stopPropagation();
- e.preventDefault();
-};
-
-/**
- * Start a dragging operation.
- * Called when scrollbar handle is clicked.
- * @param {!Event} e Mouse down event.
- * @private
- */
-Blockly.Scrollbar.prototype.onMouseDownHandle_ = function(e) {
- this.onMouseUpHandle_();
- if (Blockly.isRightButton(e)) {
- // Right-click.
- // Scrollbars have no context menu.
- e.stopPropagation();
- return;
- }
- // Look up the current translation and record it.
- this.startDragHandle = this.handlePosition_;
- // Record the current mouse position.
- this.startDragMouse = this.horizontal_ ? e.clientX : e.clientY;
- Blockly.Scrollbar.onMouseUpWrapper_ = Blockly.bindEvent_(document,
- 'mouseup', this, this.onMouseUpHandle_);
- Blockly.Scrollbar.onMouseMoveWrapper_ = Blockly.bindEvent_(document,
- 'mousemove', this, this.onMouseMoveHandle_);
- e.stopPropagation();
- e.preventDefault();
-};
-
-/**
- * Drag the scrollbar's handle.
- * @param {!Event} e Mouse up event.
- * @private
- */
-Blockly.Scrollbar.prototype.onMouseMoveHandle_ = function(e) {
- var currentMouse = this.horizontal_ ? e.clientX : e.clientY;
- var mouseDelta = currentMouse - this.startDragMouse;
- var handlePosition = this.startDragHandle + mouseDelta;
- // Position the bar.
- this.setHandlePosition(this.constrainHandle_(handlePosition));
- this.onScroll_();
-};
-
-/**
- * Stop binding to the global mouseup and mousemove events.
- * @private
- */
-Blockly.Scrollbar.prototype.onMouseUpHandle_ = function() {
- Blockly.hideChaff(true);
- if (Blockly.Scrollbar.onMouseUpWrapper_) {
- Blockly.unbindEvent_(Blockly.Scrollbar.onMouseUpWrapper_);
- Blockly.Scrollbar.onMouseUpWrapper_ = null;
- }
- if (Blockly.Scrollbar.onMouseMoveWrapper_) {
- Blockly.unbindEvent_(Blockly.Scrollbar.onMouseMoveWrapper_);
- Blockly.Scrollbar.onMouseMoveWrapper_ = null;
- }
-};
-
-/**
- * Constrain the handle's position within the minimum (0) and maximum
- * (length of scrollbar) values allowed for the scrollbar.
- * @param {number} value Value that is potentially out of bounds.
- * @return {number} Constrained value.
- * @private
- */
-Blockly.Scrollbar.prototype.constrainHandle_ = function(value) {
- if (value <= 0 || isNaN(value) || this.scrollViewSize_ < this.handleLength_) {
- value = 0;
- } else {
- value = Math.min(value, this.scrollViewSize_ - this.handleLength_);
- }
- return value;
-};
-
-/**
- * Called when scrollbar is moved.
- * @private
- */
-Blockly.Scrollbar.prototype.onScroll_ = function() {
- var ratio = this.handlePosition_ / this.scrollViewSize_;
- if (isNaN(ratio)) {
- ratio = 0;
- }
- var xyRatio = {};
- if (this.horizontal_) {
- xyRatio.x = ratio;
- } else {
- xyRatio.y = ratio;
- }
- this.workspace_.setMetrics(xyRatio);
-};
-
-/**
- * Set the scrollbar slider's position.
- * @param {number} value The distance from the top/left end of the bar.
- */
-Blockly.Scrollbar.prototype.set = function(value) {
- this.setHandlePosition(this.constrainHandle_(value * this.ratio_));
- this.onScroll_();
-};
-
-/**
- * Insert a node after a reference node.
- * Contrast with node.insertBefore function.
- * @param {!Element} newNode New element to insert.
- * @param {!Element} refNode Existing element to precede new node.
- * @private
- */
-Blockly.Scrollbar.insertAfter_ = function(newNode, refNode) {
- var siblingNode = refNode.nextSibling;
- var parentNode = refNode.parentNode;
- if (!parentNode) {
- throw 'Reference node has no parent.';
- }
- if (siblingNode) {
- parentNode.insertBefore(newNode, siblingNode);
- } else {
- parentNode.appendChild(newNode);
- }
-};
diff --git a/core/trashcan.js.orig b/core/trashcan.js.orig
deleted file mode 100644
index 28baa0f20..000000000
--- a/core/trashcan.js.orig
+++ /dev/null
@@ -1,332 +0,0 @@
-/**
- * @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 Object representing a trash can icon.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Trashcan');
-
-goog.require('goog.Timer');
-goog.require('goog.dom');
-goog.require('goog.math');
-goog.require('goog.math.Rect');
-
-
-/**
- * Class for a trash can.
- * @param {!Blockly.Workspace} workspace The workspace to sit in.
- * @constructor
- */
-Blockly.Trashcan = function(workspace) {
- this.workspace_ = workspace;
-};
-
-/**
- * Width of both the trash can and lid images.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.WIDTH_ = 47;
-
-/**
- * Height of the trashcan image (minus lid).
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.BODY_HEIGHT_ = 44;
-
-/**
- * Height of the lid image.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.LID_HEIGHT_ = 16;
-
-/**
- * Distance between trashcan and bottom edge of workspace.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.MARGIN_BOTTOM_ = 20;
-
-/**
- * Distance between trashcan and right edge of workspace.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.MARGIN_SIDE_ = 20;
-
-/**
- * Extent of hotspot on all sides beyond the size of the image.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.MARGIN_HOTSPOT_ = 10;
-
-/**
- * Location of trashcan in sprite image.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.SPRITE_LEFT_ = 0;
-
-/**
- * Location of trashcan in sprite image.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.SPRITE_TOP_ = 32;
-
-/**
- * Current open/close state of the lid.
- * @type {boolean}
- */
-Blockly.Trashcan.prototype.isOpen = false;
-
-/**
- * The SVG group containing the trash can.
- * @type {Element}
- * @private
- */
-Blockly.Trashcan.prototype.svgGroup_ = null;
-
-/**
- * The SVG image element of the trash can lid.
- * @type {Element}
- * @private
- */
-Blockly.Trashcan.prototype.svgLid_ = null;
-
-/**
- * Task ID of opening/closing animation.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.lidTask_ = 0;
-
-/**
- * Current state of lid opening (0.0 = closed, 1.0 = open).
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.lidOpen_ = 0;
-
-/**
- * Left coordinate of the trash can.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.left_ = 0;
-
-/**
- * Top coordinate of the trash can.
- * @type {number}
- * @private
- */
-Blockly.Trashcan.prototype.top_ = 0;
-
-/**
- * Create the trash can elements.
- * @return {!Element} The trash can's SVG group.
- */
-Blockly.Trashcan.prototype.createDom = function() {
- /* Here's the markup that will be generated:
-
-
-
-
-
-
-
-
-
-
- */
- this.svgGroup_ = Blockly.createSvgElement('g',
- {'class': 'blocklyTrash'}, null);
- var rnd = String(Math.random()).substring(2);
- var clip = Blockly.createSvgElement('clipPath',
- {'id': 'blocklyTrashBodyClipPath' + rnd},
- this.svgGroup_);
- Blockly.createSvgElement('rect',
- {'width': this.WIDTH_, 'height': this.BODY_HEIGHT_,
- 'y': this.LID_HEIGHT_},
- clip);
- var body = Blockly.createSvgElement('image',
- {'width': Blockly.SPRITE.width, 'x': -this.SPRITE_LEFT_,
- 'height': Blockly.SPRITE.height, 'y': -this.SPRITE_TOP_,
- 'clip-path': 'url(#blocklyTrashBodyClipPath' + rnd + ')'},
- this.svgGroup_);
- body.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
- this.workspace_.options.pathToMedia + Blockly.SPRITE.url);
-
- var clip = Blockly.createSvgElement('clipPath',
- {'id': 'blocklyTrashLidClipPath' + rnd},
- this.svgGroup_);
- Blockly.createSvgElement('rect',
- {'width': this.WIDTH_, 'height': this.LID_HEIGHT_}, clip);
- this.svgLid_ = Blockly.createSvgElement('image',
- {'width': Blockly.SPRITE.width, 'x': -this.SPRITE_LEFT_,
- 'height': Blockly.SPRITE.height, 'y': -this.SPRITE_TOP_,
- 'clip-path': 'url(#blocklyTrashLidClipPath' + rnd + ')'},
- this.svgGroup_);
- this.svgLid_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
- this.workspace_.options.pathToMedia + Blockly.SPRITE.url);
-
- Blockly.bindEvent_(this.svgGroup_, 'mouseup', this, this.click);
- this.animateLid_();
- return this.svgGroup_;
-};
-
-/**
- * Initialize the trash can.
- * @param {number} bottom Distance from workspace bottom to bottom of trashcan.
- * @return {number} Distance from workspace bottom to the top of trashcan.
- */
-Blockly.Trashcan.prototype.init = function(bottom) {
- this.bottom_ = this.MARGIN_BOTTOM_ + bottom;
- this.setOpen_(false);
- return this.bottom_ + this.BODY_HEIGHT_ + this.LID_HEIGHT_;
-};
-
-/**
- * Dispose of this trash can.
- * Unlink from all DOM elements to prevent memory leaks.
- */
-Blockly.Trashcan.prototype.dispose = function() {
- if (this.svgGroup_) {
- goog.dom.removeNode(this.svgGroup_);
- this.svgGroup_ = null;
- }
- this.svgLid_ = null;
- this.workspace_ = null;
- goog.Timer.clear(this.lidTask_);
-};
-
-/**
- * Move the trash can to the bottom-right corner.
- */
-Blockly.Trashcan.prototype.position = function() {
- var metrics = this.workspace_.getMetrics();
- if (!metrics) {
- // There are no metrics available (workspace is probably not visible).
- return;
- }
- if (this.workspace_.RTL) {
- this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness;
- if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) {
- this.left_ += metrics.flyoutWidth;
- if (this.workspace_.toolbox_) {
- this.left_ += metrics.absoluteLeft;
- }
- }
- } else {
- this.left_ = metrics.viewWidth + metrics.absoluteLeft -
- this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness;
-
- if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) {
- this.left_ -= metrics.flyoutWidth;
- }
- }
- this.top_ = metrics.viewHeight + metrics.absoluteTop -
- (this.BODY_HEIGHT_ + this.LID_HEIGHT_) - this.bottom_;
-
- if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) {
- this.top_ -= metrics.flyoutHeight;
- }
- this.svgGroup_.setAttribute('transform',
- 'translate(' + this.left_ + ',' + this.top_ + ')');
-};
-
-/**
- * Return the deletion rectangle for this trash can.
- * @return {goog.math.Rect} Rectangle in which to delete.
- */
-Blockly.Trashcan.prototype.getClientRect = function() {
- if (!this.svgGroup_) {
- return null;
- }
-
- var trashRect = this.svgGroup_.getBoundingClientRect();
- var left = trashRect.left + this.SPRITE_LEFT_ - this.MARGIN_HOTSPOT_;
- var top = trashRect.top + this.SPRITE_TOP_ - this.MARGIN_HOTSPOT_;
- var width = this.WIDTH_ + 2 * this.MARGIN_HOTSPOT_;
- var height = this.LID_HEIGHT_ + this.BODY_HEIGHT_ + 2 * this.MARGIN_HOTSPOT_;
- return new goog.math.Rect(left, top, width, height);
-
-};
-
-/**
- * Flip the lid open or shut.
- * @param {boolean} state True if open.
- * @private
- */
-Blockly.Trashcan.prototype.setOpen_ = function(state) {
- if (this.isOpen == state) {
- return;
- }
- goog.Timer.clear(this.lidTask_);
- this.isOpen = state;
- this.animateLid_();
-};
-
-/**
- * Rotate the lid open or closed by one step. Then wait and recurse.
- * @private
- */
-Blockly.Trashcan.prototype.animateLid_ = function() {
- this.lidOpen_ += this.isOpen ? 0.2 : -0.2;
- this.lidOpen_ = goog.math.clamp(this.lidOpen_, 0, 1);
- var lidAngle = this.lidOpen_ * 45;
- this.svgLid_.setAttribute('transform', 'rotate(' +
- (this.workspace_.RTL ? -lidAngle : lidAngle) + ',' +
- (this.workspace_.RTL ? 4 : this.WIDTH_ - 4) + ',' +
- (this.LID_HEIGHT_ - 2) + ')');
- var opacity = goog.math.lerp(0.4, 0.8, this.lidOpen_);
- this.svgGroup_.style.opacity = opacity;
- if (this.lidOpen_ > 0 && this.lidOpen_ < 1) {
- this.lidTask_ = goog.Timer.callOnce(this.animateLid_, 20, this);
- }
-};
-
-/**
- * Flip the lid shut.
- * Called externally after a drag.
- */
-Blockly.Trashcan.prototype.close = function() {
- this.setOpen_(false);
-};
-
-/**
- * Inspect the contents of the trash.
- */
-Blockly.Trashcan.prototype.click = function() {
- var dx = this.workspace_.startScrollX - this.workspace_.scrollX;
- var dy = this.workspace_.startScrollY - this.workspace_.scrollY;
- if (Math.sqrt(dx * dx + dy * dy) > Blockly.DRAG_RADIUS) {
- return;
- }
- console.log('TODO: Inspect trash.');
-};
diff --git a/core/variables.js.orig b/core/variables.js.orig
deleted file mode 100644
index 00c38ad21..000000000
--- a/core/variables.js.orig
+++ /dev/null
@@ -1,273 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Utility functions for handling variables.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Variables');
-
-goog.require('Blockly.Blocks');
-goog.require('Blockly.Workspace');
-goog.require('goog.string');
-
-
-/**
- * Category to separate variable names from procedures and generated functions.
- */
-Blockly.Variables.NAME_TYPE = 'VARIABLE';
-
-/**
- * Find all user-created variables that are in use in the workspace.
- * For use by generators.
- * @param {!Blockly.Block|!Blockly.Workspace} root Root block or workspace.
- * @return {!Array.} Array of variable names.
- */
-Blockly.Variables.allUsedVariables = function(root) {
- var blocks;
- if (root instanceof Blockly.Block) {
- // Root is Block.
- blocks = root.getDescendants();
- } else if (root.getAllBlocks) {
- // Root is Workspace.
- blocks = root.getAllBlocks();
- } else {
- throw 'Not Block or Workspace: ' + root;
- }
- var variableHash = Object.create(null);
- // Iterate through every block and add each variable to the hash.
- for (var x = 0; x < blocks.length; x++) {
- var blockVariables = blocks[x].getVars();
- if (blockVariables) {
- for (var y = 0; y < blockVariables.length; y++) {
- var varName = blockVariables[y];
- // Variable name may be null if the block is only half-built.
- if (varName) {
- variableHash[varName.toLowerCase()] = varName;
- }
- }
- }
- }
- // Flatten the hash into a list.
- var variableList = [];
- for (var name in variableHash) {
- variableList.push(variableHash[name]);
- }
- return variableList;
-};
-
-/**
- * Find all variables that the user has created through the workspace or
- * toolbox. For use by generators.
- * @param {!Blockly.Workspace} root The workspace to inspect.
- * @return {!Array.} Array of variable names.
- */
-Blockly.Variables.allVariables = function(root) {
- if (root instanceof Blockly.Block) {
- // Root is Block.
- console.warn('Deprecated call to Blockly.Variables.allVariables ' +
- 'with a block instead of a workspace. You may want ' +
- 'Blockly.Variables.allUsedVariables');
- }
- return root.variableList;
-};
-
-/**
- * Construct the blocks required by the flyout for the variable category.
- * @param {!Blockly.Workspace} workspace The workspace contianing variables.
- * @return {!Array.} Array of XML block elements.
- */
-Blockly.Variables.flyoutCategory = function(workspace) {
- var variableList = workspace.variableList;
- variableList.sort(goog.string.caseInsensitiveCompare);
-
- var xmlList = [];
- var button = goog.dom.createDom('button');
- button.setAttribute('text', Blockly.Msg.NEW_VARIABLE);
- xmlList.push(button);
-
- if (variableList.length > 0) {
- if (Blockly.Blocks['variables_set']) {
- //
- // item
- //
- var block = goog.dom.createDom('block');
- block.setAttribute('type', 'variables_set');
- if (Blockly.Blocks['math_change']) {
- block.setAttribute('gap', 8);
- } else {
- block.setAttribute('gap', 24);
- }
- var field = goog.dom.createDom('field', null, variableList[0]);
- field.setAttribute('name', 'VAR');
- block.appendChild(field);
- xmlList.push(block);
- }
- if (Blockly.Blocks['math_change']) {
- //
- //
- //
- // 1
- //
- //
- //
- var block = goog.dom.createDom('block');
- block.setAttribute('type', 'math_change');
- if (Blockly.Blocks['variables_get']) {
- block.setAttribute('gap', 20);
- }
- var value = goog.dom.createDom('value');
- value.setAttribute('name', 'DELTA');
- block.appendChild(value);
-
- var field = goog.dom.createDom('field', null, variableList[0]);
- field.setAttribute('name', 'VAR');
- block.appendChild(field);
-
- var shadowBlock = goog.dom.createDom('shadow');
- shadowBlock.setAttribute('type', 'math_number');
- value.appendChild(shadowBlock);
-
- var numberField = goog.dom.createDom('field', null, '1');
- numberField.setAttribute('name', 'NUM');
- shadowBlock.appendChild(numberField);
-
- xmlList.push(block);
- }
-
- for (var i = 0; i < variableList.length; i++) {
- if (Blockly.Blocks['variables_get']) {
- //
- // item
- //
- var block = goog.dom.createDom('block');
- block.setAttribute('type', 'variables_get');
- if (Blockly.Blocks['variables_set']) {
- block.setAttribute('gap', 8);
- }
- var field = goog.dom.createDom('field', null, variableList[i]);
- field.setAttribute('name', 'VAR');
- block.appendChild(field);
- xmlList.push(block);
- }
- }
- }
- return xmlList;
-};
-
-/**
-* Return a new variable name that is not yet being used. This will try to
-* generate single letter variable names in the range 'i' to 'z' to start with.
-* If no unique name is located it will try 'i' to 'z', 'a' to 'h',
-* then 'i2' to 'z2' etc. Skip 'l'.
- * @param {!Blockly.Workspace} workspace The workspace to be unique in.
-* @return {string} New variable name.
-*/
-Blockly.Variables.generateUniqueName = function(workspace) {
- var variableList = workspace.variableList;
- var newName = '';
- if (variableList.length) {
- var nameSuffix = 1;
- var letters = 'ijkmnopqrstuvwxyzabcdefgh'; // No 'l'.
- var letterIndex = 0;
- var potName = letters.charAt(letterIndex);
- while (!newName) {
- var inUse = false;
- for (var i = 0; i < variableList.length; i++) {
- if (variableList[i].toLowerCase() == potName) {
- // This potential name is already used.
- inUse = true;
- break;
- }
- }
- if (inUse) {
- // Try the next potential name.
- letterIndex++;
- if (letterIndex == letters.length) {
- // Reached the end of the character sequence so back to 'i'.
- // a new suffix.
- letterIndex = 0;
- nameSuffix++;
- }
- potName = letters.charAt(letterIndex);
- if (nameSuffix > 1) {
- potName += nameSuffix;
- }
- } else {
- // We can use the current potential name.
- newName = potName;
- }
- }
- } else {
- newName = 'i';
- }
- return newName;
-};
-
-/**
- * Create a new variable on the given workspace.
- * @param {!Blockly.Workspace} workspace The workspace on which to create the
- * variable.
- * @return {null|undefined|string} An acceptable new variable name, or null if
- * change is to be aborted (cancel button), or undefined if an existing
- * variable was chosen.
- */
-Blockly.Variables.createVariable = function(workspace) {
- while (true) {
- var text = Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, '');
- if (text) {
- if (workspace.variableIndexOf(text) != -1) {
- window.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1',
- text.toLowerCase()));
- } else {
- workspace.createVariable(text);
- break;
- }
- } else {
- text = null;
- break;
- }
- }
- return text;
-};
-
-/**
- * Prompt the user for a new variable name.
- * @param {string} promptText The string of the prompt.
- * @param {string} defaultText The default value to show in the prompt's field.
- * @return {?string} The new variable name, or null if the user picked
- * something illegal.
- */
-Blockly.Variables.promptName = function(promptText, defaultText) {
- var newVar = window.prompt(promptText, defaultText);
- // Merge runs of whitespace. Strip leading and trailing whitespace.
- // Beyond this, all names are legal.
- if (newVar) {
- newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
- if (newVar == Blockly.Msg.RENAME_VARIABLE ||
- newVar == Blockly.Msg.NEW_VARIABLE) {
- // Ok, not ALL names are legal...
- newVar = null;
- }
- }
- return newVar;
-};
diff --git a/core/warning.js.orig b/core/warning.js.orig
deleted file mode 100644
index bffbf06df..000000000
--- a/core/warning.js.orig
+++ /dev/null
@@ -1,185 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Object representing a warning.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Warning');
-
-goog.require('Blockly.Bubble');
-goog.require('Blockly.Icon');
-
-
-/**
- * Class for a warning.
- * @param {!Blockly.Block} block The block associated with this warning.
- * @extends {Blockly.Icon}
- * @constructor
- */
-Blockly.Warning = function(block) {
- Blockly.Warning.superClass_.constructor.call(this, block);
- this.createIcon();
- // The text_ object can contain multiple warnings.
- this.text_ = {};
-};
-goog.inherits(Blockly.Warning, Blockly.Icon);
-
-/**
- * Does this icon get hidden when the block is collapsed.
- */
-Blockly.Warning.prototype.collapseHidden = false;
-
-/**
- * Draw the warning icon.
- * @param {!Element} group The icon group.
- * @private
- */
-Blockly.Warning.prototype.drawIcon_ = function(group) {
- // Triangle with rounded corners.
- Blockly.createSvgElement('path',
- {'class': 'blocklyIconShape',
- 'd': 'M2,15Q-1,15 0.5,12L6.5,1.7Q8,-1 9.5,1.7L15.5,12Q17,15 14,15z'},
- group);
- // Can't use a real '!' text character since different browsers and operating
- // systems render it differently.
- // Body of exclamation point.
- Blockly.createSvgElement('path',
- {'class': 'blocklyIconSymbol',
- 'd': 'm7,4.8v3.16l0.27,2.27h1.46l0.27,-2.27v-3.16z'},
- group);
- // Dot of exclamation point.
- Blockly.createSvgElement('rect',
- {'class': 'blocklyIconSymbol',
- 'x': '7', 'y': '11', 'height': '2', 'width': '2'},
- group);
-};
-
-/**
- * Create the text for the warning's bubble.
- * @param {string} text The text to display.
- * @return {!SVGTextElement} The top-level node of the text.
- * @private
- */
-Blockly.Warning.textToDom_ = function(text) {
- var paragraph = /** @type {!SVGTextElement} */ (
- Blockly.createSvgElement('text',
- {'class': 'blocklyText blocklyBubbleText',
- 'y': Blockly.Bubble.BORDER_WIDTH},
- null));
- var lines = text.split('\n');
- for (var i = 0; i < lines.length; i++) {
- var tspanElement = Blockly.createSvgElement('tspan',
- {'dy': '1em', 'x': Blockly.Bubble.BORDER_WIDTH}, paragraph);
- var textNode = document.createTextNode(lines[i]);
- tspanElement.appendChild(textNode);
- }
- return paragraph;
-};
-
-/**
- * Show or hide the warning bubble.
- * @param {boolean} visible True if the bubble should be visible.
- */
-Blockly.Warning.prototype.setVisible = function(visible) {
- if (visible == this.isVisible()) {
- // No change.
- return;
- }
- Blockly.Events.fire(
- new Blockly.Events.Ui(this.block_, 'warningOpen', !visible, visible));
- if (visible) {
- // Create the bubble to display all warnings.
- var paragraph = Blockly.Warning.textToDom_(this.getText());
- this.bubble_ = new Blockly.Bubble(
- /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace),
- paragraph, this.block_.svgPath_, this.iconXY_, null, null);
- if (this.block_.RTL) {
- // Right-align the paragraph.
- // This cannot be done until the bubble is rendered on screen.
- var maxWidth = paragraph.getBBox().width;
- for (var i = 0, textElement; textElement = paragraph.childNodes[i]; i++) {
- textElement.setAttribute('text-anchor', 'end');
- textElement.setAttribute('x', maxWidth + Blockly.Bubble.BORDER_WIDTH);
- }
- }
- this.updateColour();
- // Bump the warning into the right location.
- var size = this.bubble_.getBubbleSize();
- this.bubble_.setBubbleSize(size.width, size.height);
- } else {
- // Dispose of the bubble.
- this.bubble_.dispose();
- this.bubble_ = null;
- this.body_ = null;
- }
-};
-
-/**
- * Bring the warning to the top of the stack when clicked on.
- * @param {!Event} e Mouse up event.
- * @private
- */
-Blockly.Warning.prototype.bodyFocus_ = function(e) {
- this.bubble_.promote_();
-};
-
-/**
- * Set this warning's text.
- * @param {string} text Warning text (or '' to delete).
- * @param {string} id An ID for this text entry to be able to maintain
- * multiple warnings.
- */
-Blockly.Warning.prototype.setText = function(text, id) {
- if (this.text_[id] == text) {
- return;
- }
- if (text) {
- this.text_[id] = text;
- } else {
- delete this.text_[id];
- }
- if (this.isVisible()) {
- this.setVisible(false);
- this.setVisible(true);
- }
-};
-
-/**
- * Get this warning's texts.
- * @return {string} All texts concatenated into one string.
- */
-Blockly.Warning.prototype.getText = function() {
- var allWarnings = [];
- for (var id in this.text_) {
- allWarnings.push(this.text_[id]);
- }
- return allWarnings.join('\n');
-};
-
-/**
- * Dispose of this warning.
- */
-Blockly.Warning.prototype.dispose = function() {
- this.block_.warning = null;
- Blockly.Icon.prototype.dispose.call(this);
-};
diff --git a/core/workspace.js.orig b/core/workspace.js.orig
deleted file mode 100644
index a8e97c04b..000000000
--- a/core/workspace.js.orig
+++ /dev/null
@@ -1,501 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 Object representing a workspace.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Workspace');
-
-goog.require('goog.math');
-
-
-/**
- * Class for a workspace. This is a data structure that contains blocks.
- * There is no UI, and can be created headlessly.
- * @param {Blockly.Options} opt_options Dictionary of options.
- * @constructor
- */
-Blockly.Workspace = function(opt_options) {
- /** @type {string} */
- this.id = Blockly.genUid();
- Blockly.Workspace.WorkspaceDB_[this.id] = this;
- /** @type {!Blockly.Options} */
- this.options = opt_options || {};
- /** @type {boolean} */
- this.RTL = !!this.options.RTL;
- /** @type {boolean} */
- this.horizontalLayout = !!this.options.horizontalLayout;
- /** @type {number} */
- this.toolboxPosition = this.options.toolboxPosition;
-
- /**
- * @type {!Array.}
- * @private
- */
- this.topBlocks_ = [];
- /**
- * @type {!Array.}
- * @private
- */
- this.listeners_ = [];
- /**
- * @type {!Array.}
- * @private
- */
- this.undoStack_ = [];
- /**
- * @type {!Array.}
- * @private
- */
- this.redoStack_ = [];
- /**
- * @type {!Object}
- * @private
- */
- this.blockDB_ = Object.create(null);
- /*
- * @type {!Array.}
- * A list of all of the named variables in the workspace, including variables
- * that are not currently in use.
- */
- this.variableList = [];
-};
-
-/**
- * Workspaces may be headless.
- * @type {boolean} True if visible. False if headless.
- */
-Blockly.Workspace.prototype.rendered = false;
-
-/**
- * Maximum number of undo events in stack.
- * @type {number} 0 to turn off undo, Infinity for unlimited.
- */
-Blockly.Workspace.prototype.MAX_UNDO = 1024;
-
-/**
- * Dispose of this workspace.
- * Unlink from all DOM elements to prevent memory leaks.
- */
-Blockly.Workspace.prototype.dispose = function() {
- this.listeners_.length = 0;
- this.clear();
- // Remove from workspace database.
- delete Blockly.Workspace.WorkspaceDB_[this.id];
-};
-
-/**
- * Angle away from the horizontal to sweep for blocks. Order of execution is
- * generally top to bottom, but a small angle changes the scan to give a bit of
- * a left to right bias (reversed in RTL). Units are in degrees.
- * See: http://tvtropes.org/pmwiki/pmwiki.php/Main/DiagonalBilling.
- */
-Blockly.Workspace.SCAN_ANGLE = 3;
-
-/**
- * Add a block to the list of top blocks.
- * @param {!Blockly.Block} block Block to remove.
- */
-Blockly.Workspace.prototype.addTopBlock = function(block) {
- this.topBlocks_.push(block);
- if (this.isFlyout) {
- // This is for the (unlikely) case where you have a variable in a block in
- // an always-open flyout. It needs to be possible to edit the block in the
- // flyout, so the contents of the dropdown need to be correct.
- var variables = Blockly.Variables.allUsedVariables(block);
- for (var i = 0; i < variables.length; i++) {
- if (this.variableList.indexOf(variables[i]) == -1) {
- this.variableList.push(variables[i]);
- }
- }
- }
-};
-
-/**
- * Remove a block from the list of top blocks.
- * @param {!Blockly.Block} block Block to remove.
- */
-Blockly.Workspace.prototype.removeTopBlock = function(block) {
- var found = false;
- for (var child, i = 0; child = this.topBlocks_[i]; i++) {
- if (child == block) {
- this.topBlocks_.splice(i, 1);
- found = true;
- break;
- }
- }
- if (!found) {
- throw 'Block not present in workspace\'s list of top-most blocks.';
- }
-};
-
-/**
- * Finds the top-level blocks and returns them. Blocks are optionally sorted
- * by position; top to bottom (with slight LTR or RTL bias).
- * @param {boolean} ordered Sort the list if true.
- * @return {!Array.} The top-level block objects.
- */
-Blockly.Workspace.prototype.getTopBlocks = function(ordered) {
- // Copy the topBlocks_ list.
- var blocks = [].concat(this.topBlocks_);
- if (ordered && blocks.length > 1) {
- var offset = Math.sin(goog.math.toRadians(Blockly.Workspace.SCAN_ANGLE));
- if (this.RTL) {
- offset *= -1;
- }
- blocks.sort(function(a, b) {
- var aXY = a.getRelativeToSurfaceXY();
- var bXY = b.getRelativeToSurfaceXY();
- return (aXY.y + offset * aXY.x) - (bXY.y + offset * bXY.x);
- });
- }
- return blocks;
-};
-
-/**
- * Find all blocks in workspace. No particular order.
- * @return {!Array.} Array of blocks.
- */
-Blockly.Workspace.prototype.getAllBlocks = function() {
- var blocks = this.getTopBlocks(false);
- for (var i = 0; i < blocks.length; i++) {
- blocks.push.apply(blocks, blocks[i].getChildren());
- }
- return blocks;
-};
-
-/**
- * Dispose of all blocks in workspace.
- */
-Blockly.Workspace.prototype.clear = function() {
- var existingGroup = Blockly.Events.getGroup();
- if (!existingGroup) {
- Blockly.Events.setGroup(true);
- }
- while (this.topBlocks_.length) {
- this.topBlocks_[0].dispose();
- }
- if (!existingGroup) {
- Blockly.Events.setGroup(false);
- }
-
- this.variableList.length = 0;
-};
-
-/**
- * Walk the workspace and update the list of variables to only contain ones in
- * use on the workspace. Use when loading new workspaces from disk.
- * @param {boolean} clearList True if the old variable list should be cleared.
- */
-Blockly.Workspace.prototype.updateVariableList = function(clearList) {
- // TODO: Sort
- if (!this.isFlyout) {
- // Update the list in place so that the flyout's references stay correct.
- if (clearList) {
- this.variableList.length = 0;
- }
- var allVariables = Blockly.Variables.allUsedVariables(this);
- for (var i = 0; i < allVariables.length; i++) {
- this.createVariable(allVariables[i]);
- }
- }
-};
-
-/**
- * Rename a variable by updating its name in the variable list.
- * TODO: #468
- * @param {string} oldName Variable to rename.
- * @param {string} newName New variable name.
- */
-Blockly.Workspace.prototype.renameVariable = function(oldName, newName) {
- // Find the old name in the list.
- var variableIndex = this.variableIndexOf(oldName);
- var newVariableIndex = this.variableIndexOf(newName);
-
- // We might be renaming to an existing name but with different case. If so,
- // we will also update all of the blocks using the new name to have the
- // correct case.
- if (newVariableIndex != -1 &&
- this.variableList[newVariableIndex] != newName) {
- var oldCase = this.variableList[newVariableIndex];
- }
-
- Blockly.Events.setGroup(true);
- var blocks = this.getAllBlocks();
- // Iterate through every block.
- for (var i = 0; i < blocks.length; i++) {
- blocks[i].renameVar(oldName, newName);
- if (oldCase) {
- blocks[i].renameVar(oldCase, newName);
- }
- }
- Blockly.Events.setGroup(false);
-
-
- if (variableIndex == newVariableIndex ||
- variableIndex != -1 && newVariableIndex == -1) {
- // Only changing case, or renaming to a completely novel name.
- this.variableList[variableIndex] = newName;
- } else if (variableIndex != -1 && newVariableIndex != -1) {
- // Renaming one existing variable to another existing variable.
- this.variableList.splice(variableIndex, 1);
- // The case might have changed.
- this.variableList[newVariableIndex] = newName;
- } else {
- this.variableList.push(newName);
- console.log('Tried to rename an non-existent variable.');
- }
-};
-
-/**
- * Create a variable with the given name.
- * TODO: #468
- * @param {string} name The new variable's name.
- */
-Blockly.Workspace.prototype.createVariable = function(name) {
- var index = this.variableIndexOf(name);
- if (index == -1) {
- this.variableList.push(name);
- }
-};
-
-/**
- * Find all the uses of a named variable.
- * @param {string} name Name of variable.
- * @return {!Array.} Array of block usages.
- */
-Blockly.Workspace.prototype.getVariableUses = function(name) {
- var uses = [];
- var blocks = this.getAllBlocks();
- // Iterate through every block and check the name.
- for (var i = 0; i < blocks.length; i++) {
- var blockVariables = blocks[i].getVars();
- if (blockVariables) {
- for (var j = 0; j < blockVariables.length; j++) {
- var varName = blockVariables[j];
- // Variable name may be null if the block is only half-built.
- if (varName && Blockly.Names.equals(varName, name)) {
- uses.push(blocks[i]);
- }
- }
- }
- }
- return uses;
-};
-
-/**
- * Delete a variables and all of its uses from this workspace.
- * @param {string} name Name of variable to delete.
- */
-Blockly.Workspace.prototype.deleteVariable = function(name) {
- var variableIndex = this.variableIndexOf(name);
- if (variableIndex != -1) {
- var uses = this.getVariableUses(name);
- if (uses.length > 1) {
- for (var i = 0, block; block = uses[i]; i++) {
- if (block.type == 'procedures_defnoreturn' ||
- block.type == 'procedures_defreturn') {
- var procedureName = block.getFieldValue('NAME');
- window.alert(
- Blockly.Msg.CANNOT_DELETE_VARIABLE_PROCEDURE.replace('%1', name).
- replace('%2', procedureName));
- return;
- }
- }
- var ok = window.confirm(
- Blockly.Msg.DELETE_VARIABLE_CONFIRMATION.replace('%1', uses.length).
- replace('%2', name));
- if (!ok) {
- return;
- }
- }
-
- Blockly.Events.setGroup(true);
- for (var i = 0; i < uses.length; i++) {
- uses[i].dispose(true, false);
- }
- Blockly.Events.setGroup(false);
- this.variableList.splice(variableIndex, 1);
- }
-};
-
-/**
- * Check whether a variable exists with the given name. The check is
- * case-insensitive.
- * @param {string} name The name to check for.
- * @return {number} The index of the name in the variable list, or -1 if it is
- * not present.
- */
-Blockly.Workspace.prototype.variableIndexOf = function(name) {
- for (var i = 0, varname; varname = this.variableList[i]; i++) {
- if (Blockly.Names.equals(varname, name)) {
- return i;
- }
- }
- return -1;
-};
-
-/**
- * Returns the horizontal offset of the workspace.
- * Intended for LTR/RTL compatibility in XML.
- * Not relevant for a headless workspace.
- * @return {number} Width.
- */
-Blockly.Workspace.prototype.getWidth = function() {
- return 0;
-};
-
-/**
- * Obtain a newly created block.
- * @param {?string} prototypeName Name of the language object containing
- * type-specific functions for this block.
- * @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
- * create a new id.
- * @return {!Blockly.Block} The created block.
- */
-Blockly.Workspace.prototype.newBlock = function(prototypeName, opt_id) {
- return new Blockly.Block(this, prototypeName, opt_id);
-};
-
-/**
- * The number of blocks that may be added to the workspace before reaching
- * the maxBlocks.
- * @return {number} Number of blocks left.
- */
-Blockly.Workspace.prototype.remainingCapacity = function() {
- if (isNaN(this.options.maxBlocks)) {
- return Infinity;
- }
- return this.options.maxBlocks - this.getAllBlocks().length;
-};
-
-/**
- * Undo or redo the previous action.
- * @param {boolean} redo False if undo, true if redo.
- */
-Blockly.Workspace.prototype.undo = function(redo) {
- var inputStack = redo ? this.redoStack_ : this.undoStack_;
- var outputStack = redo ? this.undoStack_ : this.redoStack_;
- var inputEvent = inputStack.pop();
- if (!inputEvent) {
- return;
- }
- var events = [inputEvent];
- // Do another undo/redo if the next one is of the same group.
- while (inputStack.length && inputEvent.group &&
- inputEvent.group == inputStack[inputStack.length - 1].group) {
- events.push(inputStack.pop());
- }
- // Push these popped events on the opposite stack.
- for (var i = 0, event; event = events[i]; i++) {
- outputStack.push(event);
- }
- events = Blockly.Events.filter(events, redo);
- Blockly.Events.recordUndo = false;
- for (var i = 0, event; event = events[i]; i++) {
- event.run(redo);
- }
- Blockly.Events.recordUndo = true;
-};
-
-/**
- * Clear the undo/redo stacks.
- */
-Blockly.Workspace.prototype.clearUndo = function() {
- this.undoStack_.length = 0;
- this.redoStack_.length = 0;
- // Stop any events already in the firing queue from being undoable.
- Blockly.Events.clearPendingUndo();
-};
-
-/**
- * When something in this workspace changes, call a function.
- * @param {!Function} func Function to call.
- * @return {!Function} Function that can be passed to
- * removeChangeListener.
- */
-Blockly.Workspace.prototype.addChangeListener = function(func) {
- this.listeners_.push(func);
- return func;
-};
-
-/**
- * Stop listening for this workspace's changes.
- * @param {Function} func Function to stop calling.
- */
-Blockly.Workspace.prototype.removeChangeListener = function(func) {
- var i = this.listeners_.indexOf(func);
- if (i != -1) {
- this.listeners_.splice(i, 1);
- }
-};
-
-/**
- * Fire a change event.
- * @param {!Blockly.Events.Abstract} event Event to fire.
- */
-Blockly.Workspace.prototype.fireChangeListener = function(event) {
- if (event.recordUndo) {
- this.undoStack_.push(event);
- this.redoStack_.length = 0;
- if (this.undoStack_.length > this.MAX_UNDO) {
- this.undoStack_.unshift();
- }
- }
- for (var i = 0, func; func = this.listeners_[i]; i++) {
- func(event);
- }
-};
-
-/**
- * Find the block on this workspace with the specified ID.
- * @param {string} id ID of block to find.
- * @return {Blockly.Block} The sought after block or null if not found.
- */
-Blockly.Workspace.prototype.getBlockById = function(id) {
- return this.blockDB_[id] || null;
-};
-
-/**
- * Database of all workspaces.
- * @private
- */
-Blockly.Workspace.WorkspaceDB_ = Object.create(null);
-
-/**
- * Find the workspace with the specified ID.
- * @param {string} id ID of workspace to find.
- * @return {Blockly.Workspace} The sought after workspace or null if not found.
- */
-Blockly.Workspace.getById = function(id) {
- return Blockly.Workspace.WorkspaceDB_[id] || null;
-};
-
-// Export symbols that would otherwise be renamed by Closure compiler.
-Blockly.Workspace.prototype['clear'] = Blockly.Workspace.prototype.clear;
-Blockly.Workspace.prototype['clearUndo'] =
- Blockly.Workspace.prototype.clearUndo;
-Blockly.Workspace.prototype['addChangeListener'] =
- Blockly.Workspace.prototype.addChangeListener;
-Blockly.Workspace.prototype['removeChangeListener'] =
- Blockly.Workspace.prototype.removeChangeListener;
diff --git a/core/xml.js.orig b/core/xml.js.orig
deleted file mode 100644
index 2567560cd..000000000
--- a/core/xml.js.orig
+++ /dev/null
@@ -1,566 +0,0 @@
-/**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2012 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 XML reader and writer.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-goog.provide('Blockly.Xml');
-
-goog.require('goog.asserts');
-goog.require('goog.dom');
-
-
-/**
- * Encode a block tree as XML.
- * @param {!Blockly.Workspace} workspace The workspace containing blocks.
- * @return {!Element} XML document.
- */
-Blockly.Xml.workspaceToDom = function(workspace) {
- var xml = goog.dom.createDom('xml');
- var blocks = workspace.getTopBlocks(true);
- for (var i = 0, block; block = blocks[i]; i++) {
- xml.appendChild(Blockly.Xml.blockToDomWithXY(block));
- }
- return xml;
-};
-
-/**
- * Encode a block subtree as XML with XY coordinates.
- * @param {!Blockly.Block} block The root block to encode.
- * @return {!Element} Tree of XML elements.
- */
-Blockly.Xml.blockToDomWithXY = function(block) {
- var width; // Not used in LTR.
- if (block.workspace.RTL) {
- width = block.workspace.getWidth();
- }
- var element = Blockly.Xml.blockToDom(block);
- var xy = block.getRelativeToSurfaceXY();
- element.setAttribute('x',
- Math.round(block.workspace.RTL ? width - xy.x : xy.x));
- element.setAttribute('y', Math.round(xy.y));
- return element;
-};
-
-/**
- * Encode a block subtree as XML.
- * @param {!Blockly.Block} block The root block to encode.
- * @return {!Element} Tree of XML elements.
- */
-Blockly.Xml.blockToDom = function(block) {
- var element = goog.dom.createDom(block.isShadow() ? 'shadow' : 'block');
- element.setAttribute('type', block.type);
- element.setAttribute('id', block.id);
- if (block.mutationToDom) {
- // Custom data for an advanced block.
- var mutation = block.mutationToDom();
- if (mutation && (mutation.hasChildNodes() || mutation.hasAttributes())) {
- element.appendChild(mutation);
- }
- }
- function fieldToDom(field) {
- if (field.name && field.EDITABLE) {
- var container = goog.dom.createDom('field', null, field.getValue());
- container.setAttribute('name', field.name);
- element.appendChild(container);
- }
- }
- for (var i = 0, input; input = block.inputList[i]; i++) {
- for (var j = 0, field; field = input.fieldRow[j]; j++) {
- fieldToDom(field);
- }
- }
-
- var commentText = block.getCommentText();
- if (commentText) {
- var commentElement = goog.dom.createDom('comment', null, commentText);
- if (typeof block.comment == 'object') {
- commentElement.setAttribute('pinned', block.comment.isVisible());
- var hw = block.comment.getBubbleSize();
- commentElement.setAttribute('h', hw.height);
- commentElement.setAttribute('w', hw.width);
- }
- element.appendChild(commentElement);
- }
-
- if (block.data) {
- var dataElement = goog.dom.createDom('data', null, block.data);
- element.appendChild(dataElement);
- }
-
- for (var i = 0, input; input = block.inputList[i]; i++) {
- var container;
- var empty = true;
- if (input.type == Blockly.DUMMY_INPUT) {
- continue;
- } else {
- var childBlock = input.connection.targetBlock();
- if (input.type == Blockly.INPUT_VALUE) {
- container = goog.dom.createDom('value');
- } else if (input.type == Blockly.NEXT_STATEMENT) {
- container = goog.dom.createDom('statement');
- }
- var shadow = input.connection.getShadowDom();
- if (shadow && (!childBlock || !childBlock.isShadow())) {
- container.appendChild(Blockly.Xml.cloneShadow_(shadow));
- }
- if (childBlock) {
- container.appendChild(Blockly.Xml.blockToDom(childBlock));
- empty = false;
- }
- }
- container.setAttribute('name', input.name);
- if (!empty) {
- element.appendChild(container);
- }
- }
- if (block.inputsInlineDefault != block.inputsInline) {
- element.setAttribute('inline', block.inputsInline);
- }
- if (block.isCollapsed()) {
- element.setAttribute('collapsed', true);
- }
- if (block.disabled) {
- element.setAttribute('disabled', true);
- }
- if (!block.isDeletable() && !block.isShadow()) {
- element.setAttribute('deletable', false);
- }
- if (!block.isMovable() && !block.isShadow()) {
- element.setAttribute('movable', false);
- }
- if (!block.isEditable()) {
- element.setAttribute('editable', false);
- }
-
- var nextBlock = block.getNextBlock();
- if (nextBlock) {
- var container = goog.dom.createDom('next', null,
- Blockly.Xml.blockToDom(nextBlock));
- element.appendChild(container);
- }
- var shadow = block.nextConnection && block.nextConnection.getShadowDom();
- if (shadow && (!nextBlock || !nextBlock.isShadow())) {
- container.appendChild(Blockly.Xml.cloneShadow_(shadow));
- }
-
- return element;
-};
-
-/**
- * Deeply clone the shadow's DOM so that changes don't back-wash to the block.
- * @param {!Element} shadow A tree of XML elements.
- * @return {!Element} A tree of XML elements.
- * @private
- */
-Blockly.Xml.cloneShadow_ = function(shadow) {
- shadow = shadow.cloneNode(true);
- // Walk the tree looking for whitespace. Don't prune whitespace in a tag.
- var node = shadow;
- var textNode;
- while (node) {
- if (node.firstChild) {
- node = node.firstChild;
- } else {
- while (node && !node.nextSibling) {
- textNode = node;
- node = node.parentNode;
- if (textNode.nodeType == 3 && textNode.data.trim() == '' &&
- node.firstChild != textNode) {
- // Prune whitespace after a tag.
- goog.dom.removeNode(textNode);
- }
- }
- if (node) {
- textNode = node;
- node = node.nextSibling;
- if (textNode.nodeType == 3 && textNode.data.trim() == '') {
- // Prune whitespace before a tag.
- goog.dom.removeNode(textNode);
- }
- }
- }
- }
- return shadow;
-};
-
-/**
- * Converts a DOM structure into plain text.
- * Currently the text format is fairly ugly: all one line with no whitespace.
- * @param {!Element} dom A tree of XML elements.
- * @return {string} Text representation.
- */
-Blockly.Xml.domToText = function(dom) {
- var oSerializer = new XMLSerializer();
- return oSerializer.serializeToString(dom);
-};
-
-/**
- * Converts a DOM structure into properly indented text.
- * @param {!Element} dom A tree of XML elements.
- * @return {string} Text representation.
- */
-Blockly.Xml.domToPrettyText = function(dom) {
- // This function is not guaranteed to be correct for all XML.
- // But it handles the XML that Blockly generates.
- var blob = Blockly.Xml.domToText(dom);
- // Place every open and close tag on its own line.
- var lines = blob.split('<');
- // Indent every line.
- var indent = '';
- for (var i = 1; i < lines.length; i++) {
- var line = lines[i];
- if (line[0] == '/') {
- indent = indent.substring(2);
- }
- lines[i] = indent + '<' + line;
- if (line[0] != '/' && line.slice(-2) != '/>') {
- indent += ' ';
- }
- }
- // Pull simple tags back together.
- // E.g.
- var text = lines.join('\n');
- text = text.replace(/(<(\w+)\b[^>]*>[^\n]*)\n *<\/\2>/g, '$1$2>');
- // Trim leading blank line.
- return text.replace(/^\n/, '');
-};
-
-/**
- * Converts plain text into a DOM structure.
- * Throws an error if XML doesn't parse.
- * @param {string} text Text representation.
- * @return {!Element} A tree of XML elements.
- */
-Blockly.Xml.textToDom = function(text) {
- var oParser = new DOMParser();
- var dom = oParser.parseFromString(text, 'text/xml');
- // The DOM should have one and only one top-level node, an XML tag.
- if (!dom || !dom.firstChild ||
- dom.firstChild.nodeName.toLowerCase() != 'xml' ||
- dom.firstChild !== dom.lastChild) {
- // Whatever we got back from the parser is not XML.
- goog.asserts.fail('Blockly.Xml.textToDom did not obtain a valid XML tree.');
- }
- return dom.firstChild;
-};
-
-/**
- * Decode an XML DOM and create blocks on the workspace.
- * @param {!Element} xml XML DOM.
- * @param {!Blockly.Workspace} workspace The workspace.
- */
-Blockly.Xml.domToWorkspace = function(xml, workspace) {
- if (xml instanceof Blockly.Workspace) {
- var swap = xml;
- xml = workspace;
- workspace = swap;
- console.warn('Deprecated call to Blockly.Xml.domToWorkspace, ' +
- 'swap the arguments.');
- }
- var width; // Not used in LTR.
- if (workspace.RTL) {
- width = workspace.getWidth();
- }
- Blockly.Field.startCache();
- // Safari 7.1.3 is known to provide node lists with extra references to
- // children beyond the lists' length. Trust the length, do not use the
- // looping pattern of checking the index for an object.
- var childCount = xml.childNodes.length;
- var existingGroup = Blockly.Events.getGroup();
- if (!existingGroup) {
- Blockly.Events.setGroup(true);
- }
- for (var i = 0; i < childCount; i++) {
- var xmlChild = xml.childNodes[i];
- var name = xmlChild.nodeName.toLowerCase();
- if (name == 'block' ||
- (name == 'shadow' && !Blockly.Events.recordUndo)) {
- // Allow top-level shadow blocks if recordUndo is disabled since
- // that means an undo is in progress. Such a block is expected
- // to be moved to a nested destination in the next operation.
- var block = Blockly.Xml.domToBlock(xmlChild, workspace);
- var blockX = parseInt(xmlChild.getAttribute('x'), 10);
- var blockY = parseInt(xmlChild.getAttribute('y'), 10);
- if (!isNaN(blockX) && !isNaN(blockY)) {
- block.moveBy(workspace.RTL ? width - blockX : blockX, blockY);
- }
- } else if (name == 'shadow') {
- goog.asserts.fail('Shadow block cannot be a top-level block.');
- }
- }
- if (!existingGroup) {
- Blockly.Events.setGroup(false);
- }
- Blockly.Field.stopCache();
-
- workspace.updateVariableList(false);
-};
-
-/**
- * Decode an XML block tag and create a block (and possibly sub blocks) on the
- * workspace.
- * @param {!Element} xmlBlock XML block element.
- * @param {!Blockly.Workspace} workspace The workspace.
- * @return {!Blockly.Block} The root block created.
- */
-Blockly.Xml.domToBlock = function(xmlBlock, workspace) {
- if (xmlBlock instanceof Blockly.Workspace) {
- var swap = xmlBlock;
- xmlBlock = workspace;
- workspace = swap;
- console.warn('Deprecated call to Blockly.Xml.domToBlock, ' +
- 'swap the arguments.');
- }
- // Create top-level block.
- Blockly.Events.disable();
- try {
- var topBlock = Blockly.Xml.domToBlockHeadless_(xmlBlock, workspace);
- if (workspace.rendered) {
- // Hide connections to speed up assembly.
- topBlock.setConnectionsHidden(true);
- // Generate list of all blocks.
- var blocks = topBlock.getDescendants();
- // Render each block.
- for (var i = blocks.length - 1; i >= 0; i--) {
- blocks[i].initSvg();
- }
- for (var i = blocks.length - 1; i >= 0; i--) {
- blocks[i].render(false);
- }
- // Populating the connection database may be defered until after the
- // blocks have rendered.
- setTimeout(function() {
- if (topBlock.workspace) { // Check that the block hasn't been deleted.
- topBlock.setConnectionsHidden(false);
- }
- }, 1);
- topBlock.updateDisabled();
- // Allow the scrollbars to resize and move based on the new contents.
- // TODO(@picklesrus): #387. Remove when domToBlock avoids resizing.
- workspace.resizeContents();
- }
- } finally {
- Blockly.Events.enable();
- }
- if (Blockly.Events.isEnabled()) {
- Blockly.Events.fire(new Blockly.Events.Create(topBlock));
- }
- return topBlock;
-};
-
-/**
- * Decode an XML block tag and create a block (and possibly sub blocks) on the
- * workspace.
- * @param {!Element} xmlBlock XML block element.
- * @param {!Blockly.Workspace} workspace The workspace.
- * @return {!Blockly.Block} The root block created.
- * @private
- */
-Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) {
- var block = null;
- var prototypeName = xmlBlock.getAttribute('type');
- goog.asserts.assert(prototypeName, 'Block type unspecified: %s',
- xmlBlock.outerHTML);
- var id = xmlBlock.getAttribute('id');
- block = workspace.newBlock(prototypeName, id);
-
- var blockChild = null;
- for (var i = 0, xmlChild; xmlChild = xmlBlock.childNodes[i]; i++) {
- if (xmlChild.nodeType == 3) {
- // Ignore any text at the level. It's all whitespace anyway.
- continue;
- }
- var input;
-
- // Find any enclosed blocks or shadows in this tag.
- var childBlockNode = null;
- var childShadowNode = null;
- for (var j = 0, grandchildNode; grandchildNode = xmlChild.childNodes[j];
- j++) {
- if (grandchildNode.nodeType == 1) {
- if (grandchildNode.nodeName.toLowerCase() == 'block') {
- childBlockNode = grandchildNode;
- } else if (grandchildNode.nodeName.toLowerCase() == 'shadow') {
- childShadowNode = grandchildNode;
- }
- }
- }
- // Use the shadow block if there is no child block.
- if (!childBlockNode && childShadowNode) {
- childBlockNode = childShadowNode;
- }
-
- var name = xmlChild.getAttribute('name');
- switch (xmlChild.nodeName.toLowerCase()) {
- case 'mutation':
- // Custom data for an advanced block.
- if (block.domToMutation) {
- block.domToMutation(xmlChild);
- if (block.initSvg) {
- // Mutation may have added some elements that need initalizing.
- block.initSvg();
- }
- }
- break;
- case 'comment':
- block.setCommentText(xmlChild.textContent);
- var visible = xmlChild.getAttribute('pinned');
- if (visible && !block.isInFlyout) {
- // Give the renderer a millisecond to render and position the block
- // before positioning the comment bubble.
- setTimeout(function() {
- if (block.comment && block.comment.setVisible) {
- block.comment.setVisible(visible == 'true');
- }
- }, 1);
- }
- var bubbleW = parseInt(xmlChild.getAttribute('w'), 10);
- var bubbleH = parseInt(xmlChild.getAttribute('h'), 10);
- if (!isNaN(bubbleW) && !isNaN(bubbleH) &&
- block.comment && block.comment.setVisible) {
- block.comment.setBubbleSize(bubbleW, bubbleH);
- }
- break;
- case 'data':
- block.data = xmlChild.textContent;
- break;
- case 'title':
- // Titles were renamed to field in December 2013.
- // Fall through.
- case 'field':
- var field = block.getField(name);
- if (!field) {
- console.warn('Ignoring non-existent field ' + name + ' in block ' +
- prototypeName);
- break;
- }
- field.setValue(xmlChild.textContent);
- break;
- case 'value':
- case 'statement':
- input = block.getInput(name);
- if (!input) {
- console.warn('Ignoring non-existent input ' + name + ' in block ' +
- prototypeName);
- break;
- }
- if (childShadowNode) {
- input.connection.setShadowDom(childShadowNode);
- }
- if (childBlockNode) {
- blockChild = Blockly.Xml.domToBlockHeadless_(childBlockNode,
- workspace);
- if (blockChild.outputConnection) {
- input.connection.connect(blockChild.outputConnection);
- } else if (blockChild.previousConnection) {
- input.connection.connect(blockChild.previousConnection);
- } else {
- goog.asserts.fail(
- 'Child block does not have output or previous statement.');
- }
- }
- break;
- case 'next':
- if (childShadowNode && block.nextConnection) {
- block.nextConnection.setShadowDom(childShadowNode);
- }
- if (childBlockNode) {
- goog.asserts.assert(block.nextConnection,
- 'Next statement does not exist.');
- // If there is more than one XML 'next' tag.
- goog.asserts.assert(!block.nextConnection.isConnected(),
- 'Next statement is already connected.');
- blockChild = Blockly.Xml.domToBlockHeadless_(childBlockNode,
- workspace);
- goog.asserts.assert(blockChild.previousConnection,
- 'Next block does not have previous statement.');
- block.nextConnection.connect(blockChild.previousConnection);
- }
- break;
- default:
- // Unknown tag; ignore. Same principle as HTML parsers.
- console.warn('Ignoring unknown tag: ' + xmlChild.nodeName);
- }
- }
-
- var inline = xmlBlock.getAttribute('inline');
- if (inline) {
- block.setInputsInline(inline == 'true');
- }
- var disabled = xmlBlock.getAttribute('disabled');
- if (disabled) {
- block.setDisabled(disabled == 'true');
- }
- var deletable = xmlBlock.getAttribute('deletable');
- if (deletable) {
- block.setDeletable(deletable == 'true');
- }
- var movable = xmlBlock.getAttribute('movable');
- if (movable) {
- block.setMovable(movable == 'true');
- }
- var editable = xmlBlock.getAttribute('editable');
- if (editable) {
- block.setEditable(editable == 'true');
- }
- var collapsed = xmlBlock.getAttribute('collapsed');
- if (collapsed) {
- block.setCollapsed(collapsed == 'true');
- }
- if (xmlBlock.nodeName.toLowerCase() == 'shadow') {
- // Ensure all children are also shadows.
- var children = block.getChildren();
- for (var i = 0, child; child = children[i]; i++) {
- goog.asserts.assert(child.isShadow(),
- 'Shadow block not allowed non-shadow child.');
- }
- block.setShadow(true);
- }
- return block;
-};
-
-/**
- * Remove any 'next' block (statements in a stack).
- * @param {!Element} xmlBlock XML block element.
- */
-Blockly.Xml.deleteNext = function(xmlBlock) {
- for (var i = 0, child; child = xmlBlock.childNodes[i]; i++) {
- if (child.nodeName.toLowerCase() == 'next') {
- xmlBlock.removeChild(child);
- break;
- }
- }
-};
-
-// Export symbols that would otherwise be renamed by Closure compiler.
-if (!goog.global['Blockly']) {
- goog.global['Blockly'] = {};
-}
-if (!goog.global['Blockly']['Xml']) {
- goog.global['Blockly']['Xml'] = {};
-}
-goog.global['Blockly']['Xml']['domToText'] = Blockly.Xml.domToText;
-goog.global['Blockly']['Xml']['domToWorkspace'] = Blockly.Xml.domToWorkspace;
-goog.global['Blockly']['Xml']['textToDom'] = Blockly.Xml.textToDom;
-goog.global['Blockly']['Xml']['workspaceToDom'] = Blockly.Xml.workspaceToDom;