/**
* @license
* Copyright 2011 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Functions for injecting Blockly into a web page.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.inject');
goog.require('Blockly.BlockDragSurfaceSvg');
goog.require('Blockly.Component');
goog.require('Blockly.Css');
goog.require('Blockly.DropDownDiv');
goog.require('Blockly.Events');
goog.require('Blockly.Grid');
goog.require('Blockly.Msg');
goog.require('Blockly.Options');
goog.require('Blockly.ScrollbarPair');
goog.require('Blockly.Tooltip');
goog.require('Blockly.user.keyMap');
goog.require('Blockly.utils');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.userAgent');
goog.require('Blockly.WorkspaceDragSurfaceSvg');
goog.require('Blockly.WorkspaceSvg');
goog.requireType('Blockly.utils.Metrics');
/**
* 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 {Blockly.BlocklyOptions=} opt_options Optional dictionary of options.
* @return {!Blockly.WorkspaceSvg} Newly created main workspace.
*/
Blockly.inject = function(container, opt_options) {
Blockly.checkBlockColourConstants();
if (typeof container == 'string') {
container = document.getElementById(container) ||
document.querySelector(container);
}
// Verify that the container is in document.
if (!container || !Blockly.utils.dom.containsNode(document, container)) {
throw Error('Error: container is not in current document.');
}
var options = new Blockly.Options(opt_options ||
(/** @type {!Blockly.BlocklyOptions} */ ({})));
var subContainer = document.createElement('div');
subContainer.className = 'injectionDiv';
subContainer.tabIndex = 0;
Blockly.utils.aria.setState(subContainer,
Blockly.utils.aria.State.LABEL, Blockly.Msg['WORKSPACE_ARIA_LABEL']);
container.appendChild(subContainer);
var svg = Blockly.createDom_(subContainer, options);
// Create surfaces for dragging things. These are optimizations
// so that the browser does not repaint during the drag.
var blockDragSurface = new Blockly.BlockDragSurfaceSvg(subContainer);
var workspaceDragSurface = new Blockly.WorkspaceDragSurfaceSvg(subContainer);
var workspace = Blockly.createMainWorkspace_(svg, options, blockDragSurface,
workspaceDragSurface);
Blockly.user.keyMap.setKeyMap(options.keyMap);
Blockly.init_(workspace);
// Keep focus on the first workspace so entering keyboard navigation looks correct.
Blockly.mainWorkspace = workspace;
Blockly.svgResize(workspace);
subContainer.addEventListener('focusin', function() {
Blockly.mainWorkspace = 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');
// Set the default direction for Components to use.
Blockly.Component.defaultRightToLeft = options.RTL;
// Load CSS.
Blockly.Css.inject(options.hasCss, options.pathToMedia);
// Build the SVG DOM.
/*
*/
var svg = Blockly.utils.dom.createSvgElement(
Blockly.utils.dom.SvgElementType.SVG, {
'xmlns': Blockly.utils.dom.SVG_NS,
'xmlns:html': Blockly.utils.dom.HTML_NS,
'xmlns:xlink': Blockly.utils.dom.XLINK_NS,
'version': '1.1',
'class': 'blocklySvg',
'tabindex': '0'
}, container);
/*
... filters go here ...
*/
var defs = Blockly.utils.dom.createSvgElement(
Blockly.utils.dom.SvgElementType.DEFS, {}, svg);
// Each filter/pattern needs a unique ID for the case of multiple Blockly
// instances on a page. Browser behaviour becomes undefined otherwise.
// https://neil.fraser.name/news/2015/11/01/
var rnd = String(Math.random()).substring(2);
options.gridPattern = Blockly.Grid.createDom(rnd, options.gridOptions, defs);
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.
* @param {!Blockly.BlockDragSurfaceSvg} blockDragSurface Drag surface SVG
* for the blocks.
* @param {!Blockly.WorkspaceDragSurfaceSvg} workspaceDragSurface Drag surface
* SVG for the workspace.
* @return {!Blockly.WorkspaceSvg} Newly created main workspace.
* @private
*/
Blockly.createMainWorkspace_ = function(svg, options, blockDragSurface,
workspaceDragSurface) {
options.parentWorkspace = null;
var mainWorkspace =
new Blockly.WorkspaceSvg(options, blockDragSurface, workspaceDragSurface);
var wsOptions = mainWorkspace.options;
mainWorkspace.scale = wsOptions.zoomOptions.startScale;
svg.appendChild(mainWorkspace.createDom('blocklyMainBackground'));
// Set the theme name and renderer name onto the injection div.
Blockly.utils.dom.addClass(mainWorkspace.getInjectionDiv(),
mainWorkspace.getRenderer().getClassName());
Blockly.utils.dom.addClass(mainWorkspace.getInjectionDiv(),
mainWorkspace.getTheme().getClassName());
if (!wsOptions.hasCategories && wsOptions.languageTree) {
// Add flyout as an