mirror of
https://github.com/google/blockly.git
synced 2025-12-16 06:10:12 +01:00
* chore: add configuration for api extractor * fix: remove extra param names * chore: private to internal * remove unrestricted * chore: remove double backticks * chore: remove fileoverview and export * as * chore: return to returns * chore: fix backslashes and angle brackets in tsdoc * chore: final to sealed * chore: ignore to internal * chore: fix link tags * chore: add api-extractor configuration * chore: add unrecognized tag names * chore: remove tsdoc-metadata * fix: correct index.d.ts * chore: fix connection link
309 lines
8.7 KiB
TypeScript
309 lines
8.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2021 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* Common functions used both internally and externally, but which
|
|
* must not be at the top level to avoid circular dependencies.
|
|
*
|
|
* @namespace Blockly.common
|
|
*/
|
|
import * as goog from '../closure/goog/goog.js';
|
|
goog.declareModuleId('Blockly.common');
|
|
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
import type {Block} from './block.js';
|
|
import {BlockDefinition, Blocks} from './blocks.js';
|
|
import type {Connection} from './connection.js';
|
|
import type {ICopyable} from './interfaces/i_copyable.js';
|
|
import type {Workspace} from './workspace.js';
|
|
import type {WorkspaceSvg} from './workspace_svg.js';
|
|
|
|
|
|
/** Database of all workspaces. */
|
|
const WorkspaceDB_ = Object.create(null);
|
|
|
|
|
|
/**
|
|
* Find the workspace with the specified ID.
|
|
*
|
|
* @param id ID of workspace to find.
|
|
* @returns The sought after workspace or null if not found.
|
|
*/
|
|
export function getWorkspaceById(id: string): Workspace|null {
|
|
return WorkspaceDB_[id] || null;
|
|
}
|
|
|
|
/**
|
|
* Find all workspaces.
|
|
*
|
|
* @returns Array of workspaces.
|
|
*/
|
|
export function getAllWorkspaces(): Workspace[] {
|
|
const workspaces = [];
|
|
for (const workspaceId in WorkspaceDB_) {
|
|
workspaces.push(WorkspaceDB_[workspaceId]);
|
|
}
|
|
return workspaces;
|
|
}
|
|
|
|
/**
|
|
* Register a workspace in the workspace db.
|
|
*
|
|
* @param workspace
|
|
*/
|
|
export function registerWorkspace(workspace: Workspace) {
|
|
WorkspaceDB_[workspace.id] = workspace;
|
|
}
|
|
|
|
/**
|
|
* Unregister a workspace from the workspace db.
|
|
*
|
|
* @param workspace
|
|
*/
|
|
export function unregisterWorkpace(workspace: Workspace) {
|
|
delete WorkspaceDB_[workspace.id];
|
|
}
|
|
|
|
/**
|
|
* The main workspace most recently used.
|
|
* Set by Blockly.WorkspaceSvg.prototype.markFocused
|
|
*/
|
|
let mainWorkspace: Workspace;
|
|
|
|
/**
|
|
* Returns the last used top level workspace (based on focus). Try not to use
|
|
* this function, particularly if there are multiple Blockly instances on a
|
|
* page.
|
|
*
|
|
* @returns The main workspace.
|
|
* @alias Blockly.common.getMainWorkspace
|
|
*/
|
|
export function getMainWorkspace(): Workspace {
|
|
return mainWorkspace;
|
|
}
|
|
|
|
/**
|
|
* Sets last used main workspace.
|
|
*
|
|
* @param workspace The most recently used top level workspace.
|
|
* @alias Blockly.common.setMainWorkspace
|
|
*/
|
|
export function setMainWorkspace(workspace: Workspace) {
|
|
mainWorkspace = workspace;
|
|
}
|
|
|
|
/**
|
|
* Currently selected copyable object.
|
|
*/
|
|
let selected: ICopyable|null = null;
|
|
|
|
/**
|
|
* Returns the currently selected copyable object.
|
|
*
|
|
* @alias Blockly.common.getSelected
|
|
*/
|
|
export function getSelected(): ICopyable|null {
|
|
return selected;
|
|
}
|
|
|
|
/**
|
|
* Sets the currently selected block. This function does not visually mark the
|
|
* block as selected or fire the required events. If you wish to
|
|
* programmatically select a block, use `BlockSvg#select`.
|
|
*
|
|
* @param newSelection The newly selected block.
|
|
* @alias Blockly.common.setSelected
|
|
* @internal
|
|
*/
|
|
export function setSelected(newSelection: ICopyable|null) {
|
|
selected = newSelection;
|
|
}
|
|
|
|
/**
|
|
* Container element in which to render the WidgetDiv, DropDownDiv and Tooltip.
|
|
*/
|
|
let parentContainer: Element|null;
|
|
|
|
/**
|
|
* Get the container element in which to render the WidgetDiv, DropDownDiv and
|
|
* Tooltip.
|
|
*
|
|
* @returns The parent container.
|
|
* @alias Blockly.common.getParentContainer
|
|
*/
|
|
export function getParentContainer(): Element|null {
|
|
return parentContainer;
|
|
}
|
|
|
|
/**
|
|
* Set the parent container. This is the container element that the WidgetDiv,
|
|
* DropDownDiv, and Tooltip are rendered into the first time `Blockly.inject`
|
|
* is called.
|
|
* This method is a NOP if called after the first `Blockly.inject`.
|
|
*
|
|
* @param newParent The container element.
|
|
* @alias Blockly.common.setParentContainer
|
|
*/
|
|
export function setParentContainer(newParent: Element) {
|
|
parentContainer = newParent;
|
|
}
|
|
|
|
/**
|
|
* 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 workspace.resizeContents 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 workspace Any workspace in the SVG.
|
|
* @alias Blockly.common.svgResize
|
|
*/
|
|
export function svgResize(workspace: WorkspaceSvg) {
|
|
let mainWorkspace = workspace;
|
|
while (mainWorkspace.options.parentWorkspace) {
|
|
mainWorkspace = mainWorkspace.options.parentWorkspace;
|
|
}
|
|
const svg = mainWorkspace.getParentSvg();
|
|
const cachedSize = mainWorkspace.getCachedParentSvgSize();
|
|
const div = svg.parentElement;
|
|
if (!(div instanceof HTMLElement)) {
|
|
// Workspace deleted, or something.
|
|
return;
|
|
}
|
|
|
|
const width = div.offsetWidth;
|
|
const height = div.offsetHeight;
|
|
if (cachedSize.width !== width) {
|
|
svg.setAttribute('width', width + 'px');
|
|
mainWorkspace.setCachedParentSvgSize(width, null);
|
|
}
|
|
if (cachedSize.height !== height) {
|
|
svg.setAttribute('height', height + 'px');
|
|
mainWorkspace.setCachedParentSvgSize(null, height);
|
|
}
|
|
mainWorkspace.resize();
|
|
}
|
|
|
|
/**
|
|
* All of the connections on blocks that are currently being dragged.
|
|
*/
|
|
export const draggingConnections: Connection[] = [];
|
|
|
|
/**
|
|
* Get a map of all the block's descendants mapping their type to the number of
|
|
* children with that type.
|
|
*
|
|
* @param block The block to map.
|
|
* @param opt_stripFollowing Optionally ignore all following
|
|
* statements (blocks that are not inside a value or statement input
|
|
* of the block).
|
|
* @returns Map of types to type counts for descendants of the bock.
|
|
* @alias Blockly.common.getBlockTypeCounts
|
|
*/
|
|
export function getBlockTypeCounts(
|
|
block: Block, opt_stripFollowing?: boolean): {[key: string]: number} {
|
|
const typeCountsMap = Object.create(null);
|
|
const descendants = block.getDescendants(true);
|
|
if (opt_stripFollowing) {
|
|
const nextBlock = block.getNextBlock();
|
|
if (nextBlock) {
|
|
const index = descendants.indexOf(nextBlock);
|
|
descendants.splice(index, descendants.length - index);
|
|
}
|
|
}
|
|
for (let i = 0, checkBlock; checkBlock = descendants[i]; i++) {
|
|
if (typeCountsMap[checkBlock.type]) {
|
|
typeCountsMap[checkBlock.type]++;
|
|
} else {
|
|
typeCountsMap[checkBlock.type] = 1;
|
|
}
|
|
}
|
|
return typeCountsMap;
|
|
}
|
|
|
|
/**
|
|
* Helper function for defining a block from JSON. The resulting function has
|
|
* the correct value of jsonDef at the point in code where jsonInit is called.
|
|
*
|
|
* @param jsonDef The JSON definition of a block.
|
|
* @returns A function that calls jsonInit with the correct value
|
|
* of jsonDef.
|
|
*/
|
|
function jsonInitFactory(jsonDef: AnyDuringMigration): () => void {
|
|
return function(this: Block) {
|
|
this.jsonInit(jsonDef);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Define blocks from an array of JSON block definitions, as might be generated
|
|
* by the Blockly Developer Tools.
|
|
*
|
|
* @param jsonArray An array of JSON block definitions.
|
|
* @alias Blockly.common.defineBlocksWithJsonArray
|
|
*/
|
|
export function defineBlocksWithJsonArray(jsonArray: AnyDuringMigration[]) {
|
|
TEST_ONLY.defineBlocksWithJsonArrayInternal(jsonArray);
|
|
}
|
|
|
|
/**
|
|
* Private version of defineBlocksWithJsonArray for stubbing in tests.
|
|
*/
|
|
function defineBlocksWithJsonArrayInternal(jsonArray: AnyDuringMigration[]) {
|
|
defineBlocks(createBlockDefinitionsFromJsonArray(jsonArray));
|
|
}
|
|
|
|
/**
|
|
* Define blocks from an array of JSON block definitions, as might be generated
|
|
* by the Blockly Developer Tools.
|
|
*
|
|
* @param jsonArray An array of JSON block definitions.
|
|
* @returns A map of the block
|
|
* definitions created.
|
|
* @alias Blockly.common.defineBlocksWithJsonArray
|
|
*/
|
|
export function createBlockDefinitionsFromJsonArray(
|
|
jsonArray: AnyDuringMigration[]): {[key: string]: BlockDefinition} {
|
|
const blocks: {[key: string]: BlockDefinition} = {};
|
|
for (let i = 0; i < jsonArray.length; i++) {
|
|
const elem = jsonArray[i];
|
|
if (!elem) {
|
|
console.warn(`Block definition #${i} in JSON array is ${elem}. Skipping`);
|
|
continue;
|
|
}
|
|
const type = elem['type'];
|
|
if (!type) {
|
|
console.warn(
|
|
`Block definition #${i} in JSON array is missing a type attribute. ` +
|
|
'Skipping.');
|
|
continue;
|
|
}
|
|
blocks[type] = {init: jsonInitFactory(elem)};
|
|
}
|
|
return blocks;
|
|
}
|
|
|
|
/**
|
|
* Add the specified block definitions to the block definitions
|
|
* dictionary (Blockly.Blocks).
|
|
*
|
|
* @param blocks A map of block
|
|
* type names to block definitions.
|
|
* @alias Blockly.common.defineBlocks
|
|
*/
|
|
export function defineBlocks(blocks: {[key: string]: BlockDefinition}) {
|
|
// Iterate over own enumerable properties.
|
|
for (const type of Object.keys(blocks)) {
|
|
const definition = blocks[type];
|
|
if (type in Blocks) {
|
|
console.warn(`Block definiton "${type}" overwrites previous definition.`);
|
|
}
|
|
Blocks[type] = definition;
|
|
}
|
|
}
|
|
|
|
export const TEST_ONLY = {defineBlocksWithJsonArrayInternal};
|