/**
* @license
* Copyright 2011 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Functions for injecting Blockly into a web page.
*
* @namespace Blockly.inject
*/
import * as goog from '../closure/goog/goog.js';
goog.declareModuleId('Blockly.inject');
import type {BlocklyOptions} from './blockly_options.js';
import * as browserEvents from './browser_events.js';
import * as bumpObjects from './bump_objects.js';
import * as common from './common.js';
import * as Css from './css.js';
import * as dropDownDiv from './dropdowndiv.js';
import {Grid} from './grid.js';
import {Msg} from './msg.js';
import {Options} from './options.js';
import {ScrollbarPair} from './scrollbar_pair.js';
import {ShortcutRegistry} from './shortcut_registry.js';
import * as Tooltip from './tooltip.js';
import * as Touch from './touch.js';
import * as aria from './utils/aria.js';
import * as dom from './utils/dom.js';
import {Svg} from './utils/svg.js';
import * as userAgent from './utils/useragent.js';
import * as WidgetDiv from './widgetdiv.js';
import {WorkspaceSvg} from './workspace_svg.js';
/**
* Inject a Blockly editor into the specified container element (usually a div).
*
* @param container Containing element, or its ID, or a CSS selector.
* @param opt_options Optional dictionary of options.
* @returns Newly created main workspace.
*/
export function inject(
container: Element|string, opt_options?: BlocklyOptions): WorkspaceSvg {
if (typeof container === 'string') {
// AnyDuringMigration because: Type 'Element | null' is not assignable to
// type 'string | Element'.
container = (document.getElementById(container) ||
document.querySelector(container)) as AnyDuringMigration;
}
// Verify that the container is in document.
// AnyDuringMigration because: Argument of type 'string | Element' is not
// assignable to parameter of type 'Node'.
if (!container ||
!dom.containsNode(document, container as AnyDuringMigration)) {
throw Error('Error: container is not in current document.');
}
const options = new Options(opt_options || {} as BlocklyOptions);
const subContainer = (document.createElement('div'));
subContainer.className = 'injectionDiv';
subContainer.tabIndex = 0;
aria.setState(subContainer, aria.State.LABEL, Msg['WORKSPACE_ARIA_LABEL']);
// AnyDuringMigration because: Property 'appendChild' does not exist on type
// 'string | Element'.
(container as AnyDuringMigration).appendChild(subContainer);
const svg = createDom(subContainer, options);
const workspace = createMainWorkspace(svg, options);
init(workspace);
// Keep focus on the first workspace so entering keyboard navigation looks
// correct.
// AnyDuringMigration because: Argument of type 'WorkspaceSvg' is not
// assignable to parameter of type 'Workspace'.
common.setMainWorkspace(workspace as AnyDuringMigration);
common.svgResize(workspace);
subContainer.addEventListener('focusin', function() {
// AnyDuringMigration because: Argument of type 'WorkspaceSvg' is not
// assignable to parameter of type 'Workspace'.
common.setMainWorkspace(workspace as AnyDuringMigration);
});
return workspace;
}
/**
* Create the SVG image.
*
* @param container Containing element.
* @param options Dictionary of options.
* @returns Newly created SVG image.
*/
function createDom(container: Element, options: Options): Element {
// 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');
// Load CSS.
Css.inject(options.hasCss, options.pathToMedia);
// Build the SVG DOM.
/*
*/
const svg = dom.createSvgElement(
Svg.SVG, {
'xmlns': dom.SVG_NS,
'xmlns:html': dom.HTML_NS,
'xmlns:xlink': dom.XLINK_NS,
'version': '1.1',
'class': 'blocklySvg',
'tabindex': '0',
},
container);
/*
... filters go here ...
*/
const defs = dom.createSvgElement(Svg.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/
const rnd = String(Math.random()).substring(2);
options.gridPattern = Grid.createDom(rnd, options.gridOptions, defs);
return svg;
}
/**
* Create a main workspace and add it to the SVG.
*
* @param svg SVG element with pattern defined.
* @param options Dictionary of options.
* @returns Newly created main workspace.
*/
function createMainWorkspace(svg: Element, options: Options): WorkspaceSvg {
options.parentWorkspace = null;
const mainWorkspace = new WorkspaceSvg(options);
const wsOptions = mainWorkspace.options;
mainWorkspace.scale = wsOptions.zoomOptions.startScale;
svg.appendChild(mainWorkspace.createDom('blocklyMainBackground'));
// Set the theme name and renderer name onto the injection div.
const injectionDiv = mainWorkspace.getInjectionDiv();
const rendererClassName = mainWorkspace.getRenderer().getClassName();
if (rendererClassName) {
dom.addClass(injectionDiv, rendererClassName);
}
const themeClassName = mainWorkspace.getTheme().getClassName();
if (themeClassName) {
dom.addClass(injectionDiv, themeClassName);
}
if (!wsOptions.hasCategories && wsOptions.languageTree) {
// Add flyout as an