2013-10-30 14:46:03 -07:00
|
|
|
/**
|
2014-01-28 03:00:09 -08:00
|
|
|
* @license
|
2013-10-30 14:46:03 -07:00
|
|
|
* Visual Blocks Editor
|
|
|
|
*
|
|
|
|
* Copyright 2011 Google Inc.
|
2014-10-07 13:09:55 -07:00
|
|
|
* https://developers.google.com/blockly/
|
2013-10-30 14:46:03 -07:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @fileoverview Functions for injecting Blockly into a web page.
|
|
|
|
* @author fraser@google.com (Neil Fraser)
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
goog.provide('Blockly.inject');
|
|
|
|
|
|
|
|
goog.require('Blockly.Css');
|
2014-12-23 11:22:02 -08:00
|
|
|
goog.require('Blockly.WorkspaceSvg');
|
2013-10-30 14:46:03 -07:00
|
|
|
goog.require('goog.dom');
|
2015-02-06 15:27:25 -08:00
|
|
|
goog.require('goog.ui.Component');
|
|
|
|
goog.require('goog.userAgent');
|
2013-10-30 14:46:03 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize the SVG document with various handlers.
|
|
|
|
* @param {!Element} container Containing element.
|
|
|
|
* @param {Object} opt_options Optional dictionary of options.
|
|
|
|
*/
|
|
|
|
Blockly.inject = function(container, opt_options) {
|
|
|
|
// Verify that the container is in document.
|
|
|
|
if (!goog.dom.contains(document, container)) {
|
|
|
|
throw 'Error: container is not in current document.';
|
|
|
|
}
|
|
|
|
if (opt_options) {
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.parseOptions_(opt_options);
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
2014-02-08 03:00:02 -08:00
|
|
|
var startUi = function() {
|
|
|
|
Blockly.createDom_(container);
|
|
|
|
Blockly.init_();
|
|
|
|
};
|
|
|
|
if (Blockly.enableRealtime) {
|
|
|
|
var realtimeElement = document.getElementById('realtime');
|
|
|
|
if (realtimeElement) {
|
|
|
|
realtimeElement.style.display = 'block';
|
|
|
|
}
|
|
|
|
Blockly.Realtime.startRealtime(startUi, container, Blockly.realtimeOptions);
|
|
|
|
} else {
|
|
|
|
startUi();
|
|
|
|
}
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
2014-09-08 14:26:52 -07:00
|
|
|
/**
|
|
|
|
* Parse the provided toolbox tree into a consistent DOM format.
|
|
|
|
* @param {Node|string} tree DOM tree of blocks, or text representation of same.
|
|
|
|
* @return {Node} DOM tree of blocks or null.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.parseToolboxTree_ = function(tree) {
|
|
|
|
if (tree) {
|
|
|
|
if (typeof tree != 'string' && typeof XSLTProcessor == 'undefined') {
|
|
|
|
// In this case the tree will not have been properly built by the
|
|
|
|
// browser. The HTML will be contained in the element, but it will
|
|
|
|
// not have the proper DOM structure since the browser doesn't support
|
|
|
|
// XSLTProcessor (XML -> HTML). This is the case in IE 9+.
|
|
|
|
tree = tree.outerHTML;
|
|
|
|
}
|
|
|
|
if (typeof tree == 'string') {
|
|
|
|
tree = Blockly.Xml.textToDom(tree);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tree = null;
|
|
|
|
}
|
|
|
|
return tree;
|
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Configure Blockly to behave according to a set of options.
|
|
|
|
* @param {!Object} options Dictionary of options.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.parseOptions_ = function(options) {
|
|
|
|
var readOnly = !!options['readOnly'];
|
|
|
|
if (readOnly) {
|
|
|
|
var hasCategories = false;
|
|
|
|
var hasTrashcan = false;
|
|
|
|
var hasCollapse = false;
|
2014-09-08 14:26:52 -07:00
|
|
|
var hasComments = false;
|
|
|
|
var hasDisable = false;
|
2013-10-30 14:46:03 -07:00
|
|
|
var tree = null;
|
|
|
|
} else {
|
2014-09-08 14:26:52 -07:00
|
|
|
var tree = Blockly.parseToolboxTree_(options['toolbox']);
|
|
|
|
var hasCategories = Boolean(tree &&
|
|
|
|
tree.getElementsByTagName('category').length);
|
2013-10-30 14:46:03 -07:00
|
|
|
var hasTrashcan = options['trashcan'];
|
|
|
|
if (hasTrashcan === undefined) {
|
|
|
|
hasTrashcan = hasCategories;
|
|
|
|
}
|
|
|
|
var hasCollapse = options['collapse'];
|
|
|
|
if (hasCollapse === undefined) {
|
|
|
|
hasCollapse = hasCategories;
|
|
|
|
}
|
2014-09-08 14:26:52 -07:00
|
|
|
var hasComments = options['comments'];
|
|
|
|
if (hasComments === undefined) {
|
|
|
|
hasComments = hasCategories;
|
|
|
|
}
|
|
|
|
var hasDisable = options['disable'];
|
|
|
|
if (hasDisable === undefined) {
|
|
|
|
hasDisable = hasCategories;
|
|
|
|
}
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
2014-12-03 17:46:05 -08:00
|
|
|
var hasScrollbars = options['scrollbars'];
|
|
|
|
if (hasScrollbars === undefined) {
|
|
|
|
hasScrollbars = hasCategories;
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
2014-09-08 14:26:52 -07:00
|
|
|
var hasSounds = options['sounds'];
|
|
|
|
if (hasSounds === undefined) {
|
|
|
|
hasSounds = true;
|
|
|
|
}
|
2015-01-22 13:31:55 -08:00
|
|
|
var hasCss = options['css'];
|
|
|
|
if (hasCss === undefined) {
|
|
|
|
hasCss = true;
|
|
|
|
}
|
2014-02-08 03:00:02 -08:00
|
|
|
var enableRealtime = !!options['realtime'];
|
|
|
|
var realtimeOptions = enableRealtime ? options['realtimeOptions'] : undefined;
|
2014-09-08 14:26:52 -07:00
|
|
|
|
|
|
|
Blockly.RTL = !!options['rtl'];
|
|
|
|
Blockly.collapse = hasCollapse;
|
|
|
|
Blockly.comments = hasComments;
|
|
|
|
Blockly.disable = hasDisable;
|
|
|
|
Blockly.readOnly = readOnly;
|
|
|
|
Blockly.maxBlocks = options['maxBlocks'] || Infinity;
|
2014-11-09 16:02:24 -08:00
|
|
|
if (options['media']) {
|
|
|
|
Blockly.pathToMedia = options['media'];
|
|
|
|
} else if (options['path']) {
|
|
|
|
// 'path' is a deprecated option which has been replaced by 'media'.
|
|
|
|
Blockly.pathToMedia = options['path'] + 'media/';
|
|
|
|
}
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.hasCategories = hasCategories;
|
|
|
|
Blockly.hasScrollbars = hasScrollbars;
|
|
|
|
Blockly.hasTrashcan = hasTrashcan;
|
|
|
|
Blockly.hasSounds = hasSounds;
|
2015-01-22 13:31:55 -08:00
|
|
|
Blockly.hasCss = hasCss;
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.languageTree = tree;
|
|
|
|
Blockly.enableRealtime = enableRealtime;
|
|
|
|
Blockly.realtimeOptions = realtimeOptions;
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create the SVG image.
|
|
|
|
* @param {!Element} container Containing element.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.createDom_ = function(container) {
|
|
|
|
// 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(Blockly.RTL);
|
|
|
|
|
|
|
|
// Load CSS.
|
2015-01-22 13:54:00 -08:00
|
|
|
Blockly.Css.inject();
|
2013-10-30 14:46:03 -07:00
|
|
|
|
|
|
|
// Build the SVG DOM.
|
|
|
|
/*
|
|
|
|
<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">
|
|
|
|
...
|
|
|
|
</svg>
|
|
|
|
*/
|
|
|
|
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'
|
|
|
|
}, null);
|
|
|
|
/*
|
|
|
|
<defs>
|
|
|
|
... filters go here ...
|
|
|
|
</defs>
|
|
|
|
*/
|
|
|
|
var defs = Blockly.createSvgElement('defs', {}, svg);
|
|
|
|
var filter, feSpecularLighting, feMerge, pattern;
|
|
|
|
/*
|
|
|
|
<filter id="blocklyEmboss">
|
|
|
|
<feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"/>
|
|
|
|
<feSpecularLighting in="blur" surfaceScale="1" specularConstant="0.5"
|
|
|
|
specularExponent="10" lighting-color="white"
|
|
|
|
result="specOut">
|
|
|
|
<fePointLight x="-5000" y="-10000" z="20000"/>
|
|
|
|
</feSpecularLighting>
|
|
|
|
<feComposite in="specOut" in2="SourceAlpha" operator="in"
|
|
|
|
result="specOut"/>
|
|
|
|
<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic"
|
|
|
|
k1="0" k2="1" k3="1" k4="0"/>
|
|
|
|
</filter>
|
|
|
|
*/
|
|
|
|
filter = Blockly.createSvgElement('filter', {'id': 'blocklyEmboss'}, defs);
|
|
|
|
Blockly.createSvgElement('feGaussianBlur',
|
|
|
|
{'in': 'SourceAlpha', 'stdDeviation': 1, 'result': 'blur'}, filter);
|
|
|
|
feSpecularLighting = Blockly.createSvgElement('feSpecularLighting',
|
|
|
|
{'in': 'blur', 'surfaceScale': 1, 'specularConstant': 0.5,
|
|
|
|
'specularExponent': 10, 'lighting-color': 'white', 'result': 'specOut'},
|
|
|
|
filter);
|
|
|
|
Blockly.createSvgElement('fePointLight',
|
|
|
|
{'x': -5000, 'y': -10000, 'z': 20000}, feSpecularLighting);
|
|
|
|
Blockly.createSvgElement('feComposite',
|
|
|
|
{'in': 'specOut', 'in2': 'SourceAlpha', 'operator': 'in',
|
|
|
|
'result': 'specOut'}, filter);
|
|
|
|
Blockly.createSvgElement('feComposite',
|
|
|
|
{'in': 'SourceGraphic', 'in2': 'specOut', 'operator': 'arithmetic',
|
|
|
|
'k1': 0, 'k2': 1, 'k3': 1, 'k4': 0}, filter);
|
|
|
|
/*
|
|
|
|
<filter id="blocklyTrashcanShadowFilter">
|
|
|
|
<feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
|
|
|
|
<feOffset in="blur" dx="1" dy="1" result="offsetBlur"/>
|
|
|
|
<feMerge>
|
|
|
|
<feMergeNode in="offsetBlur"/>
|
|
|
|
<feMergeNode in="SourceGraphic"/>
|
|
|
|
</feMerge>
|
|
|
|
</filter>
|
|
|
|
*/
|
|
|
|
filter = Blockly.createSvgElement('filter',
|
|
|
|
{'id': 'blocklyTrashcanShadowFilter'}, defs);
|
|
|
|
Blockly.createSvgElement('feGaussianBlur',
|
|
|
|
{'in': 'SourceAlpha', 'stdDeviation': 2, 'result': 'blur'}, filter);
|
|
|
|
Blockly.createSvgElement('feOffset',
|
|
|
|
{'in': 'blur', 'dx': 1, 'dy': 1, 'result': 'offsetBlur'}, filter);
|
|
|
|
feMerge = Blockly.createSvgElement('feMerge', {}, filter);
|
|
|
|
Blockly.createSvgElement('feMergeNode', {'in': 'offsetBlur'}, feMerge);
|
|
|
|
Blockly.createSvgElement('feMergeNode', {'in': 'SourceGraphic'}, feMerge);
|
|
|
|
/*
|
|
|
|
<filter id="blocklyShadowFilter">
|
|
|
|
<feGaussianBlur stdDeviation="2"/>
|
|
|
|
</filter>
|
|
|
|
*/
|
|
|
|
filter = Blockly.createSvgElement('filter',
|
|
|
|
{'id': 'blocklyShadowFilter'}, defs);
|
|
|
|
Blockly.createSvgElement('feGaussianBlur', {'stdDeviation': 2}, filter);
|
|
|
|
/*
|
|
|
|
<pattern id="blocklyDisabledPattern" patternUnits="userSpaceOnUse"
|
|
|
|
width="10" height="10">
|
|
|
|
<rect width="10" height="10" fill="#aaa" />
|
|
|
|
<path d="M 0 0 L 10 10 M 10 0 L 0 10" stroke="#cc0" />
|
|
|
|
</pattern>
|
|
|
|
*/
|
|
|
|
pattern = Blockly.createSvgElement('pattern',
|
|
|
|
{'id': 'blocklyDisabledPattern', 'patternUnits': 'userSpaceOnUse',
|
|
|
|
'width': 10, 'height': 10}, defs);
|
|
|
|
Blockly.createSvgElement('rect',
|
|
|
|
{'width': 10, 'height': 10, 'fill': '#aaa'}, pattern);
|
|
|
|
Blockly.createSvgElement('path',
|
|
|
|
{'d': 'M 0 0 L 10 10 M 10 0 L 0 10', 'stroke': '#cc0'}, pattern);
|
2015-03-06 15:27:41 -06:00
|
|
|
|
2014-12-23 11:22:02 -08:00
|
|
|
Blockly.mainWorkspace = new Blockly.WorkspaceSvg(
|
2013-10-30 14:46:03 -07:00
|
|
|
Blockly.getMainWorkspaceMetrics_,
|
|
|
|
Blockly.setMainWorkspaceMetrics_);
|
|
|
|
svg.appendChild(Blockly.mainWorkspace.createDom());
|
|
|
|
Blockly.mainWorkspace.maxBlocks = Blockly.maxBlocks;
|
|
|
|
|
|
|
|
if (!Blockly.readOnly) {
|
|
|
|
// Determine if there needs to be a category tree, or a simple list of
|
|
|
|
// blocks. This cannot be changed later, since the UI is very different.
|
|
|
|
if (Blockly.hasCategories) {
|
2014-11-29 15:41:27 -08:00
|
|
|
Blockly.mainWorkspace.toolbox_ = new Blockly.Toolbox(svg, container);
|
2015-01-12 17:31:03 -08:00
|
|
|
} else if (Blockly.languageTree) {
|
2015-03-06 15:27:41 -06:00
|
|
|
Blockly.mainWorkspace.addFlyout();
|
2014-12-03 17:46:05 -08:00
|
|
|
}
|
|
|
|
if (!Blockly.hasScrollbars) {
|
2013-10-30 14:46:03 -07:00
|
|
|
var workspaceChanged = function() {
|
2014-12-23 11:22:02 -08:00
|
|
|
if (Blockly.dragMode_ == 0) {
|
2013-10-30 14:46:03 -07:00
|
|
|
var metrics = Blockly.mainWorkspace.getMetrics();
|
2014-12-03 17:46:05 -08:00
|
|
|
var edgeLeft = metrics.viewLeft + metrics.absoluteLeft;
|
|
|
|
var edgeTop = metrics.viewTop + metrics.absoluteTop;
|
|
|
|
if (metrics.contentTop < edgeTop ||
|
2013-10-30 14:46:03 -07:00
|
|
|
metrics.contentTop + metrics.contentHeight >
|
2014-12-03 17:46:05 -08:00
|
|
|
metrics.viewHeight + edgeTop ||
|
|
|
|
metrics.contentLeft <
|
|
|
|
(Blockly.RTL ? metrics.viewLeft : edgeLeft) ||
|
2014-09-08 14:26:52 -07:00
|
|
|
metrics.contentLeft + metrics.contentWidth > (Blockly.RTL ?
|
2014-12-03 17:46:05 -08:00
|
|
|
metrics.viewWidth : metrics.viewWidth + edgeLeft)) {
|
|
|
|
// One or more blocks may be out of bounds. Bump them back in.
|
2013-10-30 14:46:03 -07:00
|
|
|
var MARGIN = 25;
|
|
|
|
var blocks = Blockly.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.
|
2014-12-03 17:46:05 -08:00
|
|
|
var overflow = edgeTop + MARGIN - blockHW.height - blockXY.y;
|
2013-10-30 14:46:03 -07:00
|
|
|
if (overflow > 0) {
|
|
|
|
block.moveBy(0, overflow);
|
|
|
|
}
|
|
|
|
// Bump any block that's below the bottom back inside.
|
2014-12-03 17:46:05 -08:00
|
|
|
var overflow = edgeTop + metrics.viewHeight - MARGIN - blockXY.y;
|
2013-10-30 14:46:03 -07:00
|
|
|
if (overflow < 0) {
|
|
|
|
block.moveBy(0, overflow);
|
|
|
|
}
|
|
|
|
// Bump any block that's off the left back inside.
|
2014-12-03 17:46:05 -08:00
|
|
|
var overflow = MARGIN + edgeLeft -
|
|
|
|
blockXY.x - (Blockly.RTL ? 0 : blockHW.width);
|
2013-10-30 14:46:03 -07:00
|
|
|
if (overflow > 0) {
|
|
|
|
block.moveBy(overflow, 0);
|
|
|
|
}
|
|
|
|
// Bump any block that's off the right back inside.
|
2014-12-03 17:46:05 -08:00
|
|
|
var overflow = edgeLeft + metrics.viewWidth - MARGIN -
|
2013-10-30 14:46:03 -07:00
|
|
|
blockXY.x + (Blockly.RTL ? blockHW.width : 0);
|
|
|
|
if (overflow < 0) {
|
|
|
|
block.moveBy(overflow, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Blockly.addChangeListener(workspaceChanged);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-08 14:26:52 -07:00
|
|
|
svg.appendChild(Blockly.Tooltip.createDom());
|
2013-10-30 14:46:03 -07:00
|
|
|
|
|
|
|
// The SVG is now fully assembled. Add it to the container.
|
|
|
|
container.appendChild(svg);
|
|
|
|
Blockly.svg = svg;
|
|
|
|
Blockly.svgResize();
|
|
|
|
|
|
|
|
// Create an HTML container for popup overlays (e.g. editor widgets).
|
|
|
|
Blockly.WidgetDiv.DIV = goog.dom.createDom('div', 'blocklyWidgetDiv');
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.WidgetDiv.DIV.style.direction = Blockly.RTL ? 'rtl' : 'ltr';
|
2013-10-30 14:46:03 -07:00
|
|
|
document.body.appendChild(Blockly.WidgetDiv.DIV);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize Blockly with various handlers.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.init_ = function() {
|
|
|
|
// 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.
|
|
|
|
Blockly.bindEvent_(Blockly.svg, 'mousedown', null, Blockly.onMouseDown_);
|
|
|
|
Blockly.bindEvent_(Blockly.svg, 'contextmenu', null, Blockly.onContextMenu_);
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.bindEvent_(Blockly.WidgetDiv.DIV, 'contextmenu', null,
|
|
|
|
Blockly.onContextMenu_);
|
2013-10-30 14:46:03 -07:00
|
|
|
|
|
|
|
if (!Blockly.documentEventsBound_) {
|
|
|
|
// Only bind the window/document events once.
|
|
|
|
// Destroying and reinjecting Blockly should not bind again.
|
|
|
|
Blockly.bindEvent_(window, 'resize', document, Blockly.svgResize);
|
|
|
|
Blockly.bindEvent_(document, 'keydown', null, Blockly.onKeyDown_);
|
2014-09-08 14:26:52 -07:00
|
|
|
// 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);
|
2013-10-30 14:46:03 -07:00
|
|
|
// Some iPad versions don't fire resize after portrait to landscape change.
|
|
|
|
if (goog.userAgent.IPAD) {
|
|
|
|
Blockly.bindEvent_(window, 'orientationchange', document, function() {
|
|
|
|
Blockly.fireUiEvent(window, 'resize');
|
2014-09-08 14:26:52 -07:00
|
|
|
});
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
|
|
|
Blockly.documentEventsBound_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Blockly.languageTree) {
|
2014-11-29 15:41:27 -08:00
|
|
|
if (Blockly.mainWorkspace.toolbox_) {
|
2015-01-12 15:11:24 -08:00
|
|
|
Blockly.mainWorkspace.toolbox_.init(Blockly.mainWorkspace);
|
2014-11-29 15:41:27 -08:00
|
|
|
} else if (Blockly.mainWorkspace.flyout_) {
|
2013-10-30 14:46:03 -07:00
|
|
|
// Build a fixed flyout with the root blocks.
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.mainWorkspace.flyout_.init(Blockly.mainWorkspace);
|
2013-10-30 14:46:03 -07:00
|
|
|
Blockly.mainWorkspace.flyout_.show(Blockly.languageTree.childNodes);
|
|
|
|
// Translate the workspace sideways to avoid the fixed flyout.
|
|
|
|
Blockly.mainWorkspace.scrollX = Blockly.mainWorkspace.flyout_.width_;
|
2014-09-08 14:26:52 -07:00
|
|
|
if (Blockly.RTL) {
|
|
|
|
Blockly.mainWorkspace.scrollX *= -1;
|
|
|
|
}
|
2013-10-30 14:46:03 -07:00
|
|
|
var translation = 'translate(' + Blockly.mainWorkspace.scrollX + ', 0)';
|
|
|
|
Blockly.mainWorkspace.getCanvas().setAttribute('transform', translation);
|
|
|
|
Blockly.mainWorkspace.getBubbleCanvas().setAttribute('transform',
|
|
|
|
translation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Blockly.hasScrollbars) {
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.mainWorkspace.scrollbar =
|
|
|
|
new Blockly.ScrollbarPair(Blockly.mainWorkspace);
|
2013-10-30 14:46:03 -07:00
|
|
|
Blockly.mainWorkspace.scrollbar.resize();
|
|
|
|
}
|
|
|
|
|
|
|
|
Blockly.mainWorkspace.addTrashcan();
|
|
|
|
|
|
|
|
// Load the sounds.
|
2014-09-08 14:26:52 -07:00
|
|
|
if (Blockly.hasSounds) {
|
|
|
|
Blockly.loadAudio_(
|
2014-11-09 18:24:20 -08:00
|
|
|
[Blockly.pathToMedia + 'click.mp3',
|
|
|
|
Blockly.pathToMedia + 'click.wav',
|
|
|
|
Blockly.pathToMedia + 'click.ogg'], 'click');
|
2014-09-08 14:26:52 -07:00
|
|
|
Blockly.loadAudio_(
|
2014-11-09 18:24:20 -08:00
|
|
|
[Blockly.pathToMedia + 'delete.mp3',
|
|
|
|
Blockly.pathToMedia + 'delete.ogg',
|
|
|
|
Blockly.pathToMedia + 'delete.wav'], 'delete');
|
2014-09-08 14:26:52 -07:00
|
|
|
|
|
|
|
// Bind temporary hooks that preload the sounds.
|
|
|
|
var soundBinds = [];
|
|
|
|
var unbindSounds = function() {
|
|
|
|
while (soundBinds.length) {
|
|
|
|
Blockly.unbindEvent_(soundBinds.pop());
|
|
|
|
}
|
|
|
|
Blockly.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) {
|
|
|
|
tree = Blockly.parseToolboxTree_(tree);
|
|
|
|
if (!tree) {
|
|
|
|
if (Blockly.languageTree) {
|
|
|
|
throw 'Can\'t nullify an existing toolbox.';
|
|
|
|
}
|
|
|
|
// No change (null to null).
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!Blockly.languageTree) {
|
|
|
|
throw 'Existing toolbox is null. Can\'t create new toolbox.';
|
|
|
|
}
|
|
|
|
var hasCategories = !!tree.getElementsByTagName('category').length;
|
|
|
|
if (hasCategories) {
|
|
|
|
if (!Blockly.hasCategories) {
|
|
|
|
throw 'Existing toolbox has no categories. Can\'t change mode.';
|
|
|
|
}
|
|
|
|
Blockly.languageTree = tree;
|
2014-11-29 15:41:27 -08:00
|
|
|
Blockly.mainWorkspace.toolbox_.populate_();
|
2014-09-08 14:26:52 -07:00
|
|
|
} else {
|
|
|
|
if (Blockly.hasCategories) {
|
|
|
|
throw 'Existing toolbox has categories. Can\'t change mode.';
|
|
|
|
}
|
|
|
|
Blockly.languageTree = tree;
|
|
|
|
Blockly.mainWorkspace.flyout_.show(Blockly.languageTree.childNodes);
|
|
|
|
}
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|