From 68a8a5ff194ae2cc23ceadb06c74a76d76fbdec4 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Mon, 31 Jul 2023 09:22:24 -0700 Subject: [PATCH 01/92] feat: add basic pasters (#7331) * feat: implement basic IPaster interface * feat: add pasters for blocks and workspace comments --- core/clipboard/block_paster.ts | 31 ++++++++++++++++++++++ core/clipboard/workspace_comment_paster.ts | 30 +++++++++++++++++++++ core/interfaces/i_paster.ts | 23 ++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 core/clipboard/block_paster.ts create mode 100644 core/clipboard/workspace_comment_paster.ts create mode 100644 core/interfaces/i_paster.ts diff --git a/core/clipboard/block_paster.ts b/core/clipboard/block_paster.ts new file mode 100644 index 000000000..363f70cad --- /dev/null +++ b/core/clipboard/block_paster.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {BlockSvg} from '../block_svg.js'; +import {CopyData} from '../interfaces/i_copyable'; +import {IPaster} from '../interfaces/i_paster.js'; +import {State, append} from '../serialization/blocks'; +import {Coordinate} from '../utils/coordinate.js'; +import {WorkspaceSvg} from '../workspace_svg.js'; + +export class BlockPaster implements IPaster { + paste( + copyData: BlockCopyData, + workspace: WorkspaceSvg, + coordinate?: Coordinate, + ): BlockSvg | null { + if (!workspace.isCapacityAvailable(copyData.typeCounts!)) return null; + + const state = copyData.saveInfo as State; + if (coordinate) { + state['x'] = coordinate.x; + state['y'] = coordinate.y; + } + return append(state, workspace, {recordUndo: true}) as BlockSvg; + } +} + +export interface BlockCopyData extends CopyData {} diff --git a/core/clipboard/workspace_comment_paster.ts b/core/clipboard/workspace_comment_paster.ts new file mode 100644 index 000000000..11211564c --- /dev/null +++ b/core/clipboard/workspace_comment_paster.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {IPaster} from '../interfaces/i_paster.js'; +import {CopyData} from '../interfaces/i_copyable.js'; +import {Coordinate} from '../utils/coordinate.js'; +import {WorkspaceSvg} from '../workspace_svg.js'; +import {WorkspaceCommentSvg} from '../workspace_comment_svg.js'; + +export class WorkspaceCommentPaster + implements IPaster +{ + paste( + copyData: WorkspaceCommentCopyData, + workspace: WorkspaceSvg, + coordinate?: Coordinate, + ): WorkspaceCommentSvg { + const state = copyData.saveInfo as Element; + if (coordinate) { + state.setAttribute('x', `${coordinate.x}`); + state.setAttribute('y', `${coordinate.y}`); + } + return WorkspaceCommentSvg.fromXmlRendered(state, workspace); + } +} + +export interface WorkspaceCommentCopyData extends CopyData {} diff --git a/core/interfaces/i_paster.ts b/core/interfaces/i_paster.ts new file mode 100644 index 000000000..9653d25df --- /dev/null +++ b/core/interfaces/i_paster.ts @@ -0,0 +1,23 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {Coordinate} from '../utils/coordinate.js'; +import {WorkspaceSvg} from '../workspace_svg.js'; +import {ICopyable, CopyData} from './i_copyable.js'; + +/** An object that can paste data into a workspace. */ +export interface IPaster { + paste( + copyData: U, + workspace: WorkspaceSvg, + coordinate?: Coordinate, + ): T | null; +} + +/** @returns True if the given object is a paster. */ +export function isPaster(obj: any): obj is IPaster { + return obj.paste !== undefined; +} From 74c01d27941a0a56ba4b733beb77d142fb0db943 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Mon, 31 Jul 2023 10:46:49 -0700 Subject: [PATCH 02/92] chore: fix paster exports and registration (#7343) --- core/blockly.ts | 2 ++ core/clipboard.ts | 4 +++ core/clipboard/block_paster.ts | 9 +++++-- core/clipboard/registry.ts | 31 ++++++++++++++++++++++ core/clipboard/workspace_comment_paster.ts | 5 ++++ core/registry.ts | 5 ++++ 6 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 core/clipboard/registry.ts diff --git a/core/blockly.ts b/core/blockly.ts index c14f3452b..9e8599ed9 100644 --- a/core/blockly.ts +++ b/core/blockly.ts @@ -152,6 +152,7 @@ import {IKeyboardAccessible} from './interfaces/i_keyboard_accessible.js'; import {IMetricsManager} from './interfaces/i_metrics_manager.js'; import {IMovable} from './interfaces/i_movable.js'; import {IObservable, isObservable} from './interfaces/i_observable.js'; +import {IPaster, isPaster} from './interfaces/i_paster.js'; import {IPositionable} from './interfaces/i_positionable.js'; import {IRegistrable} from './interfaces/i_registrable.js'; import {ISelectable} from './interfaces/i_selectable.js'; @@ -606,6 +607,7 @@ export {Input}; export {inputs}; export {InsertionMarkerManager}; export {IObservable, isObservable}; +export {IPaster, isPaster}; export {IPositionable}; export {IRegistrable}; export {ISelectable}; diff --git a/core/clipboard.ts b/core/clipboard.ts index 662a1c973..9cad96751 100644 --- a/core/clipboard.ts +++ b/core/clipboard.ts @@ -8,6 +8,8 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.clipboard'); import type {CopyData, ICopyable} from './interfaces/i_copyable.js'; +import {BlockPaster} from './clipboard/block_paster.js'; +import * as registry from './clipboard/registry.js'; /** Metadata about the object that is currently on the clipboard. */ let copyData: CopyData | null = null; @@ -82,3 +84,5 @@ export const TEST_ONLY = { duplicateInternal, copyInternal, }; + +export {BlockPaster, registry}; diff --git a/core/clipboard/block_paster.ts b/core/clipboard/block_paster.ts index 363f70cad..60ca336a5 100644 --- a/core/clipboard/block_paster.ts +++ b/core/clipboard/block_paster.ts @@ -5,13 +5,16 @@ */ import {BlockSvg} from '../block_svg.js'; -import {CopyData} from '../interfaces/i_copyable'; +import {registry} from '../clipboard.js'; +import {CopyData} from '../interfaces/i_copyable.js'; import {IPaster} from '../interfaces/i_paster.js'; -import {State, append} from '../serialization/blocks'; +import {State, append} from '../serialization/blocks.js'; import {Coordinate} from '../utils/coordinate.js'; import {WorkspaceSvg} from '../workspace_svg.js'; export class BlockPaster implements IPaster { + static TYPE = 'block'; + paste( copyData: BlockCopyData, workspace: WorkspaceSvg, @@ -29,3 +32,5 @@ export class BlockPaster implements IPaster { } export interface BlockCopyData extends CopyData {} + +registry.register(BlockPaster.TYPE, new BlockPaster()); diff --git a/core/clipboard/registry.ts b/core/clipboard/registry.ts new file mode 100644 index 000000000..5a8c8342e --- /dev/null +++ b/core/clipboard/registry.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {ICopyable, CopyData} from '../interfaces/i_copyable.js'; +import type {IPaster} from '../interfaces/i_paster.js'; +import * as registry from '../registry.js'; + +/** + * Registers the given paster so that it cna be used for pasting. + * + * @param type The type of the paster to register, e.g. 'block', 'comment', etc. + * @param paster The paster to register. + */ +export function register( + type: string, + paster: IPaster, +) { + registry.register(registry.Type.PASTER, type, paster); +} + +/** + * Unregisters the paster associated with the given type. + * + * @param type The type of the paster to unregister. + */ +export function unregister(type: string) { + registry.unregister(registry.Type.PASTER, type); +} diff --git a/core/clipboard/workspace_comment_paster.ts b/core/clipboard/workspace_comment_paster.ts index 11211564c..7959802d6 100644 --- a/core/clipboard/workspace_comment_paster.ts +++ b/core/clipboard/workspace_comment_paster.ts @@ -9,10 +9,13 @@ import {CopyData} from '../interfaces/i_copyable.js'; import {Coordinate} from '../utils/coordinate.js'; import {WorkspaceSvg} from '../workspace_svg.js'; import {WorkspaceCommentSvg} from '../workspace_comment_svg.js'; +import {registry} from '../clipboard.js'; export class WorkspaceCommentPaster implements IPaster { + static TYPE = 'workspace-comment'; + paste( copyData: WorkspaceCommentCopyData, workspace: WorkspaceSvg, @@ -28,3 +31,5 @@ export class WorkspaceCommentPaster } export interface WorkspaceCommentCopyData extends CopyData {} + +registry.register(WorkspaceCommentPaster.TYPE, new WorkspaceCommentPaster()); diff --git a/core/registry.ts b/core/registry.ts index a20227000..2893f3d0f 100644 --- a/core/registry.ts +++ b/core/registry.ts @@ -22,6 +22,8 @@ import type {Options} from './options.js'; import type {Renderer} from './renderers/common/renderer.js'; import type {Theme} from './theme.js'; import type {ToolboxItem} from './toolbox/toolbox_item.js'; +import {IPaster} from './interfaces/i_paster.js'; +import {CopyData, ICopyable} from './interfaces/i_copyable.js'; /** * A map of maps. With the keys being the type and name of the class we are @@ -96,6 +98,9 @@ export class Type<_T> { /** @internal */ static ICON = new Type('icon'); + + /** @internal */ + static PASTER = new Type>('paster'); } /** From ce1678e8a757b02299a305c3729b5c406dd1a498 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Tue, 1 Aug 2023 11:51:21 -0700 Subject: [PATCH 03/92] fix: refactor CopyData interface to have the correct structure (#7344) * chore: rename CopyData to ICopyData * fix: ICopyable data structures * fix: switch clipboard over to use new copy data interfaces * chore: rename saveInfo to somthing more descriptive --- core/block_svg.ts | 10 +++---- core/clipboard.ts | 31 +++++++++++++--------- core/clipboard/block_paster.ts | 18 ++++++++----- core/clipboard/registry.ts | 4 +-- core/clipboard/workspace_comment_paster.ts | 18 ++++++++----- core/interfaces/i_copyable.ts | 12 +++------ core/interfaces/i_paster.ts | 6 ++--- core/registry.ts | 4 +-- core/workspace_comment_svg.ts | 14 +++++----- 9 files changed, 65 insertions(+), 52 deletions(-) diff --git a/core/block_svg.ts b/core/block_svg.ts index e4053de0a..ff251b2ef 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -37,7 +37,7 @@ import {FieldLabel} from './field_label.js'; import type {Input} from './inputs/input.js'; import type {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js'; import type {IBoundedElement} from './interfaces/i_bounded_element.js'; -import type {CopyData, ICopyable} from './interfaces/i_copyable.js'; +import type {ICopyable} from './interfaces/i_copyable.js'; import type {IDraggable} from './interfaces/i_draggable.js'; import {IIcon} from './interfaces/i_icon.js'; import * as internalConstants from './internal_constants.js'; @@ -62,6 +62,7 @@ import type {WorkspaceSvg} from './workspace_svg.js'; import * as renderManagement from './render_management.js'; import * as deprecation from './utils/deprecation.js'; import {IconType} from './icons/icon_types.js'; +import {BlockCopyData, BlockPaster} from './clipboard/block_paster.js'; /** * Class for a block's SVG representation. @@ -823,18 +824,17 @@ export class BlockSvg * Encode a block for copying. * * @returns Copy metadata, or null if the block is an insertion marker. - * @internal */ - toCopyData(): CopyData | null { + toCopyData(): BlockCopyData | null { if (this.isInsertionMarker_) { return null; } return { - saveInfo: blocks.save(this, { + paster: BlockPaster.TYPE, + blockState: blocks.save(this, { addCoordinates: true, addNextBlocks: false, }) as blocks.State, - source: this.workspace, typeCounts: common.getBlockTypeCounts(this, true), }; } diff --git a/core/clipboard.ts b/core/clipboard.ts index 9cad96751..627d356bc 100644 --- a/core/clipboard.ts +++ b/core/clipboard.ts @@ -7,12 +7,16 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.clipboard'); -import type {CopyData, ICopyable} from './interfaces/i_copyable.js'; +import type {ICopyData, ICopyable} from './interfaces/i_copyable.js'; import {BlockPaster} from './clipboard/block_paster.js'; +import * as globalRegistry from './registry.js'; +import {WorkspaceSvg} from './workspace_svg.js'; import * as registry from './clipboard/registry.js'; /** Metadata about the object that is currently on the clipboard. */ -let copyData: CopyData | null = null; +let copyData: ICopyData | null = null; + +let source: WorkspaceSvg | null = null; /** * Copy a block or workspace comment onto the local clipboard. @@ -29,6 +33,7 @@ export function copy(toCopy: ICopyable) { */ function copyInternal(toCopy: ICopyable) { copyData = toCopy.toCopyData(); + source = (toCopy as any).workspace ?? null; } /** @@ -43,17 +48,16 @@ export function paste(): ICopyable | null { } // Pasting always pastes to the main workspace, even if the copy // started in a flyout workspace. - let workspace = copyData.source; - if (workspace.isFlyout) { + let workspace = source; + if (workspace?.isFlyout) { workspace = workspace.targetWorkspace!; } - if ( - copyData.typeCounts && - workspace.isCapacityAvailable(copyData.typeCounts) - ) { - return workspace.paste(copyData.saveInfo); - } - return null; + if (!workspace) return null; + return ( + globalRegistry + .getObject(globalRegistry.Type.PASTER, copyData.paster, false) + ?.paste(copyData, workspace) ?? null + ); } /** @@ -74,8 +78,11 @@ export function duplicate(toDuplicate: ICopyable): ICopyable | null { function duplicateInternal(toDuplicate: ICopyable): ICopyable | null { const oldCopyData = copyData; copy(toDuplicate); + if (!copyData || !source) return null; const pastedThing = - toDuplicate.toCopyData()?.source?.paste(copyData!.saveInfo) ?? null; + globalRegistry + .getObject(globalRegistry.Type.PASTER, copyData.paster, false) + ?.paste(copyData, source) ?? null; copyData = oldCopyData; return pastedThing; } diff --git a/core/clipboard/block_paster.ts b/core/clipboard/block_paster.ts index 60ca336a5..3ed8383db 100644 --- a/core/clipboard/block_paster.ts +++ b/core/clipboard/block_paster.ts @@ -5,8 +5,8 @@ */ import {BlockSvg} from '../block_svg.js'; -import {registry} from '../clipboard.js'; -import {CopyData} from '../interfaces/i_copyable.js'; +import * as registry from './registry.js'; +import {ICopyData} from '../interfaces/i_copyable.js'; import {IPaster} from '../interfaces/i_paster.js'; import {State, append} from '../serialization/blocks.js'; import {Coordinate} from '../utils/coordinate.js'; @@ -22,15 +22,19 @@ export class BlockPaster implements IPaster { ): BlockSvg | null { if (!workspace.isCapacityAvailable(copyData.typeCounts!)) return null; - const state = copyData.saveInfo as State; if (coordinate) { - state['x'] = coordinate.x; - state['y'] = coordinate.y; + copyData.blockState['x'] = coordinate.x; + copyData.blockState['y'] = coordinate.y; } - return append(state, workspace, {recordUndo: true}) as BlockSvg; + return append(copyData.blockState, workspace, { + recordUndo: true, + }) as BlockSvg; } } -export interface BlockCopyData extends CopyData {} +export interface BlockCopyData extends ICopyData { + blockState: State; + typeCounts: {[key: string]: number}; +} registry.register(BlockPaster.TYPE, new BlockPaster()); diff --git a/core/clipboard/registry.ts b/core/clipboard/registry.ts index 5a8c8342e..396342341 100644 --- a/core/clipboard/registry.ts +++ b/core/clipboard/registry.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ICopyable, CopyData} from '../interfaces/i_copyable.js'; +import {ICopyable, ICopyData} from '../interfaces/i_copyable.js'; import type {IPaster} from '../interfaces/i_paster.js'; import * as registry from '../registry.js'; @@ -14,7 +14,7 @@ import * as registry from '../registry.js'; * @param type The type of the paster to register, e.g. 'block', 'comment', etc. * @param paster The paster to register. */ -export function register( +export function register( type: string, paster: IPaster, ) { diff --git a/core/clipboard/workspace_comment_paster.ts b/core/clipboard/workspace_comment_paster.ts index 7959802d6..b9933f5a1 100644 --- a/core/clipboard/workspace_comment_paster.ts +++ b/core/clipboard/workspace_comment_paster.ts @@ -5,11 +5,11 @@ */ import {IPaster} from '../interfaces/i_paster.js'; -import {CopyData} from '../interfaces/i_copyable.js'; +import {ICopyData} from '../interfaces/i_copyable.js'; import {Coordinate} from '../utils/coordinate.js'; import {WorkspaceSvg} from '../workspace_svg.js'; import {WorkspaceCommentSvg} from '../workspace_comment_svg.js'; -import {registry} from '../clipboard.js'; +import * as registry from './registry.js'; export class WorkspaceCommentPaster implements IPaster @@ -21,15 +21,19 @@ export class WorkspaceCommentPaster workspace: WorkspaceSvg, coordinate?: Coordinate, ): WorkspaceCommentSvg { - const state = copyData.saveInfo as Element; if (coordinate) { - state.setAttribute('x', `${coordinate.x}`); - state.setAttribute('y', `${coordinate.y}`); + copyData.commentState.setAttribute('x', `${coordinate.x}`); + copyData.commentState.setAttribute('y', `${coordinate.y}`); } - return WorkspaceCommentSvg.fromXmlRendered(state, workspace); + return WorkspaceCommentSvg.fromXmlRendered( + copyData.commentState, + workspace, + ); } } -export interface WorkspaceCommentCopyData extends CopyData {} +export interface WorkspaceCommentCopyData extends ICopyData { + commentState: Element; +} registry.register(WorkspaceCommentPaster.TYPE, new WorkspaceCommentPaster()); diff --git a/core/interfaces/i_copyable.ts b/core/interfaces/i_copyable.ts index d62d9ef83..6035c97ce 100644 --- a/core/interfaces/i_copyable.ts +++ b/core/interfaces/i_copyable.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.ICopyable'); -import type {WorkspaceSvg} from '../workspace_svg.js'; import type {ISelectable} from './i_selectable.js'; export interface ICopyable extends ISelectable { @@ -15,17 +14,14 @@ export interface ICopyable extends ISelectable { * Encode for copying. * * @returns Copy metadata. - * @internal */ - toCopyData(): CopyData | null; + toCopyData(): ICopyData | null; } export namespace ICopyable { - export interface CopyData { - saveInfo: Object | Element; - source: WorkspaceSvg; - typeCounts: {[key: string]: number} | null; + export interface ICopyData { + paster: string; } } -export type CopyData = ICopyable.CopyData; +export type ICopyData = ICopyable.ICopyData; diff --git a/core/interfaces/i_paster.ts b/core/interfaces/i_paster.ts index 9653d25df..cb2d079c2 100644 --- a/core/interfaces/i_paster.ts +++ b/core/interfaces/i_paster.ts @@ -6,10 +6,10 @@ import {Coordinate} from '../utils/coordinate.js'; import {WorkspaceSvg} from '../workspace_svg.js'; -import {ICopyable, CopyData} from './i_copyable.js'; +import {ICopyable, ICopyData} from './i_copyable.js'; /** An object that can paste data into a workspace. */ -export interface IPaster { +export interface IPaster { paste( copyData: U, workspace: WorkspaceSvg, @@ -18,6 +18,6 @@ export interface IPaster { } /** @returns True if the given object is a paster. */ -export function isPaster(obj: any): obj is IPaster { +export function isPaster(obj: any): obj is IPaster { return obj.paste !== undefined; } diff --git a/core/registry.ts b/core/registry.ts index 2893f3d0f..6e974718c 100644 --- a/core/registry.ts +++ b/core/registry.ts @@ -23,7 +23,7 @@ import type {Renderer} from './renderers/common/renderer.js'; import type {Theme} from './theme.js'; import type {ToolboxItem} from './toolbox/toolbox_item.js'; import {IPaster} from './interfaces/i_paster.js'; -import {CopyData, ICopyable} from './interfaces/i_copyable.js'; +import {ICopyData, ICopyable} from './interfaces/i_copyable.js'; /** * A map of maps. With the keys being the type and name of the class we are @@ -100,7 +100,7 @@ export class Type<_T> { static ICON = new Type('icon'); /** @internal */ - static PASTER = new Type>('paster'); + static PASTER = new Type>('paster'); } /** diff --git a/core/workspace_comment_svg.ts b/core/workspace_comment_svg.ts index ab57fd5ef..4e1a72611 100644 --- a/core/workspace_comment_svg.ts +++ b/core/workspace_comment_svg.ts @@ -23,7 +23,7 @@ import type {CommentMove} from './events/events_comment_move.js'; import * as eventUtils from './events/utils.js'; import type {IBoundedElement} from './interfaces/i_bounded_element.js'; import type {IBubble} from './interfaces/i_bubble.js'; -import type {CopyData, ICopyable} from './interfaces/i_copyable.js'; +import type {ICopyable} from './interfaces/i_copyable.js'; import * as Touch from './touch.js'; import {Coordinate} from './utils/coordinate.js'; import * as dom from './utils/dom.js'; @@ -32,6 +32,10 @@ import {Svg} from './utils/svg.js'; import * as svgMath from './utils/svg_math.js'; import {WorkspaceComment} from './workspace_comment.js'; import type {WorkspaceSvg} from './workspace_svg.js'; +import { + WorkspaceCommentCopyData, + WorkspaceCommentPaster, +} from './clipboard/workspace_comment_paster.js'; /** Size of the resize icon. */ const RESIZE_SIZE = 8; @@ -566,13 +570,11 @@ export class WorkspaceCommentSvg * Encode a comment for copying. * * @returns Copy metadata. - * @internal */ - toCopyData(): CopyData { + toCopyData(): WorkspaceCommentCopyData { return { - saveInfo: this.toXmlWithXY(), - source: this.workspace, - typeCounts: null, + paster: WorkspaceCommentPaster.TYPE, + commentState: this.toXmlWithXY(), }; } From 001d9ff2c9609d2288a5cd33cc86f957c58c05cc Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Thu, 3 Aug 2023 15:33:58 -0700 Subject: [PATCH 04/92] feat: make ICopyable generic and update clipboard APIs (#7348) * chore: rename module-local variables to not conflict * feat: make ICopyable generic and update clipboard APIs * chore: switch over more things to use generic ICopyables * chore: fix shortcut items using copy paste * chore: add test for interface between clipboard and pasters * chore: export isCopyable * chore: format * chore: fixup PR comments * chore: add deprecation tags --- core/block_svg.ts | 6 +- core/blockly.ts | 4 +- core/clipboard.ts | 142 ++++++++++++++++++++++++---------- core/clipboard/registry.ts | 2 +- core/common.ts | 8 +- core/interfaces/i_copyable.ts | 9 ++- core/interfaces/i_paster.ts | 6 +- core/registry.ts | 2 +- core/shortcut_items.ts | 11 ++- core/workspace_comment_svg.ts | 2 +- core/workspace_svg.ts | 4 +- tests/mocha/clipboard_test.js | 35 +++++++++ tests/mocha/index.html | 1 + 13 files changed, 169 insertions(+), 63 deletions(-) create mode 100644 tests/mocha/clipboard_test.js diff --git a/core/block_svg.ts b/core/block_svg.ts index ff251b2ef..d8c9ef6ee 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -70,7 +70,11 @@ import {BlockCopyData, BlockPaster} from './clipboard/block_paster.js'; */ export class BlockSvg extends Block - implements IASTNodeLocationSvg, IBoundedElement, ICopyable, IDraggable + implements + IASTNodeLocationSvg, + IBoundedElement, + ICopyable, + IDraggable { /** * Constant for identifying rows that are to be rendered inline. diff --git a/core/blockly.ts b/core/blockly.ts index 9e8599ed9..76cf5d136 100644 --- a/core/blockly.ts +++ b/core/blockly.ts @@ -140,7 +140,7 @@ import {ICollapsibleToolboxItem} from './interfaces/i_collapsible_toolbox_item.j import {IComponent} from './interfaces/i_component.js'; import {IConnectionChecker} from './interfaces/i_connection_checker.js'; import {IContextMenu} from './interfaces/i_contextmenu.js'; -import {ICopyable} from './interfaces/i_copyable.js'; +import {ICopyable, isCopyable} from './interfaces/i_copyable.js'; import {IDeletable} from './interfaces/i_deletable.js'; import {IDeleteArea} from './interfaces/i_delete_area.js'; import {IDragTarget} from './interfaces/i_drag_target.js'; @@ -592,7 +592,7 @@ export {IComponent}; export {IConnectionChecker}; export {IContextMenu}; export {icons}; -export {ICopyable}; +export {ICopyable, isCopyable}; export {IDeletable}; export {IDeleteArea}; export {IDragTarget}; diff --git a/core/clipboard.ts b/core/clipboard.ts index 627d356bc..ae49a93e4 100644 --- a/core/clipboard.ts +++ b/core/clipboard.ts @@ -12,79 +12,139 @@ import {BlockPaster} from './clipboard/block_paster.js'; import * as globalRegistry from './registry.js'; import {WorkspaceSvg} from './workspace_svg.js'; import * as registry from './clipboard/registry.js'; +import {Coordinate} from './utils/coordinate.js'; +import * as deprecation from './utils/deprecation.js'; /** Metadata about the object that is currently on the clipboard. */ -let copyData: ICopyData | null = null; +let stashedCopyData: ICopyData | null = null; -let source: WorkspaceSvg | null = null; +let stashedWorkspace: WorkspaceSvg | null = null; /** - * Copy a block or workspace comment onto the local clipboard. + * Copy a copyable element onto the local clipboard. * - * @param toCopy Block or Workspace Comment to be copied. + * @param toCopy The copyable element to be copied. + * @deprecated v11. Use `myCopyable.toCopyData()` instead. To be removed v12. * @internal */ -export function copy(toCopy: ICopyable) { - TEST_ONLY.copyInternal(toCopy); +export function copy(toCopy: ICopyable): T | null { + deprecation.warn( + 'Blockly.clipboard.copy', + 'v11', + 'v12', + 'myCopyable.toCopyData()', + ); + return TEST_ONLY.copyInternal(toCopy); } /** * Private version of copy for stubbing in tests. */ -function copyInternal(toCopy: ICopyable) { - copyData = toCopy.toCopyData(); - source = (toCopy as any).workspace ?? null; +function copyInternal(toCopy: ICopyable): T | null { + const data = toCopy.toCopyData(); + stashedCopyData = data; + stashedWorkspace = (toCopy as any).workspace ?? null; + return data; } /** - * Paste a block or workspace comment on to the main workspace. + * Paste a pasteable element into the workspace. * + * @param copyData The data to paste into the workspace. + * @param workspace The workspace to paste the data into. + * @param coordinate The location to paste the thing at. * @returns The pasted thing if the paste was successful, null otherwise. - * @internal */ -export function paste(): ICopyable | null { - if (!copyData) { - return null; +export function paste( + copyData: T, + workspace: WorkspaceSvg, + coordinate?: Coordinate, +): ICopyable | null; + +/** + * Pastes the last copied ICopyable into the workspace. + * + * @returns the pasted thing if the paste was successful, null otherwise. + */ +export function paste(): ICopyable | null; + +/** + * Pastes the given data into the workspace, or the last copied ICopyable if + * no data is passed. + * + * @param copyData The data to paste into the workspace. + * @param workspace The workspace to paste the data into. + * @param coordinate The location to paste the thing at. + * @returns The pasted thing if the paste was successful, null otherwise. + */ +export function paste( + copyData?: T, + workspace?: WorkspaceSvg, + coordinate?: Coordinate, +): ICopyable | null { + if (!copyData || !workspace) { + if (!stashedCopyData || !stashedWorkspace) return null; + return pasteFromData(stashedCopyData, stashedWorkspace); } - // Pasting always pastes to the main workspace, even if the copy - // started in a flyout workspace. - let workspace = source; - if (workspace?.isFlyout) { - workspace = workspace.targetWorkspace!; - } - if (!workspace) return null; - return ( - globalRegistry - .getObject(globalRegistry.Type.PASTER, copyData.paster, false) - ?.paste(copyData, workspace) ?? null - ); + return pasteFromData(copyData, workspace, coordinate); } /** - * Duplicate this block and its children, or a workspace comment. + * Paste a pasteable element into the workspace. * - * @param toDuplicate Block or Workspace Comment to be duplicated. - * @returns The block or workspace comment that was duplicated, or null if the - * duplication failed. + * @param copyData The data to paste into the workspace. + * @param workspace The workspace to paste the data into. + * @param coordinate The location to paste the thing at. + * @returns The pasted thing if the paste was successful, null otherwise. + */ +function pasteFromData( + copyData: T, + workspace: WorkspaceSvg, + coordinate?: Coordinate, +): ICopyable | null { + workspace = workspace.getRootWorkspace() ?? workspace; + return (globalRegistry + .getObject(globalRegistry.Type.PASTER, copyData.paster, false) + ?.paste(copyData, workspace, coordinate) ?? null) as ICopyable | null; +} + +/** + * Duplicate this copy-paste-able element. + * + * @param toDuplicate The element to be duplicated. + * @returns The element that was duplicated, or null if the duplication failed. + * @deprecated v11. Use + * `Blockly.clipboard.paste(myCopyable.toCopyData(), myWorkspace)` instead. + * To be removed v12. * @internal */ -export function duplicate(toDuplicate: ICopyable): ICopyable | null { +export function duplicate< + U extends ICopyData, + T extends ICopyable & IHasWorkspace, +>(toDuplicate: T): T | null { + deprecation.warn( + 'Blockly.clipboard.duplicate', + 'v11', + 'v12', + 'Blockly.clipboard.paste(myCopyable.toCopyData(), myWorkspace)', + ); return TEST_ONLY.duplicateInternal(toDuplicate); } /** * Private version of duplicate for stubbing in tests. */ -function duplicateInternal(toDuplicate: ICopyable): ICopyable | null { - const oldCopyData = copyData; - copy(toDuplicate); - if (!copyData || !source) return null; - const pastedThing = - globalRegistry - .getObject(globalRegistry.Type.PASTER, copyData.paster, false) - ?.paste(copyData, source) ?? null; - copyData = oldCopyData; - return pastedThing; +function duplicateInternal< + U extends ICopyData, + T extends ICopyable & IHasWorkspace, +>(toDuplicate: T): T | null { + const data = toDuplicate.toCopyData(); + if (!data) return null; + return paste(data, toDuplicate.workspace) as T; +} + +interface IHasWorkspace { + workspace: WorkspaceSvg; } export const TEST_ONLY = { diff --git a/core/clipboard/registry.ts b/core/clipboard/registry.ts index 396342341..1257f5bdb 100644 --- a/core/clipboard/registry.ts +++ b/core/clipboard/registry.ts @@ -14,7 +14,7 @@ import * as registry from '../registry.js'; * @param type The type of the paster to register, e.g. 'block', 'comment', etc. * @param paster The paster to register. */ -export function register( +export function register>( type: string, paster: IPaster, ) { diff --git a/core/common.ts b/core/common.ts index b5fcd3540..abb6b4042 100644 --- a/core/common.ts +++ b/core/common.ts @@ -9,9 +9,9 @@ goog.declareModuleId('Blockly.common'); /* eslint-disable-next-line no-unused-vars */ import type {Block} from './block.js'; +import {ISelectable} from './blockly.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'; @@ -88,12 +88,12 @@ export function setMainWorkspace(workspace: Workspace) { /** * Currently selected copyable object. */ -let selected: ICopyable | null = null; +let selected: ISelectable | null = null; /** * Returns the currently selected copyable object. */ -export function getSelected(): ICopyable | null { +export function getSelected(): ISelectable | null { return selected; } @@ -105,7 +105,7 @@ export function getSelected(): ICopyable | null { * @param newSelection The newly selected block. * @internal */ -export function setSelected(newSelection: ICopyable | null) { +export function setSelected(newSelection: ISelectable | null) { selected = newSelection; } diff --git a/core/interfaces/i_copyable.ts b/core/interfaces/i_copyable.ts index 6035c97ce..b77819839 100644 --- a/core/interfaces/i_copyable.ts +++ b/core/interfaces/i_copyable.ts @@ -9,13 +9,13 @@ goog.declareModuleId('Blockly.ICopyable'); import type {ISelectable} from './i_selectable.js'; -export interface ICopyable extends ISelectable { +export interface ICopyable extends ISelectable { /** * Encode for copying. * * @returns Copy metadata. */ - toCopyData(): ICopyData | null; + toCopyData(): T | null; } export namespace ICopyable { @@ -25,3 +25,8 @@ export namespace ICopyable { } export type ICopyData = ICopyable.ICopyData; + +/** @returns true if the given object is copyable. */ +export function isCopyable(obj: any): obj is ICopyable { + return obj.toCopyData !== undefined; +} diff --git a/core/interfaces/i_paster.ts b/core/interfaces/i_paster.ts index cb2d079c2..321ff118f 100644 --- a/core/interfaces/i_paster.ts +++ b/core/interfaces/i_paster.ts @@ -9,7 +9,7 @@ import {WorkspaceSvg} from '../workspace_svg.js'; import {ICopyable, ICopyData} from './i_copyable.js'; /** An object that can paste data into a workspace. */ -export interface IPaster { +export interface IPaster> { paste( copyData: U, workspace: WorkspaceSvg, @@ -18,6 +18,8 @@ export interface IPaster { } /** @returns True if the given object is a paster. */ -export function isPaster(obj: any): obj is IPaster { +export function isPaster( + obj: any, +): obj is IPaster> { return obj.paste !== undefined; } diff --git a/core/registry.ts b/core/registry.ts index 6e974718c..7772f9fb2 100644 --- a/core/registry.ts +++ b/core/registry.ts @@ -100,7 +100,7 @@ export class Type<_T> { static ICON = new Type('icon'); /** @internal */ - static PASTER = new Type>('paster'); + static PASTER = new Type>>('paster'); } /** diff --git a/core/shortcut_items.ts b/core/shortcut_items.ts index efe876649..4b840fce5 100644 --- a/core/shortcut_items.ts +++ b/core/shortcut_items.ts @@ -11,7 +11,7 @@ import {BlockSvg} from './block_svg.js'; import * as clipboard from './clipboard.js'; import * as common from './common.js'; import {Gesture} from './gesture.js'; -import type {ICopyable} from './interfaces/i_copyable.js'; +import {isCopyable} from './interfaces/i_copyable.js'; import {KeyboardShortcut, ShortcutRegistry} from './shortcut_registry.js'; import {KeyCodes} from './utils/keycodes.js'; import type {WorkspaceSvg} from './workspace_svg.js'; @@ -114,7 +114,9 @@ export function registerCopy() { // AnyDuringMigration because: Property 'hideChaff' does not exist on // type 'Workspace'. (workspace as AnyDuringMigration).hideChaff(); - clipboard.copy(common.getSelected() as ICopyable); + const selected = common.getSelected(); + if (!selected || !isCopyable(selected)) return false; + clipboard.copy(selected); return true; }, keyCodes: [ctrlC, altC, metaC], @@ -152,10 +154,7 @@ export function registerCut() { }, callback() { const selected = common.getSelected(); - if (!selected) { - // Shouldn't happen but appeases the type system - return false; - } + if (!selected || !isCopyable(selected)) return false; clipboard.copy(selected); (selected as BlockSvg).checkAndDelete(); return true; diff --git a/core/workspace_comment_svg.ts b/core/workspace_comment_svg.ts index 4e1a72611..0194252eb 100644 --- a/core/workspace_comment_svg.ts +++ b/core/workspace_comment_svg.ts @@ -51,7 +51,7 @@ const TEXTAREA_OFFSET = 2; */ export class WorkspaceCommentSvg extends WorkspaceComment - implements IBoundedElement, IBubble, ICopyable + implements IBoundedElement, IBubble, ICopyable { /** * The width and height to use to size a workspace comment when it is first diff --git a/core/workspace_svg.ts b/core/workspace_svg.ts index 01888e55f..16c84c91e 100644 --- a/core/workspace_svg.ts +++ b/core/workspace_svg.ts @@ -36,7 +36,7 @@ import {Gesture} from './gesture.js'; import {Grid} from './grid.js'; import type {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js'; import type {IBoundedElement} from './interfaces/i_bounded_element.js'; -import type {ICopyable} from './interfaces/i_copyable.js'; +import type {ICopyData, ICopyable} from './interfaces/i_copyable.js'; import type {IDragTarget} from './interfaces/i_drag_target.js'; import type {IFlyout} from './interfaces/i_flyout.js'; import type {IMetricsManager} from './interfaces/i_metrics_manager.js'; @@ -1300,7 +1300,7 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg { */ paste( state: AnyDuringMigration | Element | DocumentFragment, - ): ICopyable | null { + ): ICopyable | null { if (!this.rendered || (!state['type'] && !state['tagName'])) { return null; } diff --git a/tests/mocha/clipboard_test.js b/tests/mocha/clipboard_test.js new file mode 100644 index 000000000..2db315f6e --- /dev/null +++ b/tests/mocha/clipboard_test.js @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.clipboard'); + +import { + sharedTestSetup, + sharedTestTeardown, +} from './test_helpers/setup_teardown.js'; + +suite('Clipboard', function () { + setup(function () { + this.clock = sharedTestSetup.call(this, {fireEventsNow: false}).clock; + this.workspace = new Blockly.WorkspaceSvg(new Blockly.Options({})); + }); + + teardown(function () { + sharedTestTeardown.call(this); + }); + + test('a paster registered with a given type is called when pasting that type', function () { + const paster = { + paste: sinon.stub().returns(null), + }; + Blockly.clipboard.registry.register('test-paster', paster); + + Blockly.clipboard.paste({paster: 'test-paster'}, this.workspace); + chai.assert.isTrue(paster.paste.calledOnce); + + Blockly.clipboard.registry.unregister('test-paster'); + }); +}); diff --git a/tests/mocha/index.html b/tests/mocha/index.html index a38e08517..de11f6a94 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -38,6 +38,7 @@ 'Blockly.test.astNode', 'Blockly.test.blockJson', 'Blockly.test.blocks', + 'Blockly.test.clipboard', 'Blockly.test.comments', 'Blockly.test.commentDeserialization', 'Blockly.test.connectionChecker', From a901c62d0c0d1755668b459980cedb86f1233a91 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Wed, 9 Aug 2023 10:31:29 -0700 Subject: [PATCH 05/92] fix: bumping copied objects (#7349) * fix: add logic for bumping pasted blocks * chore: add tests for bumping pasted blocks to the correct location * fix: add logic for bumping pasted comments * chore: add tests for bumping pasted comments --- core/clipboard/block_paster.ts | 86 ++++++++++++++++- core/clipboard/workspace_comment_paster.ts | 10 +- core/serialization/blocks.ts | 4 +- core/workspace_svg.ts | 7 ++ tests/mocha/clipboard_test.js | 105 ++++++++++++++++++++- tests/mocha/test_helpers/events.js | 11 +-- 6 files changed, 208 insertions(+), 15 deletions(-) diff --git a/core/clipboard/block_paster.ts b/core/clipboard/block_paster.ts index 3ed8383db..0bb707c36 100644 --- a/core/clipboard/block_paster.ts +++ b/core/clipboard/block_paster.ts @@ -11,6 +11,8 @@ import {IPaster} from '../interfaces/i_paster.js'; import {State, append} from '../serialization/blocks.js'; import {Coordinate} from '../utils/coordinate.js'; import {WorkspaceSvg} from '../workspace_svg.js'; +import * as eventUtils from '../events/utils.js'; +import {config} from '../config.js'; export class BlockPaster implements IPaster { static TYPE = 'block'; @@ -26,12 +28,90 @@ export class BlockPaster implements IPaster { copyData.blockState['x'] = coordinate.x; copyData.blockState['y'] = coordinate.y; } - return append(copyData.blockState, workspace, { - recordUndo: true, - }) as BlockSvg; + + eventUtils.disable(); + let block; + try { + block = append(copyData.blockState, workspace) as BlockSvg; + moveBlockToNotConflict(block); + } finally { + eventUtils.enable(); + } + + if (!block) return block; + + if (eventUtils.isEnabled() && !block.isShadow()) { + eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(block)); + } + block.select(); + return block; } } +/** + * Moves the given block to a location where it does not: (1) overlap exactly + * with any other blocks, or (2) look like it is connected to any other blocks. + * + * Exported for testing. + * + * @param block The block to move to an unambiguous location. + * @internal + */ +export function moveBlockToNotConflict(block: BlockSvg) { + const workspace = block.workspace; + const snapRadius = config.snapRadius; + const coord = block.getRelativeToSurfaceXY(); + const offset = new Coordinate(0, 0); + // getRelativeToSurfaceXY is really expensive, so we want to cache this. + const otherCoords = workspace + .getAllBlocks(false) + .filter((otherBlock) => otherBlock.id != block.id) + .map((b) => b.getRelativeToSurfaceXY()); + + while ( + blockOverlapsOtherExactly(Coordinate.sum(coord, offset), otherCoords) || + blockIsInSnapRadius(block, offset, snapRadius) + ) { + if (workspace.RTL) { + offset.translate(-snapRadius, snapRadius * 2); + } else { + offset.translate(snapRadius, snapRadius * 2); + } + } + + block!.moveTo(Coordinate.sum(coord, offset)); +} + +/** + * @returns true if the given block coordinates are less than a delta of 1 from + * any of the other coordinates. + */ +function blockOverlapsOtherExactly( + coord: Coordinate, + otherCoords: Coordinate[], +): boolean { + return otherCoords.some( + (otherCoord) => + Math.abs(otherCoord.x - coord.x) <= 1 && + Math.abs(otherCoord.y - coord.y) <= 1, + ); +} + +/** + * @returns true if the given block (when offset by the given amount) is close + * enough to any other connections (within the snap radius) that it looks + * like they could connect. + */ +function blockIsInSnapRadius( + block: BlockSvg, + offset: Coordinate, + snapRadius: number, +): boolean { + return block + .getConnections_(false) + .some((connection) => !!connection.closest(snapRadius, offset).connection); +} + export interface BlockCopyData extends ICopyData { blockState: State; typeCounts: {[key: string]: number}; diff --git a/core/clipboard/workspace_comment_paster.ts b/core/clipboard/workspace_comment_paster.ts index b9933f5a1..aeedbfb2b 100644 --- a/core/clipboard/workspace_comment_paster.ts +++ b/core/clipboard/workspace_comment_paster.ts @@ -21,9 +21,15 @@ export class WorkspaceCommentPaster workspace: WorkspaceSvg, coordinate?: Coordinate, ): WorkspaceCommentSvg { + const state = copyData.commentState; if (coordinate) { - copyData.commentState.setAttribute('x', `${coordinate.x}`); - copyData.commentState.setAttribute('y', `${coordinate.y}`); + state.setAttribute('x', `${coordinate.x}`); + state.setAttribute('y', `${coordinate.y}`); + } else { + const x = parseInt(state.getAttribute('x') ?? '0') + 50; + const y = parseInt(state.getAttribute('y') ?? '0') + 50; + state.setAttribute('x', `${x}`); + state.setAttribute('y', `${y}`); } return WorkspaceCommentSvg.fromXmlRendered( copyData.commentState, diff --git a/core/serialization/blocks.ts b/core/serialization/blocks.ts index c9c6395ac..f11a7d8cd 100644 --- a/core/serialization/blocks.ts +++ b/core/serialization/blocks.ts @@ -396,7 +396,9 @@ export function appendInternal( const block = appendPrivate(state, workspace, {parentConnection, isShadow}); eventUtils.enable(); - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(block)); + if (eventUtils.isEnabled()) { + eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(block)); + } eventUtils.setGroup(existingGroup); eventUtils.setRecordUndo(prevRecordUndo); diff --git a/core/workspace_svg.ts b/core/workspace_svg.ts index 16c84c91e..5978c3197 100644 --- a/core/workspace_svg.ts +++ b/core/workspace_svg.ts @@ -1388,6 +1388,10 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg { for (let i = 0, connection; (connection = connections[i]); i++) { const neighbour = connection.closest( config.snapRadius, + // TODO: This code doesn't work because it's passing an absolute + // coordinate instead of a relative coordinate. Need to + // figure out if I'm deprecating this function or if I + // need to fix this. new Coordinate(blockX, blockY), ); if (neighbour.connection) { @@ -1441,6 +1445,9 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg { // with any blocks. commentX += 50; commentY += 50; + // TODO: This code doesn't work because it's using absolute coords + // where relative coords are expected. Need to figure out what I'm + // doing with this function and if I need to fix it. comment.moveBy(commentX, commentY); } } finally { diff --git a/tests/mocha/clipboard_test.js b/tests/mocha/clipboard_test.js index 2db315f6e..f6cf10a25 100644 --- a/tests/mocha/clipboard_test.js +++ b/tests/mocha/clipboard_test.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2019 Google LLC + * Copyright 2023 Google LLC * SPDX-License-Identifier: Apache-2.0 */ @@ -10,11 +10,15 @@ import { sharedTestSetup, sharedTestTeardown, } from './test_helpers/setup_teardown.js'; +import { + assertEventFired, + createChangeListenerSpy, +} from './test_helpers/events.js'; suite('Clipboard', function () { setup(function () { this.clock = sharedTestSetup.call(this, {fireEventsNow: false}).clock; - this.workspace = new Blockly.WorkspaceSvg(new Blockly.Options({})); + this.workspace = Blockly.inject('blocklyDiv'); }); teardown(function () { @@ -32,4 +36,101 @@ suite('Clipboard', function () { Blockly.clipboard.registry.unregister('test-paster'); }); + + suite('pasting blocks', function () { + test('pasting blocks fires a create event', function () { + const eventSpy = createChangeListenerSpy(this.workspace); + const block = Blockly.serialization.blocks.append( + { + 'type': 'controls_if', + 'id': 'blockId', + }, + this.workspace, + ); + const data = block.toCopyData(); + this.clock.runAll(); + eventSpy.resetHistory(); + + Blockly.clipboard.paste(data, this.workspace); + this.clock.runAll(); + + assertEventFired( + eventSpy, + Blockly.Events.BlockCreate, + {'recordUndo': true, 'type': Blockly.Events.BLOCK_CREATE}, + this.workspace.id, + ); + }); + + suite('pasted blocks are placed in unambiguous locations', function () { + test('pasted blocks are bumped to not overlap', function () { + const block = Blockly.serialization.blocks.append( + { + 'type': 'controls_if', + 'x': 38, + 'y': 13, + }, + this.workspace, + ); + const data = block.toCopyData(); + + const newBlock = Blockly.clipboard.paste(data, this.workspace); + chai.assert.deepEqual( + newBlock.getRelativeToSurfaceXY(), + new Blockly.utils.Coordinate(66, 69), + ); + }); + + test('pasted blocks are bumped to be outside the connection snap radius', function () { + Blockly.serialization.workspaces.load( + { + 'blocks': { + 'languageVersion': 0, + 'blocks': [ + { + 'type': 'controls_if', + 'id': 'sourceBlockId', + 'x': 38, + 'y': 13, + }, + { + 'type': 'logic_compare', + 'x': 113, + 'y': 63, + }, + ], + }, + }, + this.workspace, + ); + this.clock.runAll(); // Update the connection DB. + const data = this.workspace.getBlockById('sourceBlockId').toCopyData(); + + const newBlock = Blockly.clipboard.paste(data, this.workspace); + chai.assert.deepEqual( + newBlock.getRelativeToSurfaceXY(), + new Blockly.utils.Coordinate(94, 125), + ); + }); + }); + }); + + suite('pasting comments', function () { + test('pasted comments are bumped to not overlap', function () { + Blockly.Xml.domToWorkspace( + Blockly.utils.xml.textToDom( + '', + ), + this.workspace, + ); + const comment = this.workspace.getTopComments(false)[0]; + const data = comment.toCopyData(); + + const newComment = Blockly.clipboard.paste(data, this.workspace); + chai.assert.deepEqual( + newComment.getRelativeToSurfaceXY(), + new Blockly.utils.Coordinate(60, 60), + ); + }); + }); }); diff --git a/tests/mocha/test_helpers/events.js b/tests/mocha/test_helpers/events.js index 46ce32830..ff0b5fb3c 100644 --- a/tests/mocha/test_helpers/events.js +++ b/tests/mocha/test_helpers/events.js @@ -149,13 +149,10 @@ export function assertEventFired( expectedWorkspaceId, expectedBlockId, ) { - expectedProperties = Object.assign( - { - workspaceId: expectedWorkspaceId, - blockId: expectedBlockId, - }, - expectedProperties, - ); + const baseProps = {}; + if (expectedWorkspaceId) baseProps.workspaceId = expectedWorkspaceId; + if (expectedBlockId) baseProps.blockId = expectedBlockId; + expectedProperties = Object.assign(baseProps, expectedProperties); const expectedEvent = sinon.match .instanceOf(instanceType) .and(sinon.match(expectedProperties)); From 7bca438ab8948ebdf39cf323b3876cbb9eea3255 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Thu, 10 Aug 2023 13:40:08 -0700 Subject: [PATCH 06/92] Revert "fix: removed X & Y from toolbox.ts and replaced movBy to moveTo (#7333)" (#7375) This reverts commit dbe926db4af5d8b1e722b1f0c1869d72ff24968b. The reverted commit made it so that RTL flyouts were rendered incorrectly. --- core/flyout_horizontal.ts | 5 ++--- core/flyout_vertical.ts | 8 +++----- core/utils/toolbox.ts | 2 ++ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/core/flyout_horizontal.ts b/core/flyout_horizontal.ts index 019570493..ad27ca1dc 100644 --- a/core/flyout_horizontal.ts +++ b/core/flyout_horizontal.ts @@ -19,7 +19,7 @@ import type {FlyoutButton} from './flyout_button.js'; import type {Options} from './options.js'; import * as registry from './registry.js'; import {Scrollbar} from './scrollbar.js'; -import {Coordinate} from './utils/coordinate.js'; +import type {Coordinate} from './utils/coordinate.js'; import {Rect} from './utils/rect.js'; import * as toolbox from './utils/toolbox.js'; import * as WidgetDiv from './widgetdiv.js'; @@ -285,8 +285,7 @@ export class HorizontalFlyout extends Flyout { } else { moveX = cursorX - tab; } - // No 'reason' provided since events are disabled. - block!.moveTo(new Coordinate(moveX, cursorY)); + block!.moveBy(moveX, cursorY); const rect = this.createRect_(block!, moveX, cursorY, blockHW, i); cursorX += blockHW.width + gaps[i]; diff --git a/core/flyout_vertical.ts b/core/flyout_vertical.ts index 9dac14d73..d0c4a5d58 100644 --- a/core/flyout_vertical.ts +++ b/core/flyout_vertical.ts @@ -19,7 +19,7 @@ import type {FlyoutButton} from './flyout_button.js'; import type {Options} from './options.js'; import * as registry from './registry.js'; import {Scrollbar} from './scrollbar.js'; -import {Coordinate} from './utils/coordinate.js'; +import type {Coordinate} from './utils/coordinate.js'; import {Rect} from './utils/rect.js'; import * as toolbox from './utils/toolbox.js'; import * as WidgetDiv from './widgetdiv.js'; @@ -246,8 +246,7 @@ export class VerticalFlyout extends Flyout { const moveX = block!.outputConnection ? cursorX - this.tabWidth_ : cursorX; - // No 'reason' provided since events are disabled. - block!.moveTo(new Coordinate(moveX, cursorY)); + block!.moveBy(moveX, cursorY); const rect = this.createRect_( block!, @@ -358,8 +357,7 @@ export class VerticalFlyout extends Flyout { if (!block.outputConnection) { newX -= this.tabWidth_; } - // No 'reason' provided since events are disabled. - block.moveTo(new Coordinate(newX - oldX, 0)); + block.moveBy(newX - oldX, 0); } if (this.rectMap_.has(block)) { this.moveRectToBlock_(this.rectMap_.get(block)!, block); diff --git a/core/utils/toolbox.ts b/core/utils/toolbox.ts index c192d4079..b2d71e3b6 100644 --- a/core/utils/toolbox.ts +++ b/core/utils/toolbox.ts @@ -24,6 +24,8 @@ export interface BlockInfo { disabled?: string | boolean; enabled?: boolean; id?: string; + x?: number; + y?: number; collapsed?: boolean; inline?: boolean; data?: string; From 8b6c780c3e01531a1d10914537132717cb770a23 Mon Sep 17 00:00:00 2001 From: Maribeth Bottorff Date: Thu, 10 Aug 2023 17:01:12 -0700 Subject: [PATCH 07/92] fix: remove specific warning text (#7376) * fix: remove specific warning text * chore: teardown rendered workspace after test * chore: reformat new tests --- core/block_svg.ts | 11 +++-- tests/mocha/block_test.js | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/core/block_svg.ts b/core/block_svg.ts index 81dd63ded..cb319409b 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -896,11 +896,10 @@ export class BlockSvg * Set this block's warning text. * * @param text The text, or null to delete. - * @param opt_id An optional ID for the warning text to be able to maintain + * @param id An optional ID for the warning text to be able to maintain * multiple warnings. */ - override setWarningText(text: string | null, opt_id?: string) { - const id = opt_id || ''; + override setWarningText(text: string | null, id: string = '') { if (!id) { // Kill all previous pending processes, this edit supersedes them all. for (const timeout of this.warningTextDb.values()) { @@ -931,8 +930,9 @@ export class BlockSvg } const icon = this.getIcon(WarningIcon.TYPE) as WarningIcon | undefined; - if (typeof text === 'string') { + if (text) { // Bubble up to add a warning on top-most collapsed block. + // TODO(#6020): This warning is never removed. let parent = this.getSurroundParent(); let collapsedParent = null; while (parent) { @@ -958,6 +958,9 @@ export class BlockSvg if (!id) { this.removeIcon(WarningIcon.TYPE); } else { + // Remove just this warning id's message. + icon.addMessage('', id); + // Then remove the entire icon if there is no longer any text. if (!icon.getText()) this.removeIcon(WarningIcon.TYPE); } } diff --git a/tests/mocha/block_test.js b/tests/mocha/block_test.js index 3d038e608..f32d04a1c 100644 --- a/tests/mocha/block_test.js +++ b/tests/mocha/block_test.js @@ -1612,6 +1612,98 @@ suite('Blocks', function () { }); }); + suite('Warning icons', function () { + setup(function () { + this.workspace = Blockly.inject('blocklyDiv'); + + this.block = this.workspace.newBlock('stack_block'); + this.block.initSvg(); + this.block.render(); + }); + + teardown(function () { + workspaceTeardown.call(this, this.workspace); + }); + + test('Block with no warning text does not have warning icon', function () { + const icon = this.block.getIcon(Blockly.icons.WarningIcon.TYPE); + + chai.assert.isUndefined( + icon, + 'Block with no warning should not have warning icon', + ); + }); + + test('Set warning text creates new icon if none existed', function () { + const text = 'Warning Text'; + + this.block.setWarningText(text); + + const icon = this.block.getIcon(Blockly.icons.WarningIcon.TYPE); + chai.assert.equal( + icon.getText(), + text, + 'Expected warning icon text to be set', + ); + }); + + test('Set warning text adds text to existing icon if needed', function () { + const text1 = 'Warning Text 1'; + const text2 = 'Warning Text 2'; + + this.block.setWarningText(text1, '1'); + this.block.setWarningText(text2, '2'); + + const icon = this.block.getIcon(Blockly.icons.WarningIcon.TYPE); + chai.assert.equal(icon.getText(), `${text1}\n${text2}`); + }); + + test('Clearing all warning text deletes the warning icon', function () { + const text = 'Warning Text'; + this.block.setWarningText(text); + + this.block.setWarningText(null); + + const icon = this.block.getIcon(Blockly.icons.WarningIcon.TYPE); + chai.assert.isUndefined( + icon, + 'Expected warning icon to be undefined after deleting all warning text', + ); + }); + + test('Clearing specific warning does not delete the icon if other warnings present', function () { + const text1 = 'Warning Text 1'; + const text2 = 'Warning Text 2'; + + this.block.setWarningText(text1, '1'); + this.block.setWarningText(text2, '2'); + this.block.setWarningText(null, '1'); + + const icon = this.block.getIcon(Blockly.icons.WarningIcon.TYPE); + chai.assert.equal( + icon.getText(), + text2, + 'Expected first warning text to be deleted', + ); + }); + + test('Clearing specific warning removes icon if it was only warning present', function () { + const text1 = 'Warning Text 1'; + const text2 = 'Warning Text 2'; + + this.block.setWarningText(text1, '1'); + this.block.setWarningText(text2, '2'); + this.block.setWarningText(null, '1'); + this.block.setWarningText(null, '2'); + + const icon = this.block.getIcon(Blockly.icons.WarningIcon.TYPE); + chai.assert.isUndefined( + icon, + 'Expected warning icon to be deleted after all warning text is cleared', + ); + }); + }); + suite('Bubbles and collapsing', function () { setup(function () { this.workspace = Blockly.inject('blocklyDiv'); From e30c4acd92b9b8512bc5ca0c7772b04397a206e4 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 11 Aug 2023 09:38:50 -0700 Subject: [PATCH 08/92] chore: upgrade keyboard shortcuts and context menus to use non-deprecated APIs (#7352) * chore: remove references to clipboard.copy in shortcuts * chore: remove references to clipboard.copy in context menus * chore: fix tests * chore: format * fix: PR comments --- core/contextmenu.ts | 4 +++- core/contextmenu_items.ts | 7 ++++--- core/shortcut_items.ts | 25 +++++++++++++++---------- core/shortcut_registry.ts | 8 ++++---- tests/mocha/contextmenu_items_test.js | 12 ++++++------ tests/mocha/keydown_test.js | 9 ++++++--- 6 files changed, 38 insertions(+), 27 deletions(-) diff --git a/core/contextmenu.ts b/core/contextmenu.ts index b7e107e01..abc729470 100644 --- a/core/contextmenu.ts +++ b/core/contextmenu.ts @@ -297,7 +297,9 @@ export function commentDuplicateOption( text: Msg['DUPLICATE_COMMENT'], enabled: true, callback: function () { - clipboard.duplicate(comment); + const data = comment.toCopyData(); + if (!data) return; + clipboard.paste(data, comment.workspace); }, }; return duplicateOption; diff --git a/core/contextmenu_items.ts b/core/contextmenu_items.ts index aedd72614..c784e9513 100644 --- a/core/contextmenu_items.ts +++ b/core/contextmenu_items.ts @@ -331,9 +331,10 @@ export function registerDuplicate() { return 'hidden'; }, callback(scope: Scope) { - if (scope.block) { - clipboard.duplicate(scope.block); - } + if (!scope.block) return; + const data = scope.block.toCopyData(); + if (!data) return; + clipboard.paste(data, scope.block.workspace); }, scopeType: ContextMenuRegistry.ScopeType.BLOCK, id: 'blockDuplicate', diff --git a/core/shortcut_items.ts b/core/shortcut_items.ts index 4b840fce5..940dea3f9 100644 --- a/core/shortcut_items.ts +++ b/core/shortcut_items.ts @@ -11,7 +11,7 @@ import {BlockSvg} from './block_svg.js'; import * as clipboard from './clipboard.js'; import * as common from './common.js'; import {Gesture} from './gesture.js'; -import {isCopyable} from './interfaces/i_copyable.js'; +import {ICopyData, isCopyable} from './interfaces/i_copyable.js'; import {KeyboardShortcut, ShortcutRegistry} from './shortcut_registry.js'; import {KeyCodes} from './utils/keycodes.js'; import type {WorkspaceSvg} from './workspace_svg.js'; @@ -81,6 +81,9 @@ export function registerDelete() { ShortcutRegistry.registry.register(deleteShortcut); } +let copyData: ICopyData | null = null; +let copyWorkspace: WorkspaceSvg | null = null; + /** * Keyboard shortcut to copy a block on ctrl+c, cmd+c, or alt+c. */ @@ -104,20 +107,20 @@ export function registerCopy() { !Gesture.inProgress() && selected != null && selected.isDeletable() && - selected.isMovable() + selected.isMovable() && + isCopyable(selected) ); }, callback(workspace, e) { // Prevent the default copy behavior, which may beep or otherwise indicate // an error due to the lack of a selection. e.preventDefault(); - // AnyDuringMigration because: Property 'hideChaff' does not exist on - // type 'Workspace'. - (workspace as AnyDuringMigration).hideChaff(); + workspace.hideChaff(); const selected = common.getSelected(); if (!selected || !isCopyable(selected)) return false; - clipboard.copy(selected); - return true; + copyData = selected.toCopyData(); + copyWorkspace = workspace; + return !!copyData; }, keyCodes: [ctrlC, altC, metaC], }; @@ -152,10 +155,11 @@ export function registerCut() { !selected.workspace!.isFlyout ); }, - callback() { + callback(workspace) { const selected = common.getSelected(); if (!selected || !isCopyable(selected)) return false; - clipboard.copy(selected); + copyData = selected.toCopyData(); + copyWorkspace = workspace; (selected as BlockSvg).checkAndDelete(); return true; }, @@ -185,7 +189,8 @@ export function registerPaste() { return !workspace.options.readOnly && !Gesture.inProgress(); }, callback() { - return !!clipboard.paste(); + if (!copyData || !copyWorkspace) return false; + return !!clipboard.paste(copyData, copyWorkspace); }, keyCodes: [ctrlV, altV, metaV], }; diff --git a/core/shortcut_registry.ts b/core/shortcut_registry.ts index e4d157495..d6442f51d 100644 --- a/core/shortcut_registry.ts +++ b/core/shortcut_registry.ts @@ -15,7 +15,7 @@ goog.declareModuleId('Blockly.ShortcutRegistry'); import {KeyCodes} from './utils/keycodes.js'; import * as object from './utils/object.js'; -import type {Workspace} from './workspace.js'; +import {WorkspaceSvg} from './workspace_svg.js'; /** * Class for the registry of keyboard shortcuts. This is intended to be a @@ -224,7 +224,7 @@ export class ShortcutRegistry { * @param e The key down event. * @returns True if the event was handled, false otherwise. */ - onKeyDown(workspace: Workspace, e: KeyboardEvent): boolean { + onKeyDown(workspace: WorkspaceSvg, e: KeyboardEvent): boolean { const key = this.serializeKeyEvent_(e); const shortcutNames = this.getShortcutNamesByKeyCode(key); if (!shortcutNames) { @@ -346,9 +346,9 @@ export class ShortcutRegistry { export namespace ShortcutRegistry { export interface KeyboardShortcut { - callback?: (p1: Workspace, p2: Event, p3: KeyboardShortcut) => boolean; + callback?: (p1: WorkspaceSvg, p2: Event, p3: KeyboardShortcut) => boolean; name: string; - preconditionFn?: (p1: Workspace) => boolean; + preconditionFn?: (p1: WorkspaceSvg) => boolean; metadata?: object; keyCodes?: (number | string)[]; allowCollision?: boolean; diff --git a/tests/mocha/contextmenu_items_test.js b/tests/mocha/contextmenu_items_test.js index 4e18a6801..a9d1a5009 100644 --- a/tests/mocha/contextmenu_items_test.js +++ b/tests/mocha/contextmenu_items_test.js @@ -419,13 +419,13 @@ suite('Context Menu Items', function () { ); }); - test('Calls duplicate', function () { - const spy = sinon.spy(Blockly.clipboard.TEST_ONLY, 'duplicateInternal'); - + test('the block is duplicated', function () { this.duplicateOption.callback(this.scope); - - sinon.assert.calledOnce(spy); - sinon.assert.calledWith(spy, this.block); + chai.assert.equal( + this.workspace.getTopBlocks(false).length, + 2, + 'Expected a second block', + ); }); test('Has correct label', function () { diff --git a/tests/mocha/keydown_test.js b/tests/mocha/keydown_test.js index e33f3c39d..f0f94310d 100644 --- a/tests/mocha/keydown_test.js +++ b/tests/mocha/keydown_test.js @@ -26,10 +26,13 @@ suite('Key Down', function () { /** * Creates a block and sets it as Blockly.selected. * @param {Blockly.Workspace} workspace The workspace to create a new block on. + * @return {Blockly.Block} The block being selected. */ function setSelectedBlock(workspace) { defineStackBlock(); - Blockly.common.setSelected(workspace.newBlock('stack_block')); + const block = workspace.newBlock('stack_block'); + Blockly.common.setSelected(block); + return block; } /** @@ -109,8 +112,8 @@ suite('Key Down', function () { suite('Copy', function () { setup(function () { - setSelectedBlock(this.workspace); - this.copySpy = sinon.spy(Blockly.clipboard.TEST_ONLY, 'copyInternal'); + this.block = setSelectedBlock(this.workspace); + this.copySpy = sinon.spy(this.block, 'toCopyData'); this.hideChaffSpy = sinon.spy( Blockly.WorkspaceSvg.prototype, 'hideChaff', From be2a6bb41d9528ad6d1cb826116d4b9cfa9f9da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Devansh=20Varshney=20=28=E0=A4=A6=E0=A5=87=E0=A4=B5?= =?UTF-8?q?=E0=A4=BE=E0=A4=82=E0=A4=B6=20=E0=A4=B5=E0=A4=BE=E0=A4=B0?= =?UTF-8?q?=E0=A5=8D=E0=A4=B7=E0=A5=8D=E0=A4=A3=E0=A5=87=E0=A4=AF=29?= Date: Fri, 11 Aug 2023 23:07:31 +0530 Subject: [PATCH 09/92] feat: Export shape interfaces from ConstantProvider (#7373) * feat: Export shape interfaces from ConstantProvider * feat: removed import/export ConstantProvider --- core/renderers/common/block_rendering.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/renderers/common/block_rendering.ts b/core/renderers/common/block_rendering.ts index 09eef3be0..400a68306 100644 --- a/core/renderers/common/block_rendering.ts +++ b/core/renderers/common/block_rendering.ts @@ -32,7 +32,6 @@ import {StatementInput} from '../measurables/statement_input.js'; import {TopRow} from '../measurables/top_row.js'; import {Types} from '../measurables/types.js'; -import {ConstantProvider} from './constants.js'; import {Drawer} from './drawer.js'; import type {IPathObject} from './i_path_object.js'; import {RenderInfo} from './info.js'; @@ -82,7 +81,6 @@ export function init( } export {BottomRow}; export {Connection}; -export {ConstantProvider}; export {Drawer}; export {ExternalValueInput}; export {Field}; @@ -109,3 +107,15 @@ export {SquareCorner}; export {StatementInput}; export {TopRow}; export {Types}; + +export { + OutsideCorners, + InsideCorners, + StartHat, + Notch, + PuzzleTab, + JaggedTeeth, + BaseShape, + DynamicShape, + ConstantProvider, +} from './constants.js'; From f246adbd2620b8fbb11514f60a1e2330008454a2 Mon Sep 17 00:00:00 2001 From: John Nesky Date: Fri, 11 Aug 2023 12:41:49 -0700 Subject: [PATCH 10/92] feat: Parse newlines in JSON message as row separators. (#6944) * feat: Parse message newlines as endOfRow dummies. * Fix the multilineinput field test. * Addressing PR feedback. * Addressing PR feedback. * Newline parsing now uses a new custom input. * npm run format * Added input_end_row to block factory. * Addres feedback, fix endrow after external value. --- core/block.ts | 67 +++++++++++++----- core/inputs.ts | 11 ++- core/inputs/end_row_input.ts | 31 +++++++++ core/inputs/input_types.ts | 4 ++ core/renderers/common/info.ts | 21 ++++-- core/renderers/geras/info.ts | 13 ++-- core/renderers/measurables/row.ts | 2 +- core/renderers/zelos/info.ts | 20 ++++-- core/utils/parsing.ts | 40 ++++++++--- core/xml.ts | 2 +- .../block_definition_extractor.js | 12 ++-- demos/blockfactory/blocks.js | 25 ++++++- demos/blockfactory/factory_utils.js | 11 +-- demos/blockfactory/index.html | 1 + tests/mocha/block_json_test.js | 68 +++++++++++++++++++ tests/mocha/block_test.js | 22 ++++++ tests/mocha/utils_test.js | 15 ++++ 17 files changed, 310 insertions(+), 55 deletions(-) create mode 100644 core/inputs/end_row_input.ts diff --git a/core/block.ts b/core/block.ts index 3e279f798..ea85edaf8 100644 --- a/core/block.ts +++ b/core/block.ts @@ -48,6 +48,7 @@ import {Size} from './utils/size.js'; import type {VariableModel} from './variable_model.js'; import type {Workspace} from './workspace.js'; import {DummyInput} from './inputs/dummy_input.js'; +import {EndRowInput} from './inputs/end_row_input.js'; import {ValueInput} from './inputs/value_input.js'; import {StatementInput} from './inputs/statement_input.js'; import {IconType} from './icons/icon_types.js'; @@ -1339,6 +1340,12 @@ export class Block implements IASTNodeLocation, IDeletable { return true; } } + for (let i = 0; i < this.inputList.length; i++) { + if (this.inputList[i] instanceof EndRowInput) { + // A row-end input is present. Inline value inputs. + return true; + } + } return false; } @@ -1560,6 +1567,17 @@ export class Block implements IASTNodeLocation, IDeletable { return this.appendInput(new DummyInput(name, this)); } + /** + * Appends an input that ends the row. + * + * @param name Optional language-neutral identifier which may used to find + * this input again. Should be unique to this block. + * @returns The input object created. + */ + appendEndRowInput(name = ''): Input { + return this.appendInput(new EndRowInput(name, this)); + } + /** * Appends the given input row. * @@ -1628,7 +1646,8 @@ export class Block implements IASTNodeLocation, IDeletable { this.interpolate_( json['message' + i], json['args' + i] || [], - json['lastDummyAlign' + i], + // Backwards compatibility: lastDummyAlign aliases implicitAlign. + json['implicitAlign' + i] || json['lastDummyAlign' + i], warningPrefix, ); i++; @@ -1765,19 +1784,19 @@ export class Block implements IASTNodeLocation, IDeletable { * @param message Text contains interpolation tokens (%1, %2, ...) that match * with fields or inputs defined in the args array. * @param args Array of arguments to be interpolated. - * @param lastDummyAlign If a dummy input is added at the end, how should it - * be aligned? + * @param implicitAlign If an implicit input is added at the end or in place + * of newline tokens, how should it be aligned? * @param warningPrefix Warning prefix string identifying block. */ private interpolate_( message: string, args: AnyDuringMigration[], - lastDummyAlign: string | undefined, + implicitAlign: string | undefined, warningPrefix: string, ) { const tokens = parsing.tokenizeInterpolation(message); this.validateTokens_(tokens, args.length); - const elements = this.interpolateArguments_(tokens, args, lastDummyAlign); + const elements = this.interpolateArguments_(tokens, args, implicitAlign); // An array of [field, fieldName] tuples. const fieldStack = []; @@ -1855,19 +1874,20 @@ export class Block implements IASTNodeLocation, IDeletable { /** * Inserts args in place of numerical tokens. String args are converted to - * JSON that defines a label field. If necessary an extra dummy input is added - * to the end of the elements. + * JSON that defines a label field. Newline characters are converted to + * end-row inputs, and if necessary an extra dummy input is added to the end + * of the elements. * * @param tokens The tokens to interpolate * @param args The arguments to insert. - * @param lastDummyAlign The alignment the added dummy input should have, if - * we are required to add one. + * @param implicitAlign The alignment to use for any implicitly added end-row + * or dummy inputs, if necessary. * @returns The JSON definitions of field and inputs to add to the block. */ private interpolateArguments_( tokens: Array, args: Array, - lastDummyAlign: string | undefined, + implicitAlign: string | undefined, ): AnyDuringMigration[] { const elements = []; for (let i = 0; i < tokens.length; i++) { @@ -1877,11 +1897,20 @@ export class Block implements IASTNodeLocation, IDeletable { } // Args can be strings, which is why this isn't elseif. if (typeof element === 'string') { - // AnyDuringMigration because: Type '{ text: string; type: string; } | - // null' is not assignable to type 'string | number'. - element = this.stringToFieldJson_(element) as AnyDuringMigration; - if (!element) { - continue; + if (element === '\n') { + // Convert newline tokens to end-row inputs. + const newlineInput = {'type': 'input_end_row'}; + if (implicitAlign) { + (newlineInput as AnyDuringMigration)['align'] = implicitAlign; + } + element = newlineInput as AnyDuringMigration; + } else { + // AnyDuringMigration because: Type '{ text: string; type: string; } + // | null' is not assignable to type 'string | number'. + element = this.stringToFieldJson_(element) as AnyDuringMigration; + if (!element) { + continue; + } } } elements.push(element); @@ -1895,8 +1924,8 @@ export class Block implements IASTNodeLocation, IDeletable { ) ) { const dummyInput = {'type': 'input_dummy'}; - if (lastDummyAlign) { - (dummyInput as AnyDuringMigration)['align'] = lastDummyAlign; + if (implicitAlign) { + (dummyInput as AnyDuringMigration)['align'] = implicitAlign; } elements.push(dummyInput); } @@ -1960,6 +1989,9 @@ export class Block implements IASTNodeLocation, IDeletable { case 'input_dummy': input = this.appendDummyInput(element['name']); break; + case 'input_end_row': + input = this.appendEndRowInput(element['name']); + break; default: { input = this.appendInputFromRegistry(element['type'], element['name']); break; @@ -1998,6 +2030,7 @@ export class Block implements IASTNodeLocation, IDeletable { str === 'input_value' || str === 'input_statement' || str === 'input_dummy' || + str === 'input_end_row' || registry.hasItem(registry.Type.INPUT, str) ); } diff --git a/core/inputs.ts b/core/inputs.ts index 8bd23d790..4b7bfa897 100644 --- a/core/inputs.ts +++ b/core/inputs.ts @@ -7,8 +7,17 @@ import {Align} from './inputs/align.js'; import {Input} from './inputs/input.js'; import {DummyInput} from './inputs/dummy_input.js'; +import {EndRowInput} from './inputs/end_row_input.js'; import {StatementInput} from './inputs/statement_input.js'; import {ValueInput} from './inputs/value_input.js'; import {inputTypes} from './inputs/input_types.js'; -export {Align, Input, DummyInput, StatementInput, ValueInput, inputTypes}; +export { + Align, + Input, + DummyInput, + EndRowInput, + StatementInput, + ValueInput, + inputTypes, +}; diff --git a/core/inputs/end_row_input.ts b/core/inputs/end_row_input.ts new file mode 100644 index 000000000..58227a094 --- /dev/null +++ b/core/inputs/end_row_input.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type {Block} from '../block.js'; +import {Input} from './input.js'; +import {inputTypes} from './input_types.js'; + +/** + * Represents an input on a block that is always the last input in the row. Any + * following input will be rendered on the next row even if the block has inline + * inputs. Any newline character in a JSON block definition's message will + * automatically be parsed as an end-row input. + */ +export class EndRowInput extends Input { + readonly type = inputTypes.END_ROW; + + /** + * @param name Language-neutral identifier which may used to find this input + * again. + * @param block The block containing this input. + */ + constructor( + public name: string, + block: Block, + ) { + super(name, block); + } +} diff --git a/core/inputs/input_types.ts b/core/inputs/input_types.ts index ff768deb2..a51537be7 100644 --- a/core/inputs/input_types.ts +++ b/core/inputs/input_types.ts @@ -21,4 +21,8 @@ export enum inputTypes { DUMMY = 5, // An unknown type of input defined by an external developer. CUSTOM = 6, + // An input with no connections that is always the last input of a row. Any + // subsequent input will be rendered on the next row. Any newline character in + // a JSON block definition's message will be parsed as an end-row input. + END_ROW = 7, } diff --git a/core/renderers/common/info.ts b/core/renderers/common/info.ts index 6818c608c..c035d0e52 100644 --- a/core/renderers/common/info.ts +++ b/core/renderers/common/info.ts @@ -14,6 +14,7 @@ import type {RenderedConnection} from '../../rendered_connection.js'; import type {Measurable} from '../measurables/base.js'; import {BottomRow} from '../measurables/bottom_row.js'; import {DummyInput} from '../../inputs/dummy_input.js'; +import {EndRowInput} from '../../inputs/end_row_input.js'; import {ExternalValueInput} from '../measurables/external_value_input.js'; import {Field} from '../measurables/field.js'; import {Hat} from '../measurables/hat.js'; @@ -326,9 +327,9 @@ export class RenderInfo { } else if (input instanceof ValueInput) { activeRow.elements.push(new ExternalValueInput(this.constants_, input)); activeRow.hasExternalInput = true; - } else if (input instanceof DummyInput) { - // Dummy inputs have no visual representation, but the information is - // still important. + } else if (input instanceof DummyInput || input instanceof EndRowInput) { + // Dummy and end-row inputs have no visual representation, but the + // information is still important. activeRow.minHeight = Math.max( activeRow.minHeight, input.getSourceBlock() && input.getSourceBlock()!.isShadow() @@ -355,6 +356,11 @@ export class RenderInfo { if (!lastInput) { return false; } + // If the previous input was an end-row input, then any following input + // should always be rendered on the next row. + if (lastInput instanceof EndRowInput) { + return true; + } // A statement input or an input following one always gets a new row. if ( input instanceof StatementInput || @@ -362,8 +368,13 @@ export class RenderInfo { ) { return true; } - // Value and dummy inputs get new row if inputs are not inlined. - if (input instanceof ValueInput || input instanceof DummyInput) { + // Value inputs, dummy inputs, and any input following an external value + // input get a new row if inputs are not inlined. + if ( + input instanceof ValueInput || + input instanceof DummyInput || + lastInput instanceof ValueInput + ) { return !this.isInline; } return false; diff --git a/core/renderers/geras/info.ts b/core/renderers/geras/info.ts index eb22bce09..b3a681bbf 100644 --- a/core/renderers/geras/info.ts +++ b/core/renderers/geras/info.ts @@ -13,6 +13,7 @@ import {RenderInfo as BaseRenderInfo} from '../common/info.js'; import type {Measurable} from '../measurables/base.js'; import type {BottomRow} from '../measurables/bottom_row.js'; import {DummyInput} from '../../inputs/dummy_input.js'; +import {EndRowInput} from '../../inputs/end_row_input.js'; import {ExternalValueInput} from '../measurables/external_value_input.js'; import type {Field} from '../measurables/field.js'; import {InRowSpacer} from '../measurables/in_row_spacer.js'; @@ -90,9 +91,9 @@ export class RenderInfo extends BaseRenderInfo { } else if (input instanceof ValueInput) { activeRow.elements.push(new ExternalValueInput(this.constants_, input)); activeRow.hasExternalInput = true; - } else if (input instanceof DummyInput) { - // Dummy inputs have no visual representation, but the information is - // still important. + } else if (input instanceof DummyInput || input instanceof EndRowInput) { + // Dummy and end-row inputs have no visual representation, but the + // information is still important. activeRow.minHeight = Math.max( activeRow.minHeight, this.constants_.DUMMY_INPUT_MIN_HEIGHT, @@ -379,8 +380,12 @@ export class RenderInfo extends BaseRenderInfo { row.width < prevInput.width ) { rowNextRightEdges.set(row, prevInput.width); - } else { + } else if (row.hasStatement) { nextRightEdge = row.width; + } else { + // To keep right edges of consecutive non-statement rows aligned, use + // the maximum width. + nextRightEdge = Math.max(nextRightEdge, row.width); } prevInput = row; } diff --git a/core/renderers/measurables/row.ts b/core/renderers/measurables/row.ts index c8116a2fc..e698688de 100644 --- a/core/renderers/measurables/row.ts +++ b/core/renderers/measurables/row.ts @@ -89,7 +89,7 @@ export class Row { hasInlineInput = false; /** - * Whether the row has any dummy inputs. + * Whether the row has any dummy inputs or end-row inputs. */ hasDummyInput = false; diff --git a/core/renderers/zelos/info.ts b/core/renderers/zelos/info.ts index d806fb0b6..e2d491c90 100644 --- a/core/renderers/zelos/info.ts +++ b/core/renderers/zelos/info.ts @@ -9,6 +9,7 @@ goog.declareModuleId('Blockly.zelos.RenderInfo'); import type {BlockSvg} from '../../block_svg.js'; import {DummyInput} from '../../inputs/dummy_input.js'; +import {EndRowInput} from '../../inputs/end_row_input.js'; import {FieldImage} from '../../field_image.js'; import {FieldLabel} from '../../field_label.js'; import {FieldTextInput} from '../../field_textinput.js'; @@ -124,6 +125,11 @@ export class RenderInfo extends BaseRenderInfo { if (!lastInput) { return false; } + // If the previous input was an end-row input, then any following input + // should always be rendered on the next row. + if (lastInput instanceof EndRowInput) { + return true; + } // A statement input or an input following one always gets a new row. if ( input instanceof StatementInput || @@ -131,8 +137,12 @@ export class RenderInfo extends BaseRenderInfo { ) { return true; } - // Value and dummy inputs get new row if inputs are not inlined. - if (input instanceof ValueInput || input instanceof DummyInput) { + // Value, dummy, and end-row inputs get new row if inputs are not inlined. + if ( + input instanceof ValueInput || + input instanceof DummyInput || + input instanceof EndRowInput + ) { return !this.isInline || this.isMultiRow; } return false; @@ -267,9 +277,9 @@ export class RenderInfo extends BaseRenderInfo { override addInput_(input: Input, activeRow: Row) { // If we have two dummy inputs on the same row, one aligned left and the // other right, keep track of the right aligned dummy input so we can add - // padding later. + // padding later. An end-row input after a dummy input also counts. if ( - input instanceof DummyInput && + (input instanceof DummyInput || input instanceof EndRowInput) && activeRow.hasDummyInput && activeRow.align === Align.LEFT && input.align === Align.RIGHT @@ -502,7 +512,7 @@ export class RenderInfo extends BaseRenderInfo { const connectionWidth = this.outputConnection.width; const outerShape = this.outputConnection.shape.type; const constants = this.constants_; - if (this.isMultiRow && this.inputRows.length > 1) { + if (this.inputRows.length > 1) { switch (outerShape) { case constants.SHAPES.ROUND: { // Special case for multi-row round reporter blocks. diff --git a/core/utils/parsing.ts b/core/utils/parsing.ts index ee6695db4..5d2c65dc1 100644 --- a/core/utils/parsing.ts +++ b/core/utils/parsing.ts @@ -17,13 +17,16 @@ import * as colourUtils from './colour.js'; * * @param message Text which might contain string table references and * interpolation tokens. - * @param parseInterpolationTokens Option to parse numeric - * interpolation tokens (%1, %2, ...) when true. + * @param parseInterpolationTokens Option to parse numeric interpolation + * tokens (%1, %2, ...) when true. + * @param tokenizeNewlines Split individual newline characters into separate + * tokens when true. * @returns Array of strings and numbers. */ function tokenizeInterpolationInternal( message: string, parseInterpolationTokens: boolean, + tokenizeNewlines: boolean, ): (string | number)[] { const tokens = []; const chars = message.split(''); @@ -47,6 +50,15 @@ function tokenizeInterpolationInternal( } buffer.length = 0; state = 1; + } else if (tokenizeNewlines && c === '\n') { + // Output newline characters as single-character tokens, to be replaced + // with endOfRow dummies during interpolation. + const text = buffer.join(''); + if (text) { + tokens.push(text); + } + buffer.length = 0; + tokens.push(c); } else { buffer.push(c); // Regular char. } @@ -108,6 +120,7 @@ function tokenizeInterpolationInternal( tokenizeInterpolationInternal( rawValue, parseInterpolationTokens, + tokenizeNewlines, ), ); } else if (parseInterpolationTokens) { @@ -137,11 +150,15 @@ function tokenizeInterpolationInternal( tokens.push(text); } - // Merge adjacent text tokens into a single string. + // Merge adjacent text tokens into a single string (but if newlines should be + // tokenized, don't merge those with adjacent text). const mergedTokens = []; buffer.length = 0; for (let i = 0; i < tokens.length; i++) { - if (typeof tokens[i] === 'string') { + if ( + typeof tokens[i] === 'string' && + !(tokenizeNewlines && tokens[i] === '\n') + ) { buffer.push(tokens[i] as string); } else { text = buffer.join(''); @@ -166,14 +183,15 @@ function tokenizeInterpolationInternal( * It will also replace string table references (e.g., %{bky_my_msg} and * %{BKY_MY_MSG} will both be replaced with the value in * Msg['MY_MSG']). Percentage sign characters '%' may be self-escaped - * (e.g., '%%'). + * (e.g., '%%'). Newline characters will also be output as string tokens + * containing a single newline character. * * @param message Text which might contain string table references and * interpolation tokens. * @returns Array of strings and numbers. */ export function tokenizeInterpolation(message: string): (string | number)[] { - return tokenizeInterpolationInternal(message, true); + return tokenizeInterpolationInternal(message, true, true); } /** @@ -189,9 +207,13 @@ export function replaceMessageReferences(message: string | any): string { if (typeof message !== 'string') { return message; } - const interpolatedResult = tokenizeInterpolationInternal(message, false); - // When parseInterpolationTokens === false, interpolatedResult should be at - // most length 1. + const interpolatedResult = tokenizeInterpolationInternal( + message, + false, + false, + ); + // When parseInterpolationTokens and tokenizeNewlines are false, + // interpolatedResult should be at most length 1. return interpolatedResult.length ? String(interpolatedResult[0]) : ''; } diff --git a/core/xml.ts b/core/xml.ts index b74e6e719..469f6cc45 100644 --- a/core/xml.ts +++ b/core/xml.ts @@ -213,7 +213,7 @@ export function blockToDom( const input = block.inputList[i]; let container: Element; let empty = true; - if (input.type === inputTypes.DUMMY) { + if (input.type === inputTypes.DUMMY || input.type === inputTypes.END_ROW) { continue; } else { const childBlock = input.connection!.targetBlock(); diff --git a/demos/blockfactory/block_definition_extractor.js b/demos/blockfactory/block_definition_extractor.js index 35186ac84..e6b1cedd6 100644 --- a/demos/blockfactory/block_definition_extractor.js +++ b/demos/blockfactory/block_definition_extractor.js @@ -287,14 +287,16 @@ BlockDefinitionExtractor.parseInputs_ = function(block) { * @private */ BlockDefinitionExtractor.input_ = function(input, align) { - var isDummy = (input.type === Blockly.DUMMY_INPUT); + var hasConnector = (input.type === Blockly.inputs.inputTypes.VALUE || input.type === Blockly.inputs.inputTypes.STATEMENT); var inputTypeAttr = - isDummy ? 'input_dummy' : - (input.type === Blockly.INPUT_VALUE) ? 'input_value' : 'input_statement'; + input.type === Blockly.inputs.inputTypes.DUMMY ? 'input_dummy' : + input.type === Blockly.inputs.inputTypes.END_ROW ? 'input_end_row' : + input.type === Blockly.inputs.inputTypes.VALUE ? 'input_value' : + 'input_statement'; var inputDefBlock = BlockDefinitionExtractor.newDomElement_('block', {type: inputTypeAttr}); - if (!isDummy) { + if (hasConnector) { inputDefBlock.append(BlockDefinitionExtractor.newDomElement_( 'field', {name: 'INPUTNAME'}, input.name)); } @@ -307,7 +309,7 @@ BlockDefinitionExtractor.input_ = function(input, align) { fieldsDef.append(fieldsXml); inputDefBlock.append(fieldsDef); - if (!isDummy) { + if (hasConnector) { var typeValue = BlockDefinitionExtractor.newDomElement_( 'value', {name: 'TYPE'}); typeValue.append( diff --git a/demos/blockfactory/blocks.js b/demos/blockfactory/blocks.js index 8927e6453..70691e264 100644 --- a/demos/blockfactory/blocks.js +++ b/demos/blockfactory/blocks.js @@ -220,14 +220,33 @@ Blockly.Blocks['input_dummy'] = { "previousStatement": "Input", "nextStatement": "Input", "colour": 210, - "tooltip": "For adding fields on a separate row with no " + - "connections. Alignment options (left, right, centre) " + - "apply only to multi-line fields.", + "tooltip": "For adding fields without any block connections." + + "Alignment options (left, right, centre) only affect " + + "multi-row blocks.", "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=293" }); } }; +Blockly.Blocks['input_end_row'] = { + // End-row input. + init: function() { + this.jsonInit({ + "message0": "end-row input", + "message1": FIELD_MESSAGE, + "args1": FIELD_ARGS, + "previousStatement": "Input", + "nextStatement": "Input", + "colour": 210, + "tooltip": "For adding fields without any block connections that will " + + "be rendered on a separate row from any following inputs. " + + "Alignment options (left, right, centre) only affect " + + "multi-row blocks.", + "helpUrl": "https://developers.google.com/blockly/guides/create-custom-blocks/define-blocks#block_inputs" + }); + } +}; + Blockly.Blocks['field_static'] = { // Text value. init: function() { diff --git a/demos/blockfactory/factory_utils.js b/demos/blockfactory/factory_utils.js index 0caf4b5aa..164b3357b 100644 --- a/demos/blockfactory/factory_utils.js +++ b/demos/blockfactory/factory_utils.js @@ -177,7 +177,8 @@ FactoryUtils.formatJson_ = function(blockType, rootBlock) { var input = {type: contentsBlock.type}; // Dummy inputs don't have names. Other inputs do. - if (contentsBlock.type !== 'input_dummy') { + if (contentsBlock.type !== 'input_dummy' && + contentsBlock.type !== 'input_end_row') { input.name = contentsBlock.getFieldValue('INPUTNAME'); } var check = JSON.parse( @@ -202,7 +203,7 @@ FactoryUtils.formatJson_ = function(blockType, rootBlock) { if (fields && FactoryUtils.getFieldsJson_(fields).join('').trim() !== '') { var align = lastInput.getFieldValue('ALIGN'); if (align !== 'LEFT') { - JS.lastDummyAlign0 = align; + JS.implicitAlign0 = align; } args.pop(); message.pop(); @@ -272,13 +273,15 @@ FactoryUtils.formatJavaScript_ = function(blockType, rootBlock, workspace) { // Generate inputs. var TYPES = {'input_value': 'appendValueInput', 'input_statement': 'appendStatementInput', - 'input_dummy': 'appendDummyInput'}; + 'input_dummy': 'appendDummyInput', + 'input_end_row': 'appendEndRowInput'}; var contentsBlock = rootBlock.getInputTargetBlock('INPUTS'); while (contentsBlock) { if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) { var name = ''; // Dummy inputs don't have names. Other inputs do. - if (contentsBlock.type !== 'input_dummy') { + if (contentsBlock.type !== 'input_dummy' && + contentsBlock.type !== 'input_end_row') { name = JSON.stringify(contentsBlock.getFieldValue('INPUTNAME')); } diff --git a/demos/blockfactory/index.html b/demos/blockfactory/index.html index 37c75236d..14d84d73f 100644 --- a/demos/blockfactory/index.html +++ b/demos/blockfactory/index.html @@ -422,6 +422,7 @@ + diff --git a/tests/mocha/block_json_test.js b/tests/mocha/block_json_test.js index 538995f0b..cd4337ef0 100644 --- a/tests/mocha/block_json_test.js +++ b/tests/mocha/block_json_test.js @@ -92,6 +92,10 @@ suite('Block JSON initialization', function () { 'Block "test": Message index %2 out of range.', ); }); + + test('Newline tokens are valid', function () { + this.assertNoError(['test', '\n', 'test'], 0); + }); }); suite('interpolateArguments_', function () { @@ -312,6 +316,70 @@ suite('Block JSON initialization', function () { }, ]); }); + + test('interpolation output includes end-row inputs', function () { + this.assertInterpolation( + ['test1', {'type': 'input_end_row'}, 'test2'], + [], + undefined, + [ + { + 'type': 'field_label', + 'text': 'test1', + }, + { + 'type': 'input_end_row', + }, + { + 'type': 'field_label', + 'text': 'test2', + }, + { + 'type': 'input_dummy', + }, + ], + ); + }); + + test('Newline is converted to end-row input', function () { + this.assertInterpolation(['test1', '\n', 'test2'], [], undefined, [ + { + 'type': 'field_label', + 'text': 'test1', + }, + { + 'type': 'input_end_row', + }, + { + 'type': 'field_label', + 'text': 'test2', + }, + { + 'type': 'input_dummy', + }, + ]); + }); + + test('Newline converted to end-row aligned like last dummy', function () { + this.assertInterpolation(['test1', '\n', 'test2'], [], 'CENTER', [ + { + 'type': 'field_label', + 'text': 'test1', + }, + { + 'type': 'input_end_row', + 'align': 'CENTER', + }, + { + 'type': 'field_label', + 'text': 'test2', + }, + { + 'type': 'input_dummy', + 'align': 'CENTER', + }, + ]); + }); }); suite('fieldFromJson_', function () { diff --git a/tests/mocha/block_test.js b/tests/mocha/block_test.js index f32d04a1c..7308ce607 100644 --- a/tests/mocha/block_test.js +++ b/tests/mocha/block_test.js @@ -10,6 +10,7 @@ import {ConnectionType} from '../../build/src/core/connection_type.js'; import {createDeprecationWarningStub} from './test_helpers/warnings.js'; import {createRenderedBlock} from './test_helpers/block_definitions.js'; import * as eventUtils from '../../build/src/core/events/utils.js'; +import {EndRowInput} from '../../build/src/core/inputs/end_row_input.js'; import { sharedTestSetup, sharedTestTeardown, @@ -2494,4 +2495,25 @@ suite('Blocks', function () { chai.assert.isTrue(initCalled, 'expected init function to be called'); }); }); + + suite('EndOfRow', function () { + setup(function () { + Blockly.defineBlocksWithJsonArray([ + { + 'type': 'end_row_test_block', + 'message0': 'Row1\nRow2', + 'inputsInline': true, + }, + ]); + }); + test('Newline is converted to an end-row input', function () { + const block = this.workspace.newBlock('end_row_test_block'); + chai.assert.equal(block.inputList[0].fieldRow[0].getValue(), 'Row1'); + chai.assert.isTrue( + block.inputList[0] instanceof EndRowInput, + 'newline should be converted to an end-row input', + ); + chai.assert.equal(block.inputList[1].fieldRow[0].getValue(), 'Row2'); + }); + }); }); diff --git a/tests/mocha/utils_test.js b/tests/mocha/utils_test.js index af5df8790..0d2b96a60 100644 --- a/tests/mocha/utils_test.js +++ b/tests/mocha/utils_test.js @@ -58,6 +58,13 @@ suite('Utils', function () { ['Hello%World'], ); }); + + test('Newlines are tokenized', function () { + chai.assert.deepEqual( + Blockly.utils.parsing.tokenizeInterpolation('Hello\nWorld'), + ['Hello', '\n', 'World'], + ); + }); }); suite('Number interpolation', function () { @@ -231,6 +238,14 @@ suite('Utils', function () { 'Unrecognized % escape code treated as literal', ); + resultString = + Blockly.utils.parsing.replaceMessageReferences('Hello\nWorld'); + chai.assert.equal( + resultString, + 'Hello\nWorld', + 'Newlines are not tokenized', + ); + resultString = Blockly.utils.parsing.replaceMessageReferences('%1'); chai.assert.equal(resultString, '%1', 'Interpolation tokens ignored.'); resultString = Blockly.utils.parsing.replaceMessageReferences('%1 %2'); From cd9b0c1f8387ba71d16a00596369279d59feec87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 08:34:44 -0700 Subject: [PATCH 11/92] chore(deps): Bump google-closure-compiler (#7389) Bumps [google-closure-compiler](https://github.com/google/closure-compiler-npm) from 20230502.0.0 to 20230802.0.0. - [Release notes](https://github.com/google/closure-compiler-npm/releases) - [Commits](https://github.com/google/closure-compiler-npm/compare/v20230502.0.0...v20230802.0.0) --- updated-dependencies: - dependency-name: google-closure-compiler dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 78 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index c80164dac..9f2f4a0c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-jsdoc": "^46.2.6", - "google-closure-compiler": "^20230502.0.0", + "google-closure-compiler": "^20230802.0.0", "google-closure-deps": "^20230502.0.0", "gulp": "^4.0.2", "gulp-concat": "^2.6.1", @@ -5205,13 +5205,13 @@ } }, "node_modules/google-closure-compiler": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20230502.0.0.tgz", - "integrity": "sha512-C2WZkuRnXpNjU2nc0W/Cgxm6t2VlwEyUJOTaGHaLr6qZCXK0L1uhOneKWN2X7AORKdzyLW6Tq8ONxRc7eODGJg==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20230802.0.0.tgz", + "integrity": "sha512-o2fYoc8lqOBdhm95Ick0vWrtwH2Icd5yLZhbTcQ0T7NfGiBepYvx1BB63hR8ebgzEZemz9Fh+O6Kg/3Mjm28ww==", "dev": true, "dependencies": { "chalk": "4.x", - "google-closure-compiler-java": "^20230502.0.0", + "google-closure-compiler-java": "^20230802.0.0", "minimist": "1.x", "vinyl": "2.x", "vinyl-sourcemaps-apply": "^0.2.0" @@ -5223,21 +5223,21 @@ "node": ">=10" }, "optionalDependencies": { - "google-closure-compiler-linux": "^20230502.0.0", - "google-closure-compiler-osx": "^20230502.0.0", - "google-closure-compiler-windows": "^20230502.0.0" + "google-closure-compiler-linux": "^20230802.0.0", + "google-closure-compiler-osx": "^20230802.0.0", + "google-closure-compiler-windows": "^20230802.0.0" } }, "node_modules/google-closure-compiler-java": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20230502.0.0.tgz", - "integrity": "sha512-2nMQPQz2ppU9jvHhz2zpUP5jBDAqZp4gFVOEvirEyfUuLLkHwAvU2Tl1c7xaKX+Z4uMxpxttxcwdIjQhV2g8eQ==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20230802.0.0.tgz", + "integrity": "sha512-PWKLMLwj7pR/U0yYbiy649LLqAscu+F1gyY4Y/jK6CmSLb8cIJbL8BTJd00828TzTNfWnYwxbkcQw0y9C2YsGw==", "dev": true }, "node_modules/google-closure-compiler-linux": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20230502.0.0.tgz", - "integrity": "sha512-4NDgPKJXQHUxEyJoVFPVMQPJs5at7ThOXa9u3+9UeYk2K+vtW5wVZlmW07VOy8Mk/O/n2dp+Vl+wuE35BIiHAA==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20230802.0.0.tgz", + "integrity": "sha512-F13U4iSXiWeGtHOFS25LVem1s6zI+pJvXVPVR7zSib5ppoUJ0JXnABJQezUR3FnpxmnkALG4oIGW0syH9zPLZA==", "cpu": [ "x32", "x64" @@ -5249,9 +5249,9 @@ ] }, "node_modules/google-closure-compiler-osx": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20230502.0.0.tgz", - "integrity": "sha512-jB13dcbu8O02cG3JcCCVZku1oI0ZirJc/Sr9xcGHY5MMyw3qEMlXb3IU97W6UXLcg2wCRawMWadOwL9K4L9lfQ==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20230802.0.0.tgz", + "integrity": "sha512-ANAi/ux92Tt+Na7vFDLeK2hRzotjC5j+nxoPtE0OcuNcbjji5dREKoJxkq7r0YwRTCzAFZszK5ip/NPdTOdCEg==", "cpu": [ "x32", "x64", @@ -5264,9 +5264,9 @@ ] }, "node_modules/google-closure-compiler-windows": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20230502.0.0.tgz", - "integrity": "sha512-wW5/liBxejvUViiBNo8/C9Vnhw+Lm+n3RdfE4spNkmdH9bcpKM+KQBLrPPakW17P3HbAPOPZ0L1RsrmyLYA5Cg==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20230802.0.0.tgz", + "integrity": "sha512-ZQPujoNiiUyTGl8zEGR/0yAygWnbMtX/NQ/S/EHVgq5nmYkvDEVuiVbgpPAmO9lzBTq0hvUTRRATZbTU2ISxgA==", "cpu": [ "x32", "x64" @@ -16043,45 +16043,45 @@ } }, "google-closure-compiler": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20230502.0.0.tgz", - "integrity": "sha512-C2WZkuRnXpNjU2nc0W/Cgxm6t2VlwEyUJOTaGHaLr6qZCXK0L1uhOneKWN2X7AORKdzyLW6Tq8ONxRc7eODGJg==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20230802.0.0.tgz", + "integrity": "sha512-o2fYoc8lqOBdhm95Ick0vWrtwH2Icd5yLZhbTcQ0T7NfGiBepYvx1BB63hR8ebgzEZemz9Fh+O6Kg/3Mjm28ww==", "dev": true, "requires": { "chalk": "4.x", - "google-closure-compiler-java": "^20230502.0.0", - "google-closure-compiler-linux": "^20230502.0.0", - "google-closure-compiler-osx": "^20230502.0.0", - "google-closure-compiler-windows": "^20230502.0.0", + "google-closure-compiler-java": "^20230802.0.0", + "google-closure-compiler-linux": "^20230802.0.0", + "google-closure-compiler-osx": "^20230802.0.0", + "google-closure-compiler-windows": "^20230802.0.0", "minimist": "1.x", "vinyl": "2.x", "vinyl-sourcemaps-apply": "^0.2.0" } }, "google-closure-compiler-java": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20230502.0.0.tgz", - "integrity": "sha512-2nMQPQz2ppU9jvHhz2zpUP5jBDAqZp4gFVOEvirEyfUuLLkHwAvU2Tl1c7xaKX+Z4uMxpxttxcwdIjQhV2g8eQ==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20230802.0.0.tgz", + "integrity": "sha512-PWKLMLwj7pR/U0yYbiy649LLqAscu+F1gyY4Y/jK6CmSLb8cIJbL8BTJd00828TzTNfWnYwxbkcQw0y9C2YsGw==", "dev": true }, "google-closure-compiler-linux": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20230502.0.0.tgz", - "integrity": "sha512-4NDgPKJXQHUxEyJoVFPVMQPJs5at7ThOXa9u3+9UeYk2K+vtW5wVZlmW07VOy8Mk/O/n2dp+Vl+wuE35BIiHAA==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20230802.0.0.tgz", + "integrity": "sha512-F13U4iSXiWeGtHOFS25LVem1s6zI+pJvXVPVR7zSib5ppoUJ0JXnABJQezUR3FnpxmnkALG4oIGW0syH9zPLZA==", "dev": true, "optional": true }, "google-closure-compiler-osx": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20230502.0.0.tgz", - "integrity": "sha512-jB13dcbu8O02cG3JcCCVZku1oI0ZirJc/Sr9xcGHY5MMyw3qEMlXb3IU97W6UXLcg2wCRawMWadOwL9K4L9lfQ==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20230802.0.0.tgz", + "integrity": "sha512-ANAi/ux92Tt+Na7vFDLeK2hRzotjC5j+nxoPtE0OcuNcbjji5dREKoJxkq7r0YwRTCzAFZszK5ip/NPdTOdCEg==", "dev": true, "optional": true }, "google-closure-compiler-windows": { - "version": "20230502.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20230502.0.0.tgz", - "integrity": "sha512-wW5/liBxejvUViiBNo8/C9Vnhw+Lm+n3RdfE4spNkmdH9bcpKM+KQBLrPPakW17P3HbAPOPZ0L1RsrmyLYA5Cg==", + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20230802.0.0.tgz", + "integrity": "sha512-ZQPujoNiiUyTGl8zEGR/0yAygWnbMtX/NQ/S/EHVgq5nmYkvDEVuiVbgpPAmO9lzBTq0hvUTRRATZbTU2ISxgA==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index d7649dfdf..0f51cc6b7 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-jsdoc": "^46.2.6", - "google-closure-compiler": "^20230502.0.0", + "google-closure-compiler": "^20230802.0.0", "google-closure-deps": "^20230502.0.0", "gulp": "^4.0.2", "gulp-concat": "^2.6.1", From 18ee0ec41b2a326255f3270166e60b565f75b975 Mon Sep 17 00:00:00 2001 From: Hollow Man Date: Mon, 14 Aug 2023 19:19:42 +0300 Subject: [PATCH 12/92] fix: insertion marker's next blocks become real block (#7384) Don't add next block to the insertion marker when we do an insertion marker json serialization. Also, to keep consistent with the old behavior, we don't need to add input blocks for the insertion marker. And we don't need to do a full serialization here as it will just become an insertion marker. Resolves #7383 Address issues in PR #7364 Signed-off-by: Hollow Man --- core/insertion_marker_manager.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/insertion_marker_manager.ts b/core/insertion_marker_manager.ts index 35e46dc01..effe78219 100644 --- a/core/insertion_marker_manager.ts +++ b/core/insertion_marker_manager.ts @@ -225,7 +225,12 @@ export class InsertionMarkerManager { eventUtils.disable(); let result: BlockSvg; try { - const blockJson = blocks.save(sourceBlock); + const blockJson = blocks.save(sourceBlock, { + addCoordinates: false, + addInputBlocks: false, + addNextBlocks: false, + doFullSerialization: false, + }); if (!blockJson) { throw new Error('Failed to serialize source block.'); } From 5b5a56586c21e0f6a4db0fd476170b3ab239de18 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Mon, 14 Aug 2023 22:47:19 +0100 Subject: [PATCH 13/92] refactor(tests): Introduce loading shims, use in playgrounds (#7380) * refactor(build): Simplify implementation of posixPath * fix(closure): Make safe to import in node.js Make the implementation declareModlueId safe to import and execute in node.js, which does not provide a window global. (N.B. because this is an ESM and therefore automatically strict, using window?.goog?.declareModuleId doesn't work because window being undefined is an early error.) * feat(tests): Introduce chunk loading shims - Add a buildShims task to build_tasks.js that, for each chunk, creates a correspondingly-named build/.mjs that will either (in uncompressed mode) import and reexport that chunk's entry point module (e.g. core/blockly.js) or (in compressed mode) load dist/_compressed.js using a - - - - - - - - - -

Shared Procedures Playground

- -
-
- - diff --git a/tests/scripts/load.mjs b/tests/scripts/load.mjs new file mode 100644 index 000000000..728a4fd2c --- /dev/null +++ b/tests/scripts/load.mjs @@ -0,0 +1,140 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Helper functions for loading Blockly in tests. + * + * Used by playgrounds and test harnesses, both directly and via the + * shims generated by the buildShims function in + * scripts/gulpfiles/build_tasks.js. + * + */ + +if (typeof window !== 'object') { + // Not running in a browser. Maybe we wish to support this? + // blockly_uncompressed formerly supported node.js, though it + // appears that the code had not been working for some time (at + // least since PR #5718 back in December 2021. For now just throw + // an error. + throw new Error('Bootstrapping without window is not supported.'); +} + +/** + * URL of the blockly repository. This is needed for a few reasons: + * + * - We need an absolute path instead of relative path because the + * advanced_playground and the regular playground are in + * different folders. + * - We need to get the root directory for blockly because it is + * different for github.io, appspot and local. + * + * The formulation given here will work so long as top-level page is loaded from + * somewhere in tests/. + */ +export const ROOT = window.location.href.replace(/\/tests\/.*$/, '/'); + +/** + * Decide whether to use compressed mode or not. + * + * Honours a "?compressed=" query parameter if present; otherwise uses + * uncompressed for when loading from local machine and compressed + * otherwise. See issue #5557 for additional background. + * + * @return {boolean} True if should load in compressed mode. + */ +function compressed() { + const param = location.search.match(/compressed=([^&]+)/)?.[1]; + if (param) { + if (['y', 'yes', 'true', '1'].includes(param)) return true; + if (['n', 'no', 'false', '0'].includes(param)) return false; + console.error(`Unrecognised compressed parameter "${param}"`); + } + + const LOCALHOSTS = ['localhost', '127.0.0.1', '[::1]']; + const isLocalhost = LOCALHOSTS.includes(location.hostname); + const isFileUrl = location.origin === 'file://'; + return !(isLocalhost || isFileUrl); +} + +/** @type {boolean} Load in compressed mode. */ +export const COMPRESSED = compressed(); + +/** + * Load a chunk, either by importing its ESM entrypoint or loading the + * compressed chunk script. + * + * When loading in uncompressed mode, if scriptExports is a simple + * variable name (e.g. 'Blockly') then globalThis[scriptExports] will + * be set to the the chunk's Module object. This attempts to provide + * backward compatibility with loading the compressed chunk as a + * script, where this is done by the compressed chunk's UMD wrapper. + * The compatibility is not complete, however: since Module objects + * are immutable, it is not possible to set (e.g.) + * Blockly.libraryBlocks. + * + * The intention is to allow the chunk to be accessed either via + * the returned Module / exports object (preferred) or via the global + * scope (needed by dev-tools) regardless of whether it was loaded + * compressed or uncompressed. + * + * Paths should be relative to the repository root. + * + * @param {string} modulePath Path to the chunk's ESM entry point. + * @param {string} scriptPath Path to the chunk's _compressed.js file. + * @param {string} scriptExports The global variable name (or + * dotted-identifier path relative from a global variable) in + * which the compressed chunk's UMD wrapper will save the module's + * exports object. Should be the same as the correspodning + * chunks[].scriptExport property in + * scripts/gulpfiles/build_tasks.js. + * @return {Module} The module's Module or exports object. + */ +export async function loadChunk(modulePath, scriptPath, scriptExports) { + if (COMPRESSED) { + await loadScript(`${ROOT}${scriptPath}`); + return get(scriptExports); + } else { + const exports = await import(`../../${modulePath}`); + if (!scriptExports.includes('.')) globalThis[scriptExports] = exports; + return exports; + } +} + +/** + * Load a given URL using a - + +
@@ -202,21 +219,5 @@ style="display: none"> - - diff --git a/tests/mocha/input_test.js b/tests/mocha/input_test.js index 0d7abe6bd..c4ecd7a39 100644 --- a/tests/mocha/input_test.js +++ b/tests/mocha/input_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.input'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/insertion_marker_manager_test.js b/tests/mocha/insertion_marker_manager_test.js index 1eeb8cac8..26996fa95 100644 --- a/tests/mocha/insertion_marker_manager_test.js +++ b/tests/mocha/insertion_marker_manager_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.insertionMarkerManager'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/insertion_marker_test.js b/tests/mocha/insertion_marker_test.js index b63452fae..c0b870845 100644 --- a/tests/mocha/insertion_marker_test.js +++ b/tests/mocha/insertion_marker_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.insertionMarker'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/jso_deserialization_test.js b/tests/mocha/jso_deserialization_test.js index cadcc1374..fa6590cb7 100644 --- a/tests/mocha/jso_deserialization_test.js +++ b/tests/mocha/jso_deserialization_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.jsoDeserialization'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/jso_serialization_test.js b/tests/mocha/jso_serialization_test.js index b8b28b170..500bb2011 100644 --- a/tests/mocha/jso_serialization_test.js +++ b/tests/mocha/jso_serialization_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.jsoSerialization'); - import * as Blockly from '../../build/src/core/blockly.js'; import { createGenUidStubWithReturns, diff --git a/tests/mocha/json_test.js b/tests/mocha/json_test.js index d8a139eed..b5896e5fa 100644 --- a/tests/mocha/json_test.js +++ b/tests/mocha/json_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.json'); - import { addMessageToCleanup, sharedTestSetup, diff --git a/tests/mocha/keydown_test.js b/tests/mocha/keydown_test.js index f0f94310d..0483d72c1 100644 --- a/tests/mocha/keydown_test.js +++ b/tests/mocha/keydown_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.keydown'); - import * as Blockly from '../../build/src/core/blockly.js'; import {createKeyDownEvent} from './test_helpers/user_input.js'; import {defineStackBlock} from './test_helpers/block_definitions.js'; diff --git a/tests/mocha/metrics_test.js b/tests/mocha/metrics_test.js index 7c551435f..83fa605c6 100644 --- a/tests/mocha/metrics_test.js +++ b/tests/mocha/metrics_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.metrics'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/mutator_test.js b/tests/mocha/mutator_test.js index 9a1c61698..609dc03a2 100644 --- a/tests/mocha/mutator_test.js +++ b/tests/mocha/mutator_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.mutator'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/names_test.js b/tests/mocha/names_test.js index 093e4e132..6cb4213df 100644 --- a/tests/mocha/names_test.js +++ b/tests/mocha/names_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.names'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/procedure_map_test.js b/tests/mocha/procedure_map_test.js index 2c739cc06..67d62b6ee 100644 --- a/tests/mocha/procedure_map_test.js +++ b/tests/mocha/procedure_map_test.js @@ -15,8 +15,6 @@ import { } from './test_helpers/events.js'; import {MockProcedureModel} from './test_helpers/procedures.js'; -goog.declareModuleId('Blockly.test.procedureMap'); - suite('Procedure Map', function () { setup(function () { sharedTestSetup.call(this); diff --git a/tests/mocha/registry_test.js b/tests/mocha/registry_test.js index ca7d3fca6..fd37c6e69 100644 --- a/tests/mocha/registry_test.js +++ b/tests/mocha/registry_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.registry'); - import {assertWarnings} from './test_helpers/warnings.js'; import { sharedTestSetup, diff --git a/tests/mocha/render_management_test.js b/tests/mocha/render_management_test.js index 1b880ec2c..240a9dd15 100644 --- a/tests/mocha/render_management_test.js +++ b/tests/mocha/render_management_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.renderManagement'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/serializer_test.js b/tests/mocha/serializer_test.js index 10cf210ba..bd9a47344 100644 --- a/tests/mocha/serializer_test.js +++ b/tests/mocha/serializer_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.serialization'); - import * as Blockly from '../../build/src/core/blockly.js'; import { TestCase, diff --git a/tests/mocha/shortcut_registry_test.js b/tests/mocha/shortcut_registry_test.js index 5c1cd7dcd..0f48bc2ab 100644 --- a/tests/mocha/shortcut_registry_test.js +++ b/tests/mocha/shortcut_registry_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.shortcutRegistry'); - import {createKeyDownEvent} from './test_helpers/user_input.js'; import { sharedTestSetup, diff --git a/tests/mocha/test_helpers/block_definitions.js b/tests/mocha/test_helpers/block_definitions.js index f7e3d4cad..26507b29c 100644 --- a/tests/mocha/test_helpers/block_definitions.js +++ b/tests/mocha/test_helpers/block_definitions.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.blockDefinitions'); - export function defineEmptyBlock(name = 'empty_block') { Blockly.defineBlocksWithJsonArray([ { diff --git a/tests/mocha/test_helpers/code_generation.js b/tests/mocha/test_helpers/code_generation.js index 072964a07..b65fb1e78 100644 --- a/tests/mocha/test_helpers/code_generation.js +++ b/tests/mocha/test_helpers/code_generation.js @@ -5,8 +5,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.codeGeneration'); - import {runTestSuites} from './common.js'; /** diff --git a/tests/mocha/test_helpers/common.js b/tests/mocha/test_helpers/common.js index c8dd325d6..3d79576dc 100644 --- a/tests/mocha/test_helpers/common.js +++ b/tests/mocha/test_helpers/common.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.common'); - /** * Test case configuration. * @record diff --git a/tests/mocha/test_helpers/events.js b/tests/mocha/test_helpers/events.js index ff0b5fb3c..5998ed94e 100644 --- a/tests/mocha/test_helpers/events.js +++ b/tests/mocha/test_helpers/events.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.events'); - /** * Creates spy for workspace fireChangeListener * @param {!Blockly.Workspace} workspace The workspace to spy fireChangeListener diff --git a/tests/mocha/test_helpers/fields.js b/tests/mocha/test_helpers/fields.js index 468a29bda..11b6e98ef 100644 --- a/tests/mocha/test_helpers/fields.js +++ b/tests/mocha/test_helpers/fields.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.fields'); - import {runTestCases, TestCase} from './common.js'; /** diff --git a/tests/mocha/test_helpers/procedures.js b/tests/mocha/test_helpers/procedures.js index 73f220eeb..e4ddc0e3f 100644 --- a/tests/mocha/test_helpers/procedures.js +++ b/tests/mocha/test_helpers/procedures.js @@ -3,7 +3,6 @@ * Copyright 2020 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.procedures'); import {ConnectionType} from '../../../build/src/core/connection_type.js'; import {VariableModel} from '../../../build/src/core/variable_model.js'; diff --git a/tests/mocha/test_helpers/serialization.js b/tests/mocha/test_helpers/serialization.js index 766992ab8..6b3be7ae2 100644 --- a/tests/mocha/test_helpers/serialization.js +++ b/tests/mocha/test_helpers/serialization.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.serialization'); - import {runTestCases} from './common.js'; /** @@ -112,25 +110,7 @@ export const runSerializationTestSuite = (testCases) => { }); suite('xml round-trip', function () { setup(function () { - // The genUid is undergoing change as part of the 2021Q3 - // goog.module migration: - // - // - It is being moved from Blockly.utils to - // Blockly.utils.idGenerator (which itself is being renamed - // from IdGenerator). - // - For compatibility with changes to the module system (from - // goog.provide to goog.module and in future to ES modules), - // .genUid is now a wrapper around .TEST_ONLY.genUid, which - // can be safely stubbed by sinon or other similar - // frameworks in a way that will continue to work. - if (Blockly.utils.idGenerator && Blockly.utils.idGenerator.TEST_ONLY) { - sinon - .stub(Blockly.utils.idGenerator.TEST_ONLY, 'genUid') - .returns('1'); - } else { - // Fall back to stubbing original version on Blockly.utils. - sinon.stub(Blockly.utils, 'genUid').returns('1'); - } + sinon.stub(Blockly.utils.idGenerator.TEST_ONLY, 'genUid').returns('1'); }); teardown(function () { diff --git a/tests/mocha/test_helpers/setup_teardown.js b/tests/mocha/test_helpers/setup_teardown.js index dc2dfaa09..2fc08cb69 100644 --- a/tests/mocha/test_helpers/setup_teardown.js +++ b/tests/mocha/test_helpers/setup_teardown.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.setupTeardown'); - import * as eventUtils from '../../../build/src/core/events/utils.js'; /** diff --git a/tests/mocha/test_helpers/toolbox_definitions.js b/tests/mocha/test_helpers/toolbox_definitions.js index 1a163831b..2f767ed60 100644 --- a/tests/mocha/test_helpers/toolbox_definitions.js +++ b/tests/mocha/test_helpers/toolbox_definitions.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.toolboxDefinitions'); - /** * Get JSON for a toolbox that contains categories. * @return {Blockly.utils.toolbox.ToolboxJson} The array holding information diff --git a/tests/mocha/test_helpers/user_input.js b/tests/mocha/test_helpers/user_input.js index 1405f25a2..6606a6652 100644 --- a/tests/mocha/test_helpers/user_input.js +++ b/tests/mocha/test_helpers/user_input.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.userInput'); - import {KeyCodes} from '../../../build/src/core/utils/keycodes.js'; /** diff --git a/tests/mocha/test_helpers/variables.js b/tests/mocha/test_helpers/variables.js index d1bc5eaa3..0890d58a8 100644 --- a/tests/mocha/test_helpers/variables.js +++ b/tests/mocha/test_helpers/variables.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.variables'); - /** * Check if a variable with the given values exists. * @param {Blockly.Workspace|Blockly.VariableMap} container The workspace or diff --git a/tests/mocha/test_helpers/warnings.js b/tests/mocha/test_helpers/warnings.js index e82486864..d7752e90c 100644 --- a/tests/mocha/test_helpers/warnings.js +++ b/tests/mocha/test_helpers/warnings.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.warnings'); - /** * Captures the strings sent to console.warn() when calling a function. * Copies from core. diff --git a/tests/mocha/test_helpers/workspace.js b/tests/mocha/test_helpers/workspace.js index bce02ee34..7654feba2 100644 --- a/tests/mocha/test_helpers/workspace.js +++ b/tests/mocha/test_helpers/workspace.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.helpers.workspace'); - import {assertVariableValues} from './variables.js'; import {assertWarnings} from './warnings.js'; import * as eventUtils from '../../../build/src/core/events/utils.js'; diff --git a/tests/mocha/theme_test.js b/tests/mocha/theme_test.js index b63c6f99f..bd9091a37 100644 --- a/tests/mocha/theme_test.js +++ b/tests/mocha/theme_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.theme'); - import {assertEventFired} from './test_helpers/events.js'; import * as eventUtils from '../../build/src/core/events/utils.js'; import { diff --git a/tests/mocha/toolbox_test.js b/tests/mocha/toolbox_test.js index 1f76e4cdf..01670a298 100644 --- a/tests/mocha/toolbox_test.js +++ b/tests/mocha/toolbox_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.toolbox'); - import {defineStackBlock} from './test_helpers/block_definitions.js'; import { sharedTestSetup, diff --git a/tests/mocha/tooltip_test.js b/tests/mocha/tooltip_test.js index d3b59acc7..9488697ab 100644 --- a/tests/mocha/tooltip_test.js +++ b/tests/mocha/tooltip_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.tooltip'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/touch_test.js b/tests/mocha/touch_test.js index 4b45d8a5f..94894b4eb 100644 --- a/tests/mocha/touch_test.js +++ b/tests/mocha/touch_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.touch'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/trashcan_test.js b/tests/mocha/trashcan_test.js index 3faee3793..fd622c26e 100644 --- a/tests/mocha/trashcan_test.js +++ b/tests/mocha/trashcan_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.trashcan'); - import {assertEventFired, assertEventNotFired} from './test_helpers/events.js'; import { sharedTestSetup, diff --git a/tests/mocha/utils_test.js b/tests/mocha/utils_test.js index 0d2b96a60..e87936585 100644 --- a/tests/mocha/utils_test.js +++ b/tests/mocha/utils_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.utils'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/variable_map_test.js b/tests/mocha/variable_map_test.js index 21e9af8fe..bb93c92d6 100644 --- a/tests/mocha/variable_map_test.js +++ b/tests/mocha/variable_map_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.variableMap'); - import {assertVariableValues} from './test_helpers/variables.js'; import { createGenUidStubWithReturns, diff --git a/tests/mocha/variable_model_test.js b/tests/mocha/variable_model_test.js index d328c388c..9888d6121 100644 --- a/tests/mocha/variable_model_test.js +++ b/tests/mocha/variable_model_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.variableModel'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/widget_div_test.js b/tests/mocha/widget_div_test.js index 354696ea6..5415347ad 100644 --- a/tests/mocha/widget_div_test.js +++ b/tests/mocha/widget_div_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.widgetDiv'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/workspace_comment_test.js b/tests/mocha/workspace_comment_test.js index e8a336052..a1b0e38b5 100644 --- a/tests/mocha/workspace_comment_test.js +++ b/tests/mocha/workspace_comment_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.workspaceComment'); - import { sharedTestSetup, sharedTestTeardown, diff --git a/tests/mocha/workspace_svg_test.js b/tests/mocha/workspace_svg_test.js index 9fe076904..cea58cdf9 100644 --- a/tests/mocha/workspace_svg_test.js +++ b/tests/mocha/workspace_svg_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.workspaceSvg'); - import { assertEventFired, assertEventNotFired, diff --git a/tests/mocha/workspace_test.js b/tests/mocha/workspace_test.js index 90d27242e..58829a77a 100644 --- a/tests/mocha/workspace_test.js +++ b/tests/mocha/workspace_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.workspace'); - import {assertVariableValues} from './test_helpers/variables.js'; import { sharedTestSetup, diff --git a/tests/mocha/xml_test.js b/tests/mocha/xml_test.js index 8fecbcf69..385b0f736 100644 --- a/tests/mocha/xml_test.js +++ b/tests/mocha/xml_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.xml'); - import { addBlockTypeToCleanup, createGenUidStubWithReturns, diff --git a/tests/mocha/zoom_controls_test.js b/tests/mocha/zoom_controls_test.js index ce66d3f99..5129c16c5 100644 --- a/tests/mocha/zoom_controls_test.js +++ b/tests/mocha/zoom_controls_test.js @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -goog.declareModuleId('Blockly.test.zoomControls'); - import {assertEventFired, assertEventNotFired} from './test_helpers/events.js'; import * as eventUtils from '../../build/src/core/events/utils.js'; import { From a38340bfdfd86a5097f5725633b38553e1117454 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 18 Aug 2023 17:13:32 -0700 Subject: [PATCH 29/92] chore(tests): set browser pause time in one place and enable toolbox tests (#7402) * chore(tests): add a PAUSE_TIME constant for all test pauses * chore(tests): respond to PR feedback --- tests/browser/test/basic_playground_test.js | 5 +- tests/browser/test/delete_blocks_test.js | 10 ++-- tests/browser/test/extensive_test.js | 5 +- tests/browser/test/field_edits_test.js | 3 +- tests/browser/test/mutator_test.js | 11 +++-- tests/browser/test/procedure_test.js | 3 +- tests/browser/test/test_setup.js | 15 ++++-- tests/browser/test/toolbox_drag_test.js | 55 ++++++++++----------- 8 files changed, 59 insertions(+), 48 deletions(-) diff --git a/tests/browser/test/basic_playground_test.js b/tests/browser/test/basic_playground_test.js index 7d1de3b63..965de4445 100644 --- a/tests/browser/test/basic_playground_test.js +++ b/tests/browser/test/basic_playground_test.js @@ -16,6 +16,7 @@ const { dragBlockTypeFromFlyout, connect, contextMenuSelect, + PAUSE_TIME, } = require('./test_setup'); async function getIsCollapsed(browser, blockId) { @@ -115,12 +116,12 @@ suite('Disabling', function () { setup(async function () { await this.browser.refresh(); // Pause to allow refresh time to work. - await this.browser.pause(200); + await this.browser.pause(PAUSE_TIME + 150); }); test( 'children connected to value inputs are disabled when the ' + - 'parent is diabled', + 'parent is disabled', async function () { const parent = await dragBlockTypeFromFlyout( this.browser, diff --git a/tests/browser/test/delete_blocks_test.js b/tests/browser/test/delete_blocks_test.js index eeaa8162d..261074a91 100644 --- a/tests/browser/test/delete_blocks_test.js +++ b/tests/browser/test/delete_blocks_test.js @@ -11,6 +11,7 @@ const { getAllBlocks, getBlockElementById, contextMenuSelect, + PAUSE_TIME, } = require('./test_setup'); const {Key} = require('webdriverio'); @@ -98,7 +99,6 @@ const startBlocks = { ], }, }; -const pauseLength = 200; suite('Delete blocks', function (done) { // Setting timeout to unlimited as the webdriver takes a longer time to run than most mocha test @@ -185,7 +185,7 @@ suite('Delete blocks', function (done) { ); await block.click(); await this.browser.keys([Key.Backspace]); - await this.browser.pause(pauseLength); + await this.browser.pause(PAUSE_TIME); // Undo await this.browser.keys([Key.Ctrl, 'Z']); const after = (await getAllBlocks(this.browser)).length; @@ -204,13 +204,13 @@ suite('Delete blocks', function (done) { ); await block.click(); await this.browser.keys([Key.Backspace]); - await this.browser.pause(pauseLength); + await this.browser.pause(PAUSE_TIME); // Undo await this.browser.keys([Key.Ctrl, 'Z']); - await this.browser.pause(pauseLength); + await this.browser.pause(PAUSE_TIME); // Redo await this.browser.keys([Key.Ctrl, Key.Shift, 'Z']); - await this.browser.pause(pauseLength); + await this.browser.pause(PAUSE_TIME); const after = (await getAllBlocks(this.browser)).length; chai.assert.equal( before - 2, diff --git a/tests/browser/test/extensive_test.js b/tests/browser/test/extensive_test.js index 578ed85c9..b16a06bbc 100644 --- a/tests/browser/test/extensive_test.js +++ b/tests/browser/test/extensive_test.js @@ -14,6 +14,7 @@ const { testFileLocations, getBlockElementById, getAllBlocks, + PAUSE_TIME, } = require('./test_setup'); const {Key} = require('webdriverio'); @@ -38,14 +39,14 @@ suite('This tests loading Large Configuration and Deletion', function (done) { ); await fourthRepeatDo.click({x: -100, y: -40}); await this.browser.keys([Key.Delete]); - await this.browser.pause(100); + await this.browser.pause(PAUSE_TIME); const allBlocks = await getAllBlocks(this.browser); chai.assert.equal(allBlocks.length, 10); }); test('undoing delete block results in the correct number of blocks', async function () { await this.browser.keys([Key.Ctrl, 'z']); - await this.browser.pause(100); + await this.browser.pause(PAUSE_TIME); const allBlocks = await getAllBlocks(this.browser); chai.assert.equal(allBlocks.length, 13); }); diff --git a/tests/browser/test/field_edits_test.js b/tests/browser/test/field_edits_test.js index cd720b0f3..f0e3403a7 100644 --- a/tests/browser/test/field_edits_test.js +++ b/tests/browser/test/field_edits_test.js @@ -14,6 +14,7 @@ const { testFileLocations, dragBlockTypeFromFlyout, screenDirection, + PAUSE_TIME, } = require('./test_setup'); const {Key} = require('webdriverio'); @@ -49,7 +50,7 @@ async function testFieldEdits(browser, direction) { // Click on the workspace to exit the field editor const workspace = await browser.$('#blocklyDiv > div > svg.blocklySvg > g'); await workspace.click(); - await browser.pause(200); + await browser.pause(PAUSE_TIME); const fieldValue = await browser.execute((id) => { return Blockly.getMainWorkspace() diff --git a/tests/browser/test/mutator_test.js b/tests/browser/test/mutator_test.js index f4f4b2015..4a65a933c 100644 --- a/tests/browser/test/mutator_test.js +++ b/tests/browser/test/mutator_test.js @@ -17,6 +17,7 @@ const { dragBlockTypeFromFlyout, getSelectedBlockId, screenDirection, + PAUSE_TIME, } = require('./test_setup'); suite('This tests mutating a Blockly block', function (done) { @@ -42,19 +43,19 @@ async function testingMutator(browser, delta) { delta * 50, 50, ); - // Click on the mutator and drag out else ig block + // Click on the mutator and drag out else if block const mutatorWheel = await browser.$( '#blocklyDiv > div > svg.blocklySvg > g > g.blocklyBlockCanvas > g.blocklyDraggable.blocklySelected > g.blocklyIconGroup', ); await mutatorWheel.click(); - await browser.pause(100); + await browser.pause(PAUSE_TIME); const elseIfFlyout = await browser.$( '#blocklyDiv > div > svg.blocklySvg > g > g.blocklyBubbleCanvas > g > g:nth-child(2) > svg:nth-child(1) > g > g.blocklyFlyout > g > g.blocklyBlockCanvas > g:nth-child(3)', ); await elseIfFlyout.dragAndDrop({x: delta * 50, y: 42}); - await browser.pause(100); + await browser.pause(PAUSE_TIME); - await browser.pause(100); + await browser.pause(PAUSE_TIME); // Get the ids for the blocks in the mutator const blockIds = await browser.execute(() => { const mutatorBlock = Blockly.getMainWorkspace().getAllBlocks()[0]; @@ -83,7 +84,7 @@ async function testingMutator(browser, delta) { blockIds[0], dragBlockSelector, ); - await browser.pause(200); + await browser.pause(PAUSE_TIME); // Get the ids for block after mutating const afterInputs = await browser.execute(() => { diff --git a/tests/browser/test/procedure_test.js b/tests/browser/test/procedure_test.js index 2e9fac69c..2b8eab88f 100644 --- a/tests/browser/test/procedure_test.js +++ b/tests/browser/test/procedure_test.js @@ -16,6 +16,7 @@ const { getNthBlockOfCategory, getBlockTypeFromCategory, connect, + PAUSE_TIME, } = require('./test_setup'); suite('Testing Connecting Blocks', function (done) { @@ -100,7 +101,7 @@ suite('Testing Connecting Blocks', function (done) { // Click run button and verify the number is 123 const runButton = await this.browser.$('#runButton'); runButton.click(); - await this.browser.pause(200); + await this.browser.pause(PAUSE_TIME); const alertText = await this.browser.getAlertText(); // get the alert text chai.assert.equal(alertText, '123'); }); diff --git a/tests/browser/test/test_setup.js b/tests/browser/test/test_setup.js index 5bfd70c1b..9bc660421 100644 --- a/tests/browser/test/test_setup.js +++ b/tests/browser/test/test_setup.js @@ -22,6 +22,12 @@ const {posixPath} = require('../../../scripts/helpers'); let driver = null; +/** + * The default amount of time to wait during a test. Increase this to make + * tests easier to watch; decrease it to make tests run faster. + */ +const PAUSE_TIME = 50; + /** * Start up the test page. This should only be done once, to avoid * constantly popping browser windows open and closed. @@ -351,7 +357,7 @@ async function connect( async function switchRTL(browser) { const ltrForm = await browser.$('#options > select:nth-child(1)'); await ltrForm.selectByIndex(1); - await browser.pause(500); + await browser.pause(PAUSE_TIME + 450); } /** @@ -426,7 +432,7 @@ async function contextMenuSelect(browser, block, itemText) { await item.waitForExist(); await item.click(); - await browser.pause(100); + await browser.pause(PAUSE_TIME); } /** @@ -463,12 +469,12 @@ async function scrollFlyout(browser, xDelta, yDelta) { // and one for the toolbox. We want the second one. // This assumes there is only one scrollbar handle in the flyout, but it could // be either horizontal or vertical. - await browser.pause(50); + await browser.pause(PAUSE_TIME); const scrollbarHandle = await browser .$$(`.blocklyFlyoutScrollbar`)[1] .$(`rect.blocklyScrollbarHandle`); await scrollbarHandle.dragAndDrop({x: xDelta, y: yDelta}); - await browser.pause(50); + await browser.pause(PAUSE_TIME); } module.exports = { @@ -491,4 +497,5 @@ module.exports = { getBlockTypeFromWorkspace, getAllBlocks, scrollFlyout, + PAUSE_TIME, }; diff --git a/tests/browser/test/toolbox_drag_test.js b/tests/browser/test/toolbox_drag_test.js index 1e277efc8..7991b2398 100644 --- a/tests/browser/test/toolbox_drag_test.js +++ b/tests/browser/test/toolbox_drag_test.js @@ -15,10 +15,9 @@ const { getCategory, scrollFlyout, screenDirection, + PAUSE_TIME, } = require('./test_setup'); -const PAUSE_TIME = 50; - // Categories in the basic toolbox. const basicCategories = [ 'Logic', @@ -144,32 +143,32 @@ async function openCategories(browser, categoryList, directionMultiplier) { // Unicode escape to close flyout. await browser.keys(['\uE00C']); await browser.pause(PAUSE_TIME); - } else { - const flyoutBlock = await browser.$( - `.blocklyFlyout .blocklyBlockCanvas > g:nth-child(${3 + i * 2})`, - ); - if (!(await elementInBounds(browser, flyoutBlock))) { - await scrollFlyout(browser, 0, 500); - } - - await flyoutBlock.dragAndDrop({x: directionMultiplier * 50, y: 0}); - await browser.pause(PAUSE_TIME); - // Should be one top level block on the workspace. - const topBlockCount = await browser.execute(() => { - return Blockly.getMainWorkspace().getTopBlocks(false).length; - }); - - if (topBlockCount != 1) { - failureCount++; - console.log(`fail: block ${i} in category ${categoryName}`); - } - - // Clean up between blocks so they can't interact with each other. - await browser.execute(() => { - Blockly.getMainWorkspace().clear(); - }); - await browser.pause(PAUSE_TIME); + continue; } + const flyoutBlock = await browser.$( + `.blocklyFlyout .blocklyBlockCanvas > g:nth-child(${3 + i * 2})`, + ); + if (!(await elementInBounds(browser, flyoutBlock))) { + await scrollFlyout(browser, 0, 500); + } + + await flyoutBlock.dragAndDrop({x: directionMultiplier * 50, y: 0}); + await browser.pause(PAUSE_TIME); + // Should be one top level block on the workspace. + const topBlockCount = await browser.execute(() => { + return Blockly.getMainWorkspace().getTopBlocks(false).length; + }); + + if (topBlockCount != 1) { + failureCount++; + console.log(`fail: block ${i} in category ${categoryName}`); + } + + // Clean up between blocks so they can't interact with each other. + await browser.execute(() => { + Blockly.getMainWorkspace().clear(); + }); + await browser.pause(PAUSE_TIME); } } catch (e) { failureCount++; @@ -179,7 +178,7 @@ async function openCategories(browser, categoryList, directionMultiplier) { chai.assert.equal(failureCount, 0); } -suite.skip('Open toolbox categories', function () { +suite('Open toolbox categories', function () { this.timeout(0); test('opening every toolbox category in the category toolbox in LTR', async function () { From eebf19ca39d99a4d8a6a40ee57705ea0fe510ed0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 08:27:41 -0700 Subject: [PATCH 30/92] chore(deps): Bump @typescript-eslint/eslint-plugin from 6.1.0 to 6.4.0 (#7420) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.1.0 to 6.4.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.4.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 400 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 349 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index 212098116..9d475ea46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1244,21 +1244,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.1.0.tgz", - "integrity": "sha512-qg7Bm5TyP/I7iilGyp6DRqqkt8na00lI6HbjWZObgk3FFSzH5ypRwAHXJhJkwiRtTcfn+xYQIMOR5kJgpo6upw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.0.tgz", + "integrity": "sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.1.0", - "@typescript-eslint/type-utils": "6.1.0", - "@typescript-eslint/utils": "6.1.0", - "@typescript-eslint/visitor-keys": "6.1.0", + "@typescript-eslint/scope-manager": "6.4.0", + "@typescript-eslint/type-utils": "6.4.0", + "@typescript-eslint/utils": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", - "natural-compare-lite": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -1279,6 +1278,53 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", + "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", + "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", + "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -1328,6 +1374,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.1.0.tgz", "integrity": "sha512-AxjgxDn27hgPpe2rQe19k0tXw84YCOsjDJ2r61cIebq1t+AIxbgiXKvD4999Wk49GVaAcdJ/d49FYel+Pp3jjw==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "6.1.0", "@typescript-eslint/visitor-keys": "6.1.0" @@ -1341,13 +1388,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.1.0.tgz", - "integrity": "sha512-kFXBx6QWS1ZZ5Ni89TyT1X9Ag6RXVIVhqDs0vZE/jUeWlBv/ixq2diua6G7ece6+fXw3TvNRxP77/5mOMusx2w==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.0.tgz", + "integrity": "sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.1.0", - "@typescript-eslint/utils": "6.1.0", + "@typescript-eslint/typescript-estree": "6.4.0", + "@typescript-eslint/utils": "6.4.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1367,11 +1414,84 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", + "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", + "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", + "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/types": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.1.0.tgz", "integrity": "sha512-+Gfd5NHCpDoHDOaU/yIF3WWRI2PcBRKKpP91ZcVbL0t5tQpqYWBs3z/GGhvU+EV1D0262g9XCnyqQh19prU0JQ==", "dev": true, + "peer": true, "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -1385,6 +1505,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.1.0.tgz", "integrity": "sha512-nUKAPWOaP/tQjU1IQw9sOPCDavs/iU5iYLiY/6u7gxS7oKQoi4aUxXS1nrrVGTyBBaGesjkcwwHkbkiD5eBvcg==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "6.1.0", "@typescript-eslint/visitor-keys": "6.1.0", @@ -1412,6 +1533,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "peer": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -1423,17 +1545,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.1.0.tgz", - "integrity": "sha512-wp652EogZlKmQoMS5hAvWqRKplXvkuOnNzZSE0PVvsKjpexd/XznRVHAtrfHFYmqaJz0DFkjlDsGYC9OXw+OhQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.1.0", - "@typescript-eslint/types": "6.1.0", - "@typescript-eslint/typescript-estree": "6.1.0", + "@typescript-eslint/scope-manager": "6.4.0", + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/typescript-estree": "6.4.0", "semver": "^7.5.4" }, "engines": { @@ -1447,6 +1569,80 @@ "eslint": "^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", + "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", + "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", + "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", + "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/utils/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -1467,6 +1663,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.1.0.tgz", "integrity": "sha512-yQeh+EXhquh119Eis4k0kYhj9vmFzNpbhM3LftWQVwqVjipCkwHBQOZutcYW+JVkjtTG9k8nrZU1UoNedPDd1A==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "6.1.0", "eslint-visitor-keys": "^3.4.1" @@ -7951,12 +8148,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -13102,25 +13293,50 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.1.0.tgz", - "integrity": "sha512-qg7Bm5TyP/I7iilGyp6DRqqkt8na00lI6HbjWZObgk3FFSzH5ypRwAHXJhJkwiRtTcfn+xYQIMOR5kJgpo6upw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.0.tgz", + "integrity": "sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.1.0", - "@typescript-eslint/type-utils": "6.1.0", - "@typescript-eslint/utils": "6.1.0", - "@typescript-eslint/visitor-keys": "6.1.0", + "@typescript-eslint/scope-manager": "6.4.0", + "@typescript-eslint/type-utils": "6.4.0", + "@typescript-eslint/utils": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", - "natural-compare-lite": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", + "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0" + } + }, + "@typescript-eslint/types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", + "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "dev": true + }, + "@typescript-eslint/visitor-keys": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", + "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.4.0", + "eslint-visitor-keys": "^3.4.1" + } + }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -13151,34 +13367,79 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.1.0.tgz", "integrity": "sha512-AxjgxDn27hgPpe2rQe19k0tXw84YCOsjDJ2r61cIebq1t+AIxbgiXKvD4999Wk49GVaAcdJ/d49FYel+Pp3jjw==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/types": "6.1.0", "@typescript-eslint/visitor-keys": "6.1.0" } }, "@typescript-eslint/type-utils": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.1.0.tgz", - "integrity": "sha512-kFXBx6QWS1ZZ5Ni89TyT1X9Ag6RXVIVhqDs0vZE/jUeWlBv/ixq2diua6G7ece6+fXw3TvNRxP77/5mOMusx2w==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.0.tgz", + "integrity": "sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "6.1.0", - "@typescript-eslint/utils": "6.1.0", + "@typescript-eslint/typescript-estree": "6.4.0", + "@typescript-eslint/utils": "6.4.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "@typescript-eslint/types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", + "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", + "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", + "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.4.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/types": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.1.0.tgz", "integrity": "sha512-+Gfd5NHCpDoHDOaU/yIF3WWRI2PcBRKKpP91ZcVbL0t5tQpqYWBs3z/GGhvU+EV1D0262g9XCnyqQh19prU0JQ==", - "dev": true + "dev": true, + "peer": true }, "@typescript-eslint/typescript-estree": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.1.0.tgz", "integrity": "sha512-nUKAPWOaP/tQjU1IQw9sOPCDavs/iU5iYLiY/6u7gxS7oKQoi4aUxXS1nrrVGTyBBaGesjkcwwHkbkiD5eBvcg==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/types": "6.1.0", "@typescript-eslint/visitor-keys": "6.1.0", @@ -13194,6 +13455,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "peer": true, "requires": { "lru-cache": "^6.0.0" } @@ -13201,20 +13463,61 @@ } }, "@typescript-eslint/utils": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.1.0.tgz", - "integrity": "sha512-wp652EogZlKmQoMS5hAvWqRKplXvkuOnNzZSE0PVvsKjpexd/XznRVHAtrfHFYmqaJz0DFkjlDsGYC9OXw+OhQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.0.tgz", + "integrity": "sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.1.0", - "@typescript-eslint/types": "6.1.0", - "@typescript-eslint/typescript-estree": "6.1.0", + "@typescript-eslint/scope-manager": "6.4.0", + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/typescript-estree": "6.4.0", "semver": "^7.5.4" }, "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", + "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0" + } + }, + "@typescript-eslint/types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", + "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", + "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", + "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.4.0", + "eslint-visitor-keys": "^3.4.1" + } + }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -13231,6 +13534,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.1.0.tgz", "integrity": "sha512-yQeh+EXhquh119Eis4k0kYhj9vmFzNpbhM3LftWQVwqVjipCkwHBQOZutcYW+JVkjtTG9k8nrZU1UoNedPDd1A==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/types": "6.1.0", "eslint-visitor-keys": "^3.4.1" @@ -18384,12 +18688,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", From a0301a217a3c8ffb6fad69491147d1dba802f2de Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Mon, 21 Aug 2023 09:05:23 -0700 Subject: [PATCH 31/92] feat: make generator quoting methods public (#7392) * feat: make quoting methods public * feat: add quote and multilineQuote methods * fix: update code generators to use new methods * chore: remove aliases * chore: revert changes to generators --- core/generator.ts | 4 ---- generators/dart/dart_generator.js | 2 -- generators/javascript/javascript_generator.js | 2 -- generators/lua/lua_generator.js | 2 -- generators/php/php_generator.js | 2 -- generators/python/python_generator.js | 2 -- 6 files changed, 14 deletions(-) diff --git a/core/generator.ts b/core/generator.ts index f70856367..67a30a790 100644 --- a/core/generator.ts +++ b/core/generator.ts @@ -170,10 +170,6 @@ export class CodeGenerator { return codeString; } - // The following are some helpful functions which can be used by multiple - - // languages. - /** * Prepend a common prefix onto each line of code. * Intended for indenting code or adding comment markers. diff --git a/generators/dart/dart_generator.js b/generators/dart/dart_generator.js index 5f64e919f..e43c025a9 100644 --- a/generators/dart/dart_generator.js +++ b/generators/dart/dart_generator.js @@ -176,7 +176,6 @@ export class DartGenerator extends CodeGenerator { * Encode a string as a properly escaped Dart string, complete with quotes. * @param {string} string Text to encode. * @return {string} Dart string. - * @protected */ quote_(string) { // Can't use goog.string.quote since $ must also be escaped. @@ -192,7 +191,6 @@ export class DartGenerator extends CodeGenerator { * quotes. * @param {string} string Text to encode. * @return {string} Dart string. - * @protected */ multiline_quote_(string) { const lines = string.split(/\n/g).map(this.quote_); diff --git a/generators/javascript/javascript_generator.js b/generators/javascript/javascript_generator.js index 18811011c..34e68f5aa 100644 --- a/generators/javascript/javascript_generator.js +++ b/generators/javascript/javascript_generator.js @@ -206,7 +206,6 @@ export class JavascriptGenerator extends CodeGenerator { * quotes. * @param {string} string Text to encode. * @return {string} JavaScript string. - * @protected */ quote_(string) { // Can't use goog.string.quote since Google's style guide recommends @@ -222,7 +221,6 @@ export class JavascriptGenerator extends CodeGenerator { * with quotes. * @param {string} string Text to encode. * @return {string} JavaScript string. - * @protected */ multiline_quote_(string) { // Can't use goog.string.quote since Google's style guide recommends diff --git a/generators/lua/lua_generator.js b/generators/lua/lua_generator.js index 587fea90b..ce1159639 100644 --- a/generators/lua/lua_generator.js +++ b/generators/lua/lua_generator.js @@ -149,7 +149,6 @@ export class LuaGenerator extends CodeGenerator { * quotes. * @param {string} string Text to encode. * @return {string} Lua string. - * @protected */ quote_(string) { string = string.replace(/\\/g, '\\\\') @@ -163,7 +162,6 @@ export class LuaGenerator extends CodeGenerator { * quotes. * @param {string} string Text to encode. * @return {string} Lua string. - * @protected */ multiline_quote_(string) { const lines = string.split(/\n/g).map(this.quote_); diff --git a/generators/php/php_generator.js b/generators/php/php_generator.js index 59d595b83..59e1e67c6 100644 --- a/generators/php/php_generator.js +++ b/generators/php/php_generator.js @@ -184,7 +184,6 @@ export class PhpGenerator extends CodeGenerator { * quotes. * @param {string} string Text to encode. * @return {string} PHP string. - * @protected */ quote_(string) { string = string.replace(/\\/g, '\\\\') @@ -198,7 +197,6 @@ export class PhpGenerator extends CodeGenerator { * quotes. * @param {string} string Text to encode. * @return {string} PHP string. - * @protected */ multiline_quote_(string) { const lines = string.split(/\n/g).map(this.quote_); diff --git a/generators/python/python_generator.js b/generators/python/python_generator.js index 2f0aabe0c..5e5836c47 100644 --- a/generators/python/python_generator.js +++ b/generators/python/python_generator.js @@ -228,7 +228,6 @@ export class PythonGenerator extends CodeGenerator { * Encode a string as a properly escaped Python string, complete with quotes. * @param {string} string Text to encode. * @return {string} Python string. - * @protected */ quote_(string) { string = string.replace(/\\/g, '\\\\').replace(/\n/g, '\\\n'); @@ -250,7 +249,6 @@ export class PythonGenerator extends CodeGenerator { * with quotes. * @param {string} string Text to encode. * @return {string} Python string. - * @protected */ multiline_quote_(string) { const lines = string.split(/\n/g).map(this.quote_); From 99098684352aa895f4c9c5ccf75acfac1efa6838 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Mon, 21 Aug 2023 14:58:30 -0700 Subject: [PATCH 32/92] fix: Always draw dragged blocks atop others in the workspace. (#6874) * fix: Always draw dragged blocks atop others in the workspace. * chore: format --------- Co-authored-by: Beka Westberg --- core/block_dragger.ts | 11 ++++------- core/block_svg.ts | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/core/block_dragger.ts b/core/block_dragger.ts index b6f3ab7ba..9bbce0fbe 100644 --- a/core/block_dragger.ts +++ b/core/block_dragger.ts @@ -105,13 +105,10 @@ export class BlockDragger implements IBlockDragger { } this.fireDragStartEvent_(); - // Mutators don't have the same type of z-ordering as the normal workspace - // during a drag. They have to rely on the order of the blocks in the SVG. - // For performance reasons that usually happens at the end of a drag, - // but do it at the beginning for mutators. - if (this.workspace_.isMutator) { - this.draggingBlock_.bringToFront(); - } + // The z-order of blocks depends on their order in the SVG, so move the + // block being dragged to the front so that it will appear atop other blocks + // in the workspace. + this.draggingBlock_.bringToFront(true); // During a drag there may be a lot of rerenders, but not field changes. // Turn the cache on so we don't do spurious remeasures during the drag. diff --git a/core/block_svg.ts b/core/block_svg.ts index dff7fafea..6290608fe 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -328,7 +328,14 @@ export class BlockSvg } else if (oldParent) { // If we are losing a parent, we want to move our DOM element to the // root of the workspace. - this.workspace.getCanvas().appendChild(svgRoot); + const draggingBlock = this.workspace + .getCanvas() + .querySelector('.blocklyDragging'); + if (draggingBlock) { + this.workspace.getCanvas().insertBefore(svgRoot, draggingBlock); + } else { + this.workspace.getCanvas().appendChild(svgRoot); + } this.translate(oldXY.x, oldXY.y); } @@ -1146,9 +1153,11 @@ export class BlockSvg * order that they are in the DOM. By placing this block first within the * block group's , it will render on top of any other blocks. * + * @param blockOnly: True to only move this block to the front without + * adjusting its parents. * @internal */ - bringToFront() { + bringToFront(blockOnly = false) { /* eslint-disable-next-line @typescript-eslint/no-this-alias */ let block: this | null = this; do { @@ -1159,6 +1168,7 @@ export class BlockSvg if (childNodes[childNodes.length - 1] !== root) { parent!.appendChild(root); } + if (blockOnly) break; block = block.getParent(); } while (block); } From 93106777b063d4ece43196d1df886791621f8fe3 Mon Sep 17 00:00:00 2001 From: randrei12 <79661902+randrei12@users.noreply.github.com> Date: Tue, 22 Aug 2023 20:28:10 +0300 Subject: [PATCH 33/92] feat: make ordered argument optional for workspace functions (#7424) --- core/workspace.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/workspace.ts b/core/workspace.ts index 257ff08b1..2f3fe2b61 100644 --- a/core/workspace.ts +++ b/core/workspace.ts @@ -206,7 +206,7 @@ export class Workspace implements IASTNodeLocation { * @param ordered Sort the list if true. * @returns The top-level block objects. */ - getTopBlocks(ordered: boolean): Block[] { + getTopBlocks(ordered = false): Block[] { // Copy the topBlocks list. const blocks = new Array().concat(this.topBlocks); if (ordered && blocks.length > 1) { @@ -247,7 +247,7 @@ export class Workspace implements IASTNodeLocation { * @param ordered Sort the list if true. * @returns The blocks of the given type. */ - getBlocksByType(type: string, ordered: boolean): Block[] { + getBlocksByType(type: string, ordered = false): Block[] { if (!this.typedBlocksDB.has(type)) { return []; } @@ -307,7 +307,7 @@ export class Workspace implements IASTNodeLocation { * @returns The top-level comment objects. * @internal */ - getTopComments(ordered: boolean): WorkspaceComment[] { + getTopComments(ordered = false): WorkspaceComment[] { // Copy the topComments list. const comments = new Array().concat(this.topComments); if (ordered && comments.length > 1) { @@ -323,7 +323,7 @@ export class Workspace implements IASTNodeLocation { * @param ordered Sort the list if true. * @returns Array of blocks. */ - getAllBlocks(ordered: boolean): Block[] { + getAllBlocks(ordered = false): Block[] { let blocks: Block[]; if (ordered) { // Slow, but ordered. From d6d1074f51761544218b2ffce5c3a9fd8b623e41 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 22 Aug 2023 11:24:11 -0700 Subject: [PATCH 34/92] chore: make ordered argument optional for workspace_svg functions (#7425) --- core/workspace_svg.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/workspace_svg.ts b/core/workspace_svg.ts index 4c531f35a..f6dcbac17 100644 --- a/core/workspace_svg.ts +++ b/core/workspace_svg.ts @@ -2255,7 +2255,7 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg { * @param ordered Sort the list if true. * @returns Array of blocks. */ - override getAllBlocks(ordered: boolean): BlockSvg[] { + override getAllBlocks(ordered = false): BlockSvg[] { return super.getAllBlocks(ordered) as BlockSvg[]; } @@ -2266,7 +2266,7 @@ export class WorkspaceSvg extends Workspace implements IASTNodeLocationSvg { * @param ordered Sort the list if true. * @returns The top-level block objects. */ - override getTopBlocks(ordered: boolean): BlockSvg[] { + override getTopBlocks(ordered = false): BlockSvg[] { return super.getTopBlocks(ordered) as BlockSvg[]; } From b9a7a0c7ad9495765ea87b23912638b93a823a4d Mon Sep 17 00:00:00 2001 From: Maribeth Bottorff Date: Wed, 23 Aug 2023 16:20:40 -0700 Subject: [PATCH 35/92] fix: do not create extra shadow blocks when mirroring events (#7427) --- core/events/events_block_create.ts | 21 +++++++++++++++ tests/mocha/event_block_create_test.js | 36 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/core/events/events_block_create.ts b/core/events/events_block_create.ts index 9abf5bac3..dfb21cd81 100644 --- a/core/events/events_block_create.ts +++ b/core/events/events_block_create.ts @@ -138,6 +138,7 @@ export class BlockCreate extends BlockBase { 'the constructor, or call fromJson', ); } + if (allShadowBlocks(workspace, this.ids)) return; if (forward) { blocks.append(this.json, workspace); } else { @@ -154,6 +155,26 @@ export class BlockCreate extends BlockBase { } } } +/** + * Returns true if all blocks in the list are shadow blocks. If so, that means + * the top-level block being created is a shadow block. This only happens when a + * block that was covering up a shadow block is removed. We don't need to create + * an additional block in that case because the original block still has its + * shadow block. + * + * @param workspace Workspace to check for blocks + * @param ids A list of block ids that were created in this event + * @returns True if all block ids in the list are shadow blocks + */ +const allShadowBlocks = function ( + workspace: Workspace, + ids: string[], +): boolean { + const shadows = ids + .map((id) => workspace.getBlockById(id)) + .filter((block) => block && block.isShadow()); + return shadows.length === ids.length; +}; export interface BlockCreateJson extends BlockBaseJson { xml: string; diff --git a/tests/mocha/event_block_create_test.js b/tests/mocha/event_block_create_test.js index b17b277e7..f3aed672f 100644 --- a/tests/mocha/event_block_create_test.js +++ b/tests/mocha/event_block_create_test.js @@ -56,6 +56,42 @@ suite('Block Create Event', function () { chai.assert.equal(event.xml.tagName, 'shadow'); }); + test('Does not create extra shadow blocks', function () { + const shadowId = 'shadow_block'; + const blockJson = { + 'type': 'math_arithmetic', + 'id': 'parent_with_shadow', + 'fields': {'OP': 'ADD'}, + 'inputs': { + 'A': { + 'shadow': { + 'type': 'math_number', + 'id': shadowId, + 'fields': {'NUM': 1}, + }, + }, + }, + }; + + // If there is a shadow block on the workspace and then we get + // a block create event with the same ID as the shadow block, + // this represents a block that had been covering a shadow block + // being removed. + Blockly.serialization.blocks.append(blockJson, this.workspace); + const shadowBlock = this.workspace.getBlockById(shadowId); + const blocksBefore = this.workspace.getAllBlocks(); + + const event = new Blockly.Events.BlockCreate(shadowBlock); + event.run(true); + + const blocksAfter = this.workspace.getAllBlocks(); + chai.assert.deepEqual( + blocksAfter, + blocksBefore, + 'No new blocks should be created from an event that only creates shadow blocks', + ); + }); + suite('Serialization', function () { test('events round-trip through JSON', function () { const block = this.workspace.newBlock('row_block', 'block_id'); From 9d85afc50686e42cc6bbe9b91638a93b24513dce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:23:19 -0700 Subject: [PATCH 36/92] chore(deps): Bump eslint-plugin-jsdoc from 46.2.6 to 46.5.0 (#7417) Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 46.2.6 to 46.5.0. - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v46.2.6...v46.5.0) --- updated-dependencies: - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 64 +++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d475ea46..8b9cb3fa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -272,12 +272,12 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.39.4", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", - "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz", + "integrity": "sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==", "dev": true, "dependencies": { - "comment-parser": "1.3.1", + "comment-parser": "1.4.0", "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" }, @@ -3147,9 +3147,9 @@ } }, "node_modules/comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.0.tgz", + "integrity": "sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==", "dev": true, "engines": { "node": ">= 12.0.0" @@ -4096,19 +4096,19 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "46.2.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.2.6.tgz", - "integrity": "sha512-zIaK3zbSrKuH12bP+SPybPgcHSM6MFzh3HFeaODzmsF1N8C1l8dzJ22cW1aq4g0+nayU1VMjmNf7hg0dpShLrA==", + "version": "46.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.5.0.tgz", + "integrity": "sha512-aulXdA4I1dyWpzyS1Nh/GNoS6PavzeucxEapnMR4JUERowWvaEk2Y4A5irpHAcdXtBBHLVe8WIhdXNjoAlGQgA==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.39.4", + "@es-joy/jsdoccomment": "~0.40.1", "are-docs-informative": "^0.0.2", - "comment-parser": "1.3.1", + "comment-parser": "1.4.0", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", "is-builtin-module": "^3.2.1", - "semver": "^7.5.1", + "semver": "^7.5.4", "spdx-expression-parse": "^3.0.1" }, "engines": { @@ -4119,9 +4119,9 @@ } }, "node_modules/eslint-plugin-jsdoc/node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -12505,12 +12505,12 @@ "requires": {} }, "@es-joy/jsdoccomment": { - "version": "0.39.4", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", - "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz", + "integrity": "sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==", "dev": true, "requires": { - "comment-parser": "1.3.1", + "comment-parser": "1.4.0", "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" } @@ -14663,9 +14663,9 @@ "dev": true }, "comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.0.tgz", + "integrity": "sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==", "dev": true }, "component-emitter": { @@ -15435,26 +15435,26 @@ "requires": {} }, "eslint-plugin-jsdoc": { - "version": "46.2.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.2.6.tgz", - "integrity": "sha512-zIaK3zbSrKuH12bP+SPybPgcHSM6MFzh3HFeaODzmsF1N8C1l8dzJ22cW1aq4g0+nayU1VMjmNf7hg0dpShLrA==", + "version": "46.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.5.0.tgz", + "integrity": "sha512-aulXdA4I1dyWpzyS1Nh/GNoS6PavzeucxEapnMR4JUERowWvaEk2Y4A5irpHAcdXtBBHLVe8WIhdXNjoAlGQgA==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "~0.39.4", + "@es-joy/jsdoccomment": "~0.40.1", "are-docs-informative": "^0.0.2", - "comment-parser": "1.3.1", + "comment-parser": "1.4.0", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", "is-builtin-module": "^3.2.1", - "semver": "^7.5.1", + "semver": "^7.5.4", "spdx-expression-parse": "^3.0.1" }, "dependencies": { "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" From c8c1d2653ce0527092cafe15c2edb11fb0bbeb7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:25:13 -0700 Subject: [PATCH 37/92] chore(deps): Bump eslint-config-prettier from 8.8.0 to 9.0.0 (#7419) Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 8.8.0 to 9.0.0. - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v8.8.0...v9.0.0) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b9cb3fa7..635078575 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "concurrently": "^8.0.1", "eslint": "^8.4.1", "eslint-config-google": "^0.14.0", - "eslint-config-prettier": "^8.8.0", + "eslint-config-prettier": "^9.0.0", "eslint-plugin-jsdoc": "^46.2.6", "google-closure-compiler": "^20230802.0.0", "google-closure-deps": "^20230502.0.0", @@ -4084,9 +4084,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -15428,9 +15428,9 @@ "requires": {} }, "eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index 7ec8ee1a2..91b5f21a9 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "concurrently": "^8.0.1", "eslint": "^8.4.1", "eslint-config-google": "^0.14.0", - "eslint-config-prettier": "^8.8.0", + "eslint-config-prettier": "^9.0.0", "eslint-plugin-jsdoc": "^46.2.6", "google-closure-compiler": "^20230802.0.0", "google-closure-deps": "^20230502.0.0", From 2fce8ad100818c42c7fa0b593b365c5379a59a4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:26:46 -0700 Subject: [PATCH 38/92] chore(deps): Bump @blockly/dev-tools from 7.0.2 to 7.0.3 (#7418) Bumps [@blockly/dev-tools](https://github.com/google/blockly-samples/tree/HEAD/plugins/dev-tools) from 7.0.2 to 7.0.3. - [Release notes](https://github.com/google/blockly-samples/releases) - [Changelog](https://github.com/google/blockly-samples/blob/master/plugins/dev-tools/CHANGELOG.md) - [Commits](https://github.com/google/blockly-samples/commits/@blockly/dev-tools@7.0.3/plugins/dev-tools) --- updated-dependencies: - dependency-name: "@blockly/dev-tools" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 134 +++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index 635078575..698287afa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -187,16 +187,16 @@ } }, "node_modules/@blockly/dev-tools": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@blockly/dev-tools/-/dev-tools-7.0.2.tgz", - "integrity": "sha512-AwG+CnyAeRIZGkeG+WhclRnrwMjXRWhd9xAkoXdKBaY9LHyLU61WSOk0D8E7ta98e19Yo/fl4CwYMYEQ8F63kw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@blockly/dev-tools/-/dev-tools-7.0.3.tgz", + "integrity": "sha512-IyupTGAx0yMQImOKyS0xnqe6WKYwwh9B+4EHT5VlNORrku4TxfJfM0rsVyeacOvAG6hBHAE9J9VqYih79WNFfA==", "dev": true, "dependencies": { "@blockly/block-test": "^5.0.0", - "@blockly/theme-dark": "^6.0.0", - "@blockly/theme-deuteranopia": "^5.0.0", - "@blockly/theme-highcontrast": "^5.0.0", - "@blockly/theme-tritanopia": "^5.0.0", + "@blockly/theme-dark": "^6.0.1", + "@blockly/theme-deuteranopia": "^5.0.1", + "@blockly/theme-highcontrast": "^5.0.1", + "@blockly/theme-tritanopia": "^5.0.1", "chai": "^4.2.0", "dat.gui": "^0.7.7", "lodash.assign": "^4.2.0", @@ -211,10 +211,10 @@ "blockly": "^10.0.0" } }, - "node_modules/@blockly/dev-tools/node_modules/@blockly/theme-dark": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-dark/-/theme-dark-6.0.0.tgz", - "integrity": "sha512-BipmTIYehNHI6lT2EgTmePyYWw6U6eLtoz74yxMWxpDM8QMTU511cmkHAIBSzEORw1Wh2vk87fRW5mtKpDrE1g==", + "node_modules/@blockly/theme-dark": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-dark/-/theme-dark-6.0.1.tgz", + "integrity": "sha512-fZa834SKstG31PNkoZ26DLIpevNVBWLDwDT/g99a6EtwqnkZg2VjCjbJDLA0xzrCmxN8AH6zmZU/lAdMoBH8sw==", "dev": true, "engines": { "node": ">=8.17.0" @@ -223,10 +223,10 @@ "blockly": "^10.0.0" } }, - "node_modules/@blockly/dev-tools/node_modules/@blockly/theme-deuteranopia": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-deuteranopia/-/theme-deuteranopia-5.0.0.tgz", - "integrity": "sha512-+Y9UcMjdgkYiSElLDGW877r11kS/WSyxDDbpytblI5HuzOyn+oopXCrWWzlMGyaTV/rQTzUmQfNdH4vHHaMuZQ==", + "node_modules/@blockly/theme-deuteranopia": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-deuteranopia/-/theme-deuteranopia-5.0.1.tgz", + "integrity": "sha512-xGBYGkr170VzODnio41UOZwDSjUofbNWiJwTSA5rm3jbWyeeERL8raCPJN+AB3kZ8Vfvt6BSUBobYGKcsxFGNw==", "dev": true, "engines": { "node": ">=8.17.0" @@ -235,22 +235,10 @@ "blockly": "^10.0.0" } }, - "node_modules/@blockly/dev-tools/node_modules/@blockly/theme-highcontrast": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-highcontrast/-/theme-highcontrast-5.0.0.tgz", - "integrity": "sha512-Bh8BSwdPic6NFOFsjNwEi0OduGR+FPjb4hcp5gD5JLIWXdL+/OboKVb8CgX4+NsJzlkbv+fOGfrXfrPIBiFeFg==", - "dev": true, - "engines": { - "node": ">=8.17.0" - }, - "peerDependencies": { - "blockly": "^10.0.0" - } - }, - "node_modules/@blockly/dev-tools/node_modules/@blockly/theme-tritanopia": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-tritanopia/-/theme-tritanopia-5.0.0.tgz", - "integrity": "sha512-U1vqarnMwMcStfqoKFna6nJnL/5DeyXhPFbj7gfbORJKQuOs49OETPmQlHb2/PjHbHq9VUlRnkW/MJ0K+6cVGw==", + "node_modules/@blockly/theme-highcontrast": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-highcontrast/-/theme-highcontrast-5.0.1.tgz", + "integrity": "sha512-090W+FL4mCwf3fEasERHmypJbYdoP1zjlPnnDsrvkXw5EoSEY6b8PhIgzD63JeZMjWOFjWyCgoZWsBvI9IbVCA==", "dev": true, "engines": { "node": ">=8.17.0" @@ -271,6 +259,18 @@ "blockly": "^10.0.0" } }, + "node_modules/@blockly/theme-tritanopia": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-tritanopia/-/theme-tritanopia-5.0.1.tgz", + "integrity": "sha512-0cfmzjb/2aCga16CO1QDjKxoOwucU29v9P+Dwgr16j8IoNKpFOUnnN2xt7yYw4CeJaOVGZEcHljjzgU52oRPXA==", + "dev": true, + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "blockly": "^10.0.0" + } + }, "node_modules/@es-joy/jsdoccomment": { "version": "0.40.1", "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz", @@ -12449,54 +12449,45 @@ "requires": {} }, "@blockly/dev-tools": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@blockly/dev-tools/-/dev-tools-7.0.2.tgz", - "integrity": "sha512-AwG+CnyAeRIZGkeG+WhclRnrwMjXRWhd9xAkoXdKBaY9LHyLU61WSOk0D8E7ta98e19Yo/fl4CwYMYEQ8F63kw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@blockly/dev-tools/-/dev-tools-7.0.3.tgz", + "integrity": "sha512-IyupTGAx0yMQImOKyS0xnqe6WKYwwh9B+4EHT5VlNORrku4TxfJfM0rsVyeacOvAG6hBHAE9J9VqYih79WNFfA==", "dev": true, "requires": { "@blockly/block-test": "^5.0.0", - "@blockly/theme-dark": "^6.0.0", - "@blockly/theme-deuteranopia": "^5.0.0", - "@blockly/theme-highcontrast": "^5.0.0", - "@blockly/theme-tritanopia": "^5.0.0", + "@blockly/theme-dark": "^6.0.1", + "@blockly/theme-deuteranopia": "^5.0.1", + "@blockly/theme-highcontrast": "^5.0.1", + "@blockly/theme-tritanopia": "^5.0.1", "chai": "^4.2.0", "dat.gui": "^0.7.7", "lodash.assign": "^4.2.0", "lodash.merge": "^4.6.2", "monaco-editor": "^0.20.0", "sinon": "^9.0.2" - }, - "dependencies": { - "@blockly/theme-dark": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-dark/-/theme-dark-6.0.0.tgz", - "integrity": "sha512-BipmTIYehNHI6lT2EgTmePyYWw6U6eLtoz74yxMWxpDM8QMTU511cmkHAIBSzEORw1Wh2vk87fRW5mtKpDrE1g==", - "dev": true, - "requires": {} - }, - "@blockly/theme-deuteranopia": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-deuteranopia/-/theme-deuteranopia-5.0.0.tgz", - "integrity": "sha512-+Y9UcMjdgkYiSElLDGW877r11kS/WSyxDDbpytblI5HuzOyn+oopXCrWWzlMGyaTV/rQTzUmQfNdH4vHHaMuZQ==", - "dev": true, - "requires": {} - }, - "@blockly/theme-highcontrast": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-highcontrast/-/theme-highcontrast-5.0.0.tgz", - "integrity": "sha512-Bh8BSwdPic6NFOFsjNwEi0OduGR+FPjb4hcp5gD5JLIWXdL+/OboKVb8CgX4+NsJzlkbv+fOGfrXfrPIBiFeFg==", - "dev": true, - "requires": {} - }, - "@blockly/theme-tritanopia": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-tritanopia/-/theme-tritanopia-5.0.0.tgz", - "integrity": "sha512-U1vqarnMwMcStfqoKFna6nJnL/5DeyXhPFbj7gfbORJKQuOs49OETPmQlHb2/PjHbHq9VUlRnkW/MJ0K+6cVGw==", - "dev": true, - "requires": {} - } } }, + "@blockly/theme-dark": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-dark/-/theme-dark-6.0.1.tgz", + "integrity": "sha512-fZa834SKstG31PNkoZ26DLIpevNVBWLDwDT/g99a6EtwqnkZg2VjCjbJDLA0xzrCmxN8AH6zmZU/lAdMoBH8sw==", + "dev": true, + "requires": {} + }, + "@blockly/theme-deuteranopia": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-deuteranopia/-/theme-deuteranopia-5.0.1.tgz", + "integrity": "sha512-xGBYGkr170VzODnio41UOZwDSjUofbNWiJwTSA5rm3jbWyeeERL8raCPJN+AB3kZ8Vfvt6BSUBobYGKcsxFGNw==", + "dev": true, + "requires": {} + }, + "@blockly/theme-highcontrast": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-highcontrast/-/theme-highcontrast-5.0.1.tgz", + "integrity": "sha512-090W+FL4mCwf3fEasERHmypJbYdoP1zjlPnnDsrvkXw5EoSEY6b8PhIgzD63JeZMjWOFjWyCgoZWsBvI9IbVCA==", + "dev": true, + "requires": {} + }, "@blockly/theme-modern": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@blockly/theme-modern/-/theme-modern-5.0.0.tgz", @@ -12504,6 +12495,13 @@ "dev": true, "requires": {} }, + "@blockly/theme-tritanopia": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-tritanopia/-/theme-tritanopia-5.0.1.tgz", + "integrity": "sha512-0cfmzjb/2aCga16CO1QDjKxoOwucU29v9P+Dwgr16j8IoNKpFOUnnN2xt7yYw4CeJaOVGZEcHljjzgU52oRPXA==", + "dev": true, + "requires": {} + }, "@es-joy/jsdoccomment": { "version": "0.40.1", "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz", From 7e3472c305ee00b751257a788ef3ad3a5c3589b3 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Thu, 24 Aug 2023 08:34:45 -0700 Subject: [PATCH 39/92] fix: dragging blocks within a zoomed mutator (#7423) --- core/bubbles/mini_workspace_bubble.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/bubbles/mini_workspace_bubble.ts b/core/bubbles/mini_workspace_bubble.ts index 963055081..f459654fa 100644 --- a/core/bubbles/mini_workspace_bubble.ts +++ b/core/bubbles/mini_workspace_bubble.ts @@ -65,6 +65,9 @@ export class MiniWorkspaceBubble extends Bubble { ); workspaceOptions.parentWorkspace = this.workspace; this.miniWorkspace = this.newWorkspaceSvg(new Options(workspaceOptions)); + // TODO (#7422): Change this to `internalIsMiniWorkspace` or something. Not + // all mini workspaces are necessarily mutators. + this.miniWorkspace.internalIsMutator = true; const background = this.miniWorkspace.createDom('blocklyMutatorBackground'); this.svgDialog.appendChild(background); if (options.languageTree) { From 9db853db764d60093a4e1305d7a82c1b06f1baf3 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 24 Aug 2023 10:41:17 -0700 Subject: [PATCH 40/92] Revert "fix: insertion marker's next blocks become real block (#7384)" (#7429) This reverts commit 18ee0ec41b2a326255f3270166e60b565f75b975. --- core/insertion_marker_manager.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/core/insertion_marker_manager.ts b/core/insertion_marker_manager.ts index effe78219..35e46dc01 100644 --- a/core/insertion_marker_manager.ts +++ b/core/insertion_marker_manager.ts @@ -225,12 +225,7 @@ export class InsertionMarkerManager { eventUtils.disable(); let result: BlockSvg; try { - const blockJson = blocks.save(sourceBlock, { - addCoordinates: false, - addInputBlocks: false, - addNextBlocks: false, - doFullSerialization: false, - }); + const blockJson = blocks.save(sourceBlock); if (!blockJson) { throw new Error('Failed to serialize source block.'); } From 51651dffe82980d3188f2e8ca5ba47104d40cbc7 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 24 Aug 2023 10:46:46 -0700 Subject: [PATCH 41/92] Revert "feat: Insertion marker json deserialization 7316 (#7364)" (#7430) This reverts commit 84215386fb4f71c6facb2d5e8a7336820fb9a9d9. --- core/insertion_marker_manager.ts | 56 ++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/core/insertion_marker_manager.ts b/core/insertion_marker_manager.ts index 35e46dc01..48be15c14 100644 --- a/core/insertion_marker_manager.ts +++ b/core/insertion_marker_manager.ts @@ -18,11 +18,11 @@ import type {BlockSvg} from './block_svg.js'; import * as common from './common.js'; import {ComponentManager} from './component_manager.js'; import {config} from './config.js'; +import * as constants from './constants.js'; import * as eventUtils from './events/utils.js'; import type {IDeleteArea} from './interfaces/i_delete_area.js'; import type {IDragTarget} from './interfaces/i_drag_target.js'; import type {RenderedConnection} from './rendered_connection.js'; -import * as blocks from './serialization/blocks.js'; import type {Coordinate} from './utils/coordinate.js'; import type {WorkspaceSvg} from './workspace_svg.js'; import * as renderManagement from './render_management.js'; @@ -43,6 +43,16 @@ interface CandidateConnection { radius: number; } +/** + * An error message to throw if the block created by createMarkerBlock_ is + * missing any components. + */ +const DUPLICATE_BLOCK_ERROR = + 'The insertion marker ' + + 'manager tried to create a marker but the result is missing %1. If ' + + 'you are using a mutator, make sure your domToMutation method is ' + + 'properly defined.'; + /** * Class that controls updates to connections during drags. It is primarily * responsible for finding the closest eligible connection and highlighting or @@ -222,16 +232,48 @@ export class InsertionMarkerManager { * @returns The insertion marker that represents the given block. */ private createMarkerBlock(sourceBlock: BlockSvg): BlockSvg { + const imType = sourceBlock.type; + eventUtils.disable(); let result: BlockSvg; try { - const blockJson = blocks.save(sourceBlock); - if (!blockJson) { - throw new Error('Failed to serialize source block.'); - } - result = blocks.append(blockJson, this.workspace) as BlockSvg; - + result = this.workspace.newBlock(imType); result.setInsertionMarker(true); + if (sourceBlock.saveExtraState) { + const state = sourceBlock.saveExtraState(); + if (state && result.loadExtraState) { + result.loadExtraState(state); + } + } else if (sourceBlock.mutationToDom) { + const oldMutationDom = sourceBlock.mutationToDom(); + if (oldMutationDom && result.domToMutation) { + result.domToMutation(oldMutationDom); + } + } + // Copy field values from the other block. These values may impact the + // rendered size of the insertion marker. Note that we do not care about + // child blocks here. + for (let i = 0; i < sourceBlock.inputList.length; i++) { + const sourceInput = sourceBlock.inputList[i]; + if (sourceInput.name === constants.COLLAPSED_INPUT_NAME) { + continue; // Ignore the collapsed input. + } + const resultInput = result.inputList[i]; + if (!resultInput) { + throw new Error(DUPLICATE_BLOCK_ERROR.replace('%1', 'an input')); + } + for (let j = 0; j < sourceInput.fieldRow.length; j++) { + const sourceField = sourceInput.fieldRow[j]; + const resultField = resultInput.fieldRow[j]; + if (!resultField) { + throw new Error(DUPLICATE_BLOCK_ERROR.replace('%1', 'a field')); + } + resultField.setValue(sourceField.getValue()); + } + } + + result.setCollapsed(sourceBlock.isCollapsed()); + result.setInputsInline(sourceBlock.getInputsInline()); result.initSvg(); result.getSvgRoot().setAttribute('visibility', 'hidden'); From fb6c3dc8b21e70bfe5bb524d328021966be24074 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 08:23:45 -0700 Subject: [PATCH 42/92] chore(deps): Bump @blockly/theme-modern from 5.0.0 to 5.0.1 (#7438) Bumps [@blockly/theme-modern](https://github.com/google/blockly-samples/tree/HEAD/plugins/theme-modern) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/google/blockly-samples/releases) - [Changelog](https://github.com/google/blockly-samples/blob/master/plugins/theme-modern/CHANGELOG.md) - [Commits](https://github.com/google/blockly-samples/commits/@blockly/theme-modern@5.0.1/plugins/theme-modern) --- updated-dependencies: - dependency-name: "@blockly/theme-modern" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7e4389bf2..9178ecc7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -248,9 +248,9 @@ } }, "node_modules/@blockly/theme-modern": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-modern/-/theme-modern-5.0.0.tgz", - "integrity": "sha512-K5qg+PVkapG/h4NEVuTE85/n/BeR73ygeFipKLRf0S6A8PbffpLWbolutBWDzsArTeEQ4e/ItqKveuFpG8iZIg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-modern/-/theme-modern-5.0.1.tgz", + "integrity": "sha512-88XtPw1cNVFTg3LDUeoERc9LZk6wZ7iFJYklPgbWUL/Dnq7/WOuB+kYy2nb7jBPReKchInNjdHM09tWQdKi+eQ==", "dev": true, "engines": { "node": ">=8.17.0" @@ -12489,9 +12489,9 @@ "requires": {} }, "@blockly/theme-modern": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@blockly/theme-modern/-/theme-modern-5.0.0.tgz", - "integrity": "sha512-K5qg+PVkapG/h4NEVuTE85/n/BeR73ygeFipKLRf0S6A8PbffpLWbolutBWDzsArTeEQ4e/ItqKveuFpG8iZIg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-modern/-/theme-modern-5.0.1.tgz", + "integrity": "sha512-88XtPw1cNVFTg3LDUeoERc9LZk6wZ7iFJYklPgbWUL/Dnq7/WOuB+kYy2nb7jBPReKchInNjdHM09tWQdKi+eQ==", "dev": true, "requires": {} }, From 650e620e2b76484f6523a973a5842836a054c2b1 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Mon, 28 Aug 2023 18:56:57 +0200 Subject: [PATCH 43/92] Prevent raw content being served from storage. (#7443) TODO: Detailed description to be added once deployed. --- appengine/storage.js | 2 ++ appengine/storage.py | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/appengine/storage.js b/appengine/storage.js index a9eb74cc9..c11571771 100644 --- a/appengine/storage.js +++ b/appengine/storage.js @@ -130,6 +130,8 @@ BlocklyStorage.handleRequest_ = function() { BlocklyStorage.alert(BlocklyStorage.HASH_ERROR.replace('%1', window.location.hash)); } else { + // Remove poison line to prevent raw content from being served. + data = data.replace(/^\{\[\(\< UNTRUSTED CONTENT \>\)\]\}\n/, ''); BlocklyStorage.loadXml_(data, BlocklyStorage.httpRequest_.workspace); } } diff --git a/appengine/storage.py b/appengine/storage.py index 7e5073d3d..637e8843d 100644 --- a/appengine/storage.py +++ b/appengine/storage.py @@ -80,11 +80,20 @@ def keyToXml(key_provided): with client.context(): result.put() xml = result.xml_content + # Add a poison line to prevent raw content from being served. + xml = "{[(< UNTRUSTED CONTENT >)]}\n" + xml return xml def app(environ, start_response): - forms = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ) + headers = [ + ("Content-Type", "text/plain") + ] + if environ["REQUEST_METHOD"] != "POST": + start_response("405 Method Not Allowed", headers) + return ["Storage only accepts POST".encode('utf-8')] + + forms = cgi.FieldStorage(fp=environ["wsgi.input"], environ=environ) if "xml" in forms: out = xmlToKey(forms["xml"].value) elif "key" in forms: @@ -92,8 +101,5 @@ def app(environ, start_response): else: out = "" - headers = [ - ("Content-Type", "text/plain") - ] start_response("200 OK", headers) return [out.encode("utf-8")] From 8193cffe774a8bf61a59fa9d2f84afec3ca1abe8 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Mon, 28 Aug 2023 15:01:10 -0700 Subject: [PATCH 44/92] chore: clean up mutator tests (#7434) * chore: work on cleaning up mutator tests * chore: remove need for dragBlockSelector * chore: add helpers to clean up tests * chore: add issue link in TODO * chore: format and add docs for new helpers * Revert "chore: remove need for dragBlockSelector" This reverts commit dfec88a6aa90eecd6429b6f2a878dd7d5a10793f. * chore: fixup connect helper --- core/flyout_base.ts | 1 + tests/browser/test/mutator_test.js | 85 +++++++++++++--------------- tests/browser/test/test_setup.js | 90 ++++++++++++++++++++---------- 3 files changed, 100 insertions(+), 76 deletions(-) diff --git a/core/flyout_base.ts b/core/flyout_base.ts index e9797df4f..a00c95bd8 100644 --- a/core/flyout_base.ts +++ b/core/flyout_base.ts @@ -1213,6 +1213,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout { } // Clone the block. + // TODO(#7432): Add a saveIds parameter to `save`. const json = blocks.save(oldBlock) as blocks.State; // Normallly this resizes leading to weird jumps. Save it for terminateDrag. targetWorkspace.setResizesEnabled(false); diff --git a/tests/browser/test/mutator_test.js b/tests/browser/test/mutator_test.js index 4a65a933c..93e698af6 100644 --- a/tests/browser/test/mutator_test.js +++ b/tests/browser/test/mutator_test.js @@ -4,94 +4,85 @@ * SPDX-License-Identifier: Apache-2.0 */ -/** - * @fileoverview Node.js script to run Automated tests in Chrome, via webdriver. - */ - const chai = require('chai'); const { testSetup, testFileLocations, connect, - switchRTL, dragBlockTypeFromFlyout, - getSelectedBlockId, screenDirection, PAUSE_TIME, + getBlockElementById, + dragBlockFromMutatorFlyout, + openMutatorForBlock, } = require('./test_setup'); -suite('This tests mutating a Blockly block', function (done) { - // Setting timeout to unlimited as the webdriver takes a longer time to run than most mocha test +suite('Mutating a block', function (done) { this.timeout(0); - // Setup Selenium for all of the tests suiteSetup(async function () { this.browser = await testSetup(testFileLocations.PLAYGROUND); }); - test('This test mutating a block creates more inputs', async function () { - await testingMutator(this.browser, screenDirection.LTR); + test('Mutating a block creates more inputs', async function () { + await testMutator(this.browser, screenDirection.LTR); }); }); -async function testingMutator(browser, delta) { - // Drag out print from flyout. - const controlIfFlyout = await dragBlockTypeFromFlyout( +async function testMutator(browser, delta) { + const mutatorBlock = await dragBlockTypeFromFlyout( browser, 'Logic', 'controls_if', delta * 50, 50, ); - // Click on the mutator and drag out else if block - const mutatorWheel = await browser.$( - '#blocklyDiv > div > svg.blocklySvg > g > g.blocklyBlockCanvas > g.blocklyDraggable.blocklySelected > g.blocklyIconGroup', - ); - await mutatorWheel.click(); + await openMutatorForBlock(browser, mutatorBlock); await browser.pause(PAUSE_TIME); - const elseIfFlyout = await browser.$( - '#blocklyDiv > div > svg.blocklySvg > g > g.blocklyBubbleCanvas > g > g:nth-child(2) > svg:nth-child(1) > g > g.blocklyFlyout > g > g.blocklyBlockCanvas > g:nth-child(3)', + await dragBlockFromMutatorFlyout( + browser, + mutatorBlock, + 'controls_if_elseif', + delta * 50, + 50, ); - await elseIfFlyout.dragAndDrop({x: delta * 50, y: 42}); await browser.pause(PAUSE_TIME); - await browser.pause(PAUSE_TIME); - // Get the ids for the blocks in the mutator - const blockIds = await browser.execute(() => { - const mutatorBlock = Blockly.getMainWorkspace().getAllBlocks()[0]; - // Adding the first element in the array is the original block id, the second is the first mutator block, and the third is the second mutator block - const blockIds = [ - mutatorBlock.id, - mutatorBlock.mutator.getWorkspace().getAllBlocks()[0].id, - mutatorBlock.mutator.getWorkspace().getAllBlocks()[1].id, - ]; - return blockIds; - }); + const {mutatorBlockId, ifQuarkId, elseIfQuarkId} = await browser.execute( + () => { + const mutatorBlock = Blockly.getMainWorkspace().getAllBlocks()[0]; + const quarkBlocks = mutatorBlock.mutator.getWorkspace().getAllBlocks(); + return { + mutatorBlockId: mutatorBlock.id, + ifQuarkId: quarkBlocks[0].id, + elseIfQuarkId: quarkBlocks[1].id, + }; + }, + ); - // The flyout block and the workspace block have the same id, so to get around that I pass in the selector to the connect function + // The flyout block and the workspace block have the same id, so to get + // around that I pass in the selector to the connect function. const dragBlockSelector = await browser.$( - '#blocklyDiv > div > svg.blocklySvg > g > g.blocklyBubbleCanvas > g > g:nth-child(2) > svg:nth-child(1) > g > g.blocklyBlockCanvas > g.blocklyDraggable', + '#blocklyDiv > div > svg.blocklySvg > g > g.blocklyBubbleCanvas > g > ' + + 'g:nth-child(2) > svg:nth-child(1) > g > g.blocklyBlockCanvas > ' + + 'g.blocklyDraggable', ); - // For some reason this needs a lot more time + // For some reason this needs a lot more time. await browser.pause(2000); - // Connect the mutator blocks await connect( browser, - blockIds[2], + await getBlockElementById(browser, elseIfQuarkId), 'PREVIOUS', - blockIds[1], + await getBlockElementById(browser, ifQuarkId), 'NEXT', - blockIds[0], + mutatorBlockId, dragBlockSelector, ); await browser.pause(PAUSE_TIME); - // Get the ids for block after mutating - const afterInputs = await browser.execute(() => { - const afterInputs = - Blockly.getMainWorkspace().getAllBlocks()[0].inputList.length; - return afterInputs; + const finalInputCount = await browser.execute(() => { + return Blockly.getMainWorkspace().getAllBlocks()[0].inputList.length; }); - chai.assert.equal(afterInputs, 4); + chai.assert.equal(finalInputCount, 4); } diff --git a/tests/browser/test/test_setup.js b/tests/browser/test/test_setup.js index 9bc660421..69e851b6c 100644 --- a/tests/browser/test/test_setup.js +++ b/tests/browser/test/test_setup.js @@ -295,7 +295,8 @@ async function getLocationOfBlockConnection( * @param draggedConnection The active connection on the block being dragged. * @param targetBlock The block to drag to. * @param targetConnection The connection to connect to on the target block. - * @param mutatorBlockId The block that holds the mutator icon or null if the target block is on the main workspace + * @param mutatorBlockId The block that holds the mutator icon or null if the + * target block is on the main workspace * @param dragBlockSelector The selector of the block to drag * @return A Promise that resolves when the actions are completed. */ @@ -308,34 +309,18 @@ async function connect( mutatorBlockId, dragBlockSelector, ) { - let draggedLocation; - let targetLocation; - - if (mutatorBlockId) { - draggedLocation = await getLocationOfBlockConnection( - browser, - draggedBlock, - draggedConnection, - mutatorBlockId, - ); - targetLocation = await getLocationOfBlockConnection( - browser, - targetBlock, - targetConnection, - mutatorBlockId, - ); - } else { - draggedLocation = await getLocationOfBlockConnection( - browser, - draggedBlock.id, - draggedConnection, - ); - targetLocation = await getLocationOfBlockConnection( - browser, - targetBlock.id, - targetConnection, - ); - } + const draggedLocation = await getLocationOfBlockConnection( + browser, + draggedBlock.id, + draggedConnection, + mutatorBlockId, + ); + const targetLocation = await getLocationOfBlockConnection( + browser, + targetBlock.id, + targetConnection, + mutatorBlockId, + ); const delta = { x: targetLocation.x - draggedLocation.x, @@ -405,6 +390,39 @@ async function dragBlockTypeFromFlyout(browser, categoryName, type, x, y) { return await getSelectedBlockElement(browser); } +/** + * Drags the specified block type from the mutator flyout of the given block and + * returns the root element of the block. + * + * @param browser The active WebdriverIO Browser object. + * @param mutatorBlock The block with the mutator attached that we want to drag + * a block from. + * @param type The type of the block to search for. + * @param x The x-distance to drag, as a delta from the block's + * initial location on screen. + * @param y The y-distance to drag, as a delta from the block's + * initial location on screen. + * @return A Promise that resolves to the root element of the newly + * created block. + */ +async function dragBlockFromMutatorFlyout(browser, mutatorBlock, type, x, y) { + const id = await browser.execute( + (mutatorBlockId, blockType) => { + return Blockly.getMainWorkspace() + .getBlockById(mutatorBlockId) + .mutator.getWorkspace() + .getFlyout() + .getWorkspace() + .getBlocksByType(blockType)[0].id; + }, + mutatorBlock.id, + type, + ); + const flyoutBlock = await getBlockElementById(browser, id); + await flyoutBlock.dragAndDrop({x: x, y: y}); + return await getSelectedBlockElement(browser); +} + /** * Right-click on the specified block, then click on the specified * context menu item. @@ -435,6 +453,18 @@ async function contextMenuSelect(browser, block, itemText) { await browser.pause(PAUSE_TIME); } +/** + * Opens the mutator bubble for the given block. + * + * @param browser The active WebdriverIO Browser object. + * @param block The block to click, as an interactable element. + * @return A Promise that resolves when the actions are complete. + */ +async function openMutatorForBlock(browser, block) { + const icon = await browser.$(`[data-id="${block.id}"] > g.blocklyIconGroup`); + await icon.click(); +} + /** * Get all blocks on the main workspace. Because the blocks have circular * references that can't be JSON-encoded they can't be returned directly, so @@ -490,9 +520,11 @@ module.exports = { getBlockTypeFromCategory, dragNthBlockFromFlyout, dragBlockTypeFromFlyout, + dragBlockFromMutatorFlyout, connect, switchRTL, contextMenuSelect, + openMutatorForBlock, screenDirection, getBlockTypeFromWorkspace, getAllBlocks, From 7ddbb83e68475376137349e05038013e53debb7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 08:58:13 -0700 Subject: [PATCH 45/92] chore(deps): Bump eslint from 8.43.0 to 8.48.0 (#7436) Bumps [eslint](https://github.com/eslint/eslint) from 8.43.0 to 8.48.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.43.0...v8.48.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 256 +++++++++++++++++++--------------------------- 1 file changed, 108 insertions(+), 148 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9178ecc7f..405cd3f3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,15 @@ "yargs": "^17.2.1" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", @@ -301,23 +310,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.0.tgz", - "integrity": "sha512-uiPeRISaglZnaZk8vwrjQZ1CxogZeY/4IYft6gBOTqu1WhVXWmCmZMWxUv2Q/pxSvPdp1JPaO62kLOcOkMqWrw==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -333,9 +342,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1892,9 +1901,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4016,27 +4025,27 @@ } }, "node_modules/eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -4046,7 +4055,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -4056,9 +4064,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -4134,9 +4141,9 @@ } }, "node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -4149,19 +4156,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4183,12 +4181,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -4224,15 +4222,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -4245,7 +4234,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -4617,7 +4606,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastest-stable-stringify": { @@ -5436,9 +5425,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -8492,17 +8481,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -12058,15 +12047,6 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", @@ -12348,6 +12328,12 @@ } }, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, "@babel/code-frame": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", @@ -12523,20 +12509,20 @@ } }, "@eslint-community/regexpp": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.0.tgz", - "integrity": "sha512-uiPeRISaglZnaZk8vwrjQZ1CxogZeY/4IYft6gBOTqu1WhVXWmCmZMWxUv2Q/pxSvPdp1JPaO62kLOcOkMqWrw==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", "dev": true }, "@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -12546,9 +12532,9 @@ } }, "@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true }, "@gulp-sourcemaps/identity-map": { @@ -13698,9 +13684,9 @@ } }, "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true }, "acorn-jsx": { @@ -15361,27 +15347,27 @@ "dev": true }, "eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -15391,7 +15377,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -15401,9 +15386,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { @@ -15461,36 +15445,28 @@ } }, "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } @@ -15508,14 +15484,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "esrecurse": { @@ -15525,16 +15493,14 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -15836,7 +15802,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fastest-stable-stringify": { @@ -16499,9 +16465,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -18950,17 +18916,17 @@ "dev": true }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" } }, "ordered-read-streams": { @@ -21736,12 +21702,6 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, - "word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true - }, "workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", From 7e9d1eb3ba780f523863ba94b4af5cce36292295 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 08:59:12 -0700 Subject: [PATCH 46/92] chore(deps): Bump typescript from 5.1.3 to 5.2.2 (#7440) Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.1.3 to 5.2.2. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v5.1.3...v5.2.2) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 405cd3f3b..b26aa5c80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11138,9 +11138,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -21008,9 +21008,9 @@ "dev": true }, "typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true }, "ua-parser-js": { From be809d9d984801f66a01da55f689d28c20cf6738 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Thu, 31 Aug 2023 01:02:58 +0200 Subject: [PATCH 47/92] refactor(tests): Migrate generator tests to `import` shims; delete `bootstrap.js` (#7414) * refactor(tests): Use shims instead of bootstrap to load Blockly - Modify tests/generators/index.html to import the test shims instead of using bootstrap.js to load Blockly. - Modify test/generators/webdriver.js to have it wait for the workspace to exist before calling loadSelected(). There was previously a race which index.html had been winning, but now webdriver.js is winning (and the tests failing because there is no workspace yet when start() is called. * chore(tests): Delete bootstrap.js etc. - Delete bootstrap.js, bootstrap_helper.js, and bootstrap_done.mjs. - Remove remaining references to bootstrap.js * refactor(build): Remove deps npm script buildDeps is now only needed by buildCompiled, not ever for runnning in uncompressed mode, so: - Remove the deps gulp task (and the deps npm script. - Have the minify task run buildJavaScript and buildDeps directly. Additionally, the buildAdvanceCompilationTest target hasn't needed deps.js for some time (if ever), so skip having it run buildDeps entirely. * refactor(build): Repatriate DEPS_FILE to build_tasks.js Since this is no longer used anywhere else it doesn't need to live in common.js. * fix(scripts): Remove vestigial references to deps.mocha.js * docs(tests): Add additional explanatory note --- core/main.js | 2 +- package.json | 2 - scripts/gulpfiles/build_tasks.js | 20 ++- scripts/gulpfiles/config.js | 5 - scripts/migration/js2ts | 1 - tests/bootstrap.js | 276 ------------------------------- tests/bootstrap_done.mjs | 41 ----- tests/bootstrap_helper.js | 56 ------- tests/generators/index.html | 46 ++++-- tests/generators/webdriver.js | 4 + tests/mocha/index.html | 6 +- 11 files changed, 50 insertions(+), 409 deletions(-) delete mode 100644 tests/bootstrap.js delete mode 100644 tests/bootstrap_done.mjs delete mode 100644 tests/bootstrap_helper.js diff --git a/core/main.js b/core/main.js index 9606a67b6..99d8f7da0 100644 --- a/core/main.js +++ b/core/main.js @@ -7,7 +7,7 @@ /** * @fileoverview The entrypoint for blockly_compressed.js. Provides * various backwards-compatibility hacks. Not used when loading - * in uncompiled (uncompressed) mode via bootstrap.js. + * in uncompiled (uncompressed) mode via blockly.loader.mjs. */ 'use strict'; diff --git a/package.json b/package.json index 6c3c886f1..f01be3205 100644 --- a/package.json +++ b/package.json @@ -24,14 +24,12 @@ "build-strict-log": "npm run build:strict > build-debug.log 2>&1 && tail -3 build-debug.log", "build:compiled": "exit 1 # Deprecated; use \"npm run minify\" instead.", "build:compressed": "exit 1 # Deprecated; use \"npm run minify\" instead.", - "build:deps": "exit 1 # Deprecated; use \"npm run deps\" instead.", "build:js": "exit 1 # Deprecated; use \"npm run tsc\" instead.", "build:langfiles": "exit 1 # Deprecated; use \"npm run langfiles\" instead.", "bump": "npm --no-git-tag-version version 4.$(date +'%Y%m%d').0", "clean": "gulp clean", "deployDemos": "npm ci && gulp deployDemos", "deployDemos:beta": "npm ci && gulp deployDemosBeta", - "deps": "gulp deps", "docs": "gulp docs", "format": "prettier --write .", "format:check": "prettier --check .", diff --git a/scripts/gulpfiles/build_tasks.js b/scripts/gulpfiles/build_tasks.js index 0d7517750..75b0ebcf7 100644 --- a/scripts/gulpfiles/build_tasks.js +++ b/scripts/gulpfiles/build_tasks.js @@ -22,7 +22,7 @@ const closureCompiler = require('google-closure-compiler').gulp(); const argv = require('yargs').argv; const {rimraf} = require('rimraf'); -const {BUILD_DIR, DEPS_FILE, RELEASE_DIR, TSC_OUTPUT_DIR, TYPINGS_BUILD_DIR} = require('./config'); +const {BUILD_DIR, RELEASE_DIR, TSC_OUTPUT_DIR, TYPINGS_BUILD_DIR} = require('./config'); const {getPackageJson} = require('./helper_tasks'); const {posixPath, quote} = require('../helpers'); @@ -43,6 +43,11 @@ const PYTHON = process.platform === 'win32' ? 'python' : 'python3'; */ const COMPILED_SUFFIX = '_compressed'; +/** + * Dependencies file (used by buildCompiled for chunking. + */ +const DEPS_FILE = path.join(BUILD_DIR, 'deps.js'); + /** * Name of an object to be used as a shared "global" namespace by * chunks generated by the Closure Compiler with the @@ -302,8 +307,9 @@ function buildJavaScript(done) { } /** - * This task updates DEPS_FILE (deps.js), used by the debug module - * loader (via bootstrap.js) when loading Blockly in uncompiled mode. + * This task updates DEPS_FILE (deps.js), used by + * closure-calculate-chunks when determining how to organise .js + * source files into chunks. * * Prerequisite: buildJavaScript. */ @@ -354,7 +360,6 @@ error message above, try running: reject(error); } else { log(stderr); - // Anything not about mocha goes in DEPS_FILE. fs.writeFileSync(DEPS_FILE, stdout); resolve(); } @@ -710,7 +715,7 @@ ${Object.keys(exports).map((name) => ` ${name},`).join('\n')} * This task builds Blockly core, blocks and generators together and uses * Closure Compiler's ADVANCED_COMPILATION mode. * - * Prerequisite: buildDeps. + * Prerequisite: buildJavaScript. */ function buildAdvancedCompilationTest() { // If main_compressed.js exists (from a previous run) delete it so that @@ -761,14 +766,13 @@ function cleanBuildDir() { exports.cleanBuildDir = cleanBuildDir; exports.langfiles = buildLangfiles; // Build build/msg/*.js from msg/json/*. exports.tsc = buildJavaScript; -exports.deps = gulp.series(exports.tsc, buildDeps); -exports.minify = gulp.series(exports.deps, buildCompiled, buildShims); +exports.minify = gulp.series(exports.tsc, buildDeps, buildCompiled, buildShims); exports.build = gulp.parallel(exports.minify, exports.langfiles); // Manually-invokable targets, with prerequisites where required. exports.messages = generateMessages; // Generate msg/json/en.json et al. exports.buildAdvancedCompilationTest = - gulp.series(exports.deps, buildAdvancedCompilationTest); + gulp.series(exports.tsc, buildAdvancedCompilationTest); // Targets intended only for invocation by scripts; may omit prerequisites. exports.onlyBuildAdvancedCompilationTest = buildAdvancedCompilationTest; diff --git a/scripts/gulpfiles/config.js b/scripts/gulpfiles/config.js index 02a0499c4..e1756d96e 100644 --- a/scripts/gulpfiles/config.js +++ b/scripts/gulpfiles/config.js @@ -19,15 +19,10 @@ const path = require('path'); // - tests/scripts/compile_typings.sh // - tests/scripts/check_metadata.sh // - tests/scripts/update_metadata.sh -// - tests/bootstrap.js (for location of deps.js) -// - tests/mocha/index.html (for location of deps.mocha.js) // Directory to write compiled output to. exports.BUILD_DIR = 'build'; -// Dependencies file (used by bootstrap.js in uncompiled mode): -exports.DEPS_FILE = path.join(exports.BUILD_DIR, 'deps.js'); - // Directory to write typings output to. exports.TYPINGS_BUILD_DIR = path.join(exports.BUILD_DIR, 'declarations'); diff --git a/scripts/migration/js2ts b/scripts/migration/js2ts index 4ddfd18ba..a8d941e01 100755 --- a/scripts/migration/js2ts +++ b/scripts/migration/js2ts @@ -54,7 +54,6 @@ goog.addDependency = function(relPath, provides, _requires, opt_loadFlags) { // Load deps files relative to this script's location. require(path.resolve(__dirname, '../../build/deps.js')); -require(path.resolve(__dirname, '../../build/deps.mocha.js')); ////////////////////////////////////////////////////////////////////// // Process files mentioned on the command line. diff --git a/tests/bootstrap.js b/tests/bootstrap.js deleted file mode 100644 index dace6e3a1..000000000 --- a/tests/bootstrap.js +++ /dev/null @@ -1,276 +0,0 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Bootstrap code to load Blockly, typically in - * uncompressed mode. - * - * Load this file in a `, - ); - - // Prevent spurious transpilation warnings. - document.write(''); - - // Load dependency graph info from the specified deps files - - // typically just build/deps.js. To update deps after changing - // any module's goog.requires / imports, run `npm run build:deps`. - for (const depsFile of options.depsFiles) { - document.write(``); - } - - // Assemble a list of module targets to bootstrap. - // - // The first group of targets are those listed in options.modules - // and options.requires. These are recorded on bootstrapInfo so - // so bootstrap_helper.js can goog.require() them to force loading - // to complete. - // - // The next target is a fake one that will load - // bootstrap_helper.js. We generate a call to goog.addDependency - // to tell the debug module loader that it can be loaded via a - // fake module name, and that it depends on all the targets in the - // first group (and indeed bootstrap_helper.js will make a call to - // goog.require for each one). - // - // We then create another target for each of options.scripts, - // again generating calls to goog.addDependency for each one - // making it dependent on the previous one. - let requires = (window.bootstrapInfo.requires = [ - ...options.modules.map((module) => module.id), - ...options.requires, - ]); - - const scripts = ['tests/bootstrap_helper.js', ...options.scripts]; - const scriptDeps = []; - for (const script of scripts) { - const fakeModuleName = `script.${script.replace(/[./]/g, '-')}`; - scriptDeps.push( - `goog.addDependency(${quote('../../../../' + script)}, ` + - `[${quote(fakeModuleName)}], [${requires.map(quote).join()}], ` + - `{'lang': 'es6'});`, - ); - requires = [fakeModuleName]; - } - - // Finally, write out a script containing the generated - // goog.addDependency calls and a call to goog.bootstrap - // requesting the loading of the final target, which will cause - // all the previous ones to be loaded recursively. Wrap this in a - // promise and save it so it can be awaited in bootstrap_done.mjs. - document.write(``); - } else { - // We need to load Blockly in compressed mode. Load - // blockly_compressed.js et al. using `); - } - } - - return; // All done. Only helper functions after this point. - - /** - * Convert a string into a string literal. Strictly speaking we - * only need to escape backslash, \r, \n, \u2028 (line separator), - * \u2029 (paragraph separator) and whichever quote character we're - * using, but for simplicity we escape all the control characters. - * - * Based on https://github.com/google/CodeCity/blob/master/server/code.js - * - * @param {string} str The string to convert. - * @return {string} The value s as a eval-able string literal. - */ - function quote(str) { - /* eslint-disable no-control-regex, no-multi-spaces */ - /** Regexp for characters to be escaped in a single-quoted string. */ - const singleRE = /[\x00-\x1f\\\u2028\u2029']/g; - - /** Map of control character replacements. */ - const replacements = { - '\x00': '\\0', - '\x01': '\\x01', - '\x02': '\\x02', - '\x03': '\\x03', - '\x04': '\\x04', - '\x05': '\\x05', - '\x06': '\\x06', - '\x07': '\\x07', - '\x08': '\\b', - '\x09': '\\t', - '\x0a': '\\n', - '\x0b': '\\v', - '\x0c': '\\f', - '\x0d': '\\r', - '\x0e': '\\x0e', - '\x0f': '\\x0f', - '"': '\\"', - "'": "\\'", - '\\': '\\\\', - '\u2028': '\\u2028', - '\u2029': '\\u2029', - }; - /* eslint-enable no-control-regex, no-multi-spaces */ - - return "'" + str.replace(singleRE, (c) => replacements[c]) + "'"; - } -})(); diff --git a/tests/bootstrap_done.mjs b/tests/bootstrap_done.mjs deleted file mode 100644 index 86ebcb4ad..000000000 --- a/tests/bootstrap_done.mjs +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @license - * Copyright 2022 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Finishes loading Blockly and exports it as this - * module's default export. - * - * It is exported as the default export to avoid having to - * re-export each property on Blockly individually, because you - * can't do: - * - * export * from ; // SYNTAX ERROR - * - * You must use a - - + diff --git a/tests/generators/webdriver.js b/tests/generators/webdriver.js index a422323ec..71170f6ef 100644 --- a/tests/generators/webdriver.js +++ b/tests/generators/webdriver.js @@ -73,6 +73,10 @@ async function runGeneratorsInBrowser(outputDir) { console.log('Loading url: ' + url); await browser.url(url); + await browser + .$('.blocklySvg .blocklyWorkspace > .blocklyBlockCanvas') + .waitForExist({timeout: 2000}); + await browser.execute(function() { checkAll(); loadSelected(); diff --git a/tests/mocha/index.html b/tests/mocha/index.html index f94eabfa1..160c74b0b 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -18,9 +18,9 @@
- + From b0a7c004a9eb30f08aa67d8b11f2a25d563fb5de Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Thu, 31 Aug 2023 01:24:47 +0200 Subject: [PATCH 48/92] refactor(build): Delete Closure Library (#7415) * fix(build): Restore erroneously-deleted filter function This was deleted in PR #7406 as it was mainly being used to filter core/ vs. test/mocha/ deps into separate deps files - but it turns out also to be used for filtering error messages too. Oops. * refactor(tests): Migrate advanced compilation test to ES Modules * refactor(build): Migrate main.js to TypeScript This turns out to be pretty straight forward, even if it would cause crashing if one actually tried to import this module instead of just feeding it to Closure Compiler. * chore(build): Remove goog.declareModuleId calls Replace goog.declareModuleId calls with a comment recording the former module ID for posterity (or at least until we decide how to reformat the renamings file. * chore(tests): Delete closure/goog/* For the moment we still need something to serve as base.js for the benefit of closure-make-deps, so we keep a vestigial base.js around, containing only the @provideGoog declaration. * refactor(build): Remove vestigial base.js By changing slightly the command line arguments to closure-make-deps and closure-calculate-chunks the need to have any base.js is eliminated. * chore: Typo fix for PR #7415 --- blocks/blocks.ts | 3 +- blocks/colour.ts | 3 +- blocks/lists.ts | 3 +- blocks/logic.ts | 3 +- blocks/loops.ts | 3 +- blocks/math.ts | 3 +- blocks/procedures.ts | 3 +- blocks/text.ts | 3 +- blocks/variables.ts | 3 +- blocks/variables_dynamic.ts | 3 +- closure/goog/base.js | 3882 ----------------- closure/goog/base_minimal.js | 112 - closure/goog/goog.js | 102 - core/block.ts | 3 +- core/block_animations.ts | 3 +- core/block_dragger.ts | 3 +- core/block_svg.ts | 3 +- core/blockly.ts | 3 +- core/blockly_options.ts | 3 +- core/blocks.ts | 3 +- core/browser_events.ts | 3 +- core/bubble_dragger.ts | 3 +- core/bump_objects.ts | 3 +- core/clipboard.ts | 3 +- core/common.ts | 3 +- core/component_manager.ts | 3 +- core/config.ts | 3 +- core/connection.ts | 3 +- core/connection_checker.ts | 3 +- core/connection_db.ts | 3 +- core/connection_type.ts | 3 +- core/constants.ts | 3 +- core/contextmenu.ts | 3 +- core/contextmenu_items.ts | 3 +- core/contextmenu_registry.ts | 3 +- core/css.ts | 3 +- core/delete_area.ts | 3 +- core/dialog.ts | 3 +- core/drag_target.ts | 3 +- core/dropdowndiv.ts | 3 +- core/events/events.ts | 3 +- core/events/events_abstract.ts | 3 +- core/events/events_block_base.ts | 3 +- core/events/events_block_change.ts | 3 +- core/events/events_block_create.ts | 3 +- core/events/events_block_delete.ts | 3 +- core/events/events_block_drag.ts | 3 +- .../events_block_field_intermediate_change.ts | 3 +- core/events/events_block_move.ts | 3 +- core/events/events_bubble_open.ts | 3 +- core/events/events_click.ts | 3 +- core/events/events_comment_base.ts | 3 +- core/events/events_comment_change.ts | 3 +- core/events/events_comment_create.ts | 3 +- core/events/events_comment_delete.ts | 3 +- core/events/events_comment_move.ts | 3 +- core/events/events_marker_move.ts | 3 +- core/events/events_selected.ts | 3 +- core/events/events_theme_change.ts | 3 +- core/events/events_toolbox_item_select.ts | 3 +- core/events/events_trashcan_open.ts | 3 +- core/events/events_ui_base.ts | 3 +- core/events/events_var_base.ts | 3 +- core/events/events_var_create.ts | 3 +- core/events/events_var_delete.ts | 3 +- core/events/events_var_rename.ts | 3 +- core/events/events_viewport.ts | 3 +- core/events/utils.ts | 3 +- core/events/workspace_events.ts | 3 +- core/extensions.ts | 3 +- core/field.ts | 3 +- core/field_angle.ts | 3 +- core/field_checkbox.ts | 3 +- core/field_colour.ts | 3 +- core/field_dropdown.ts | 3 +- core/field_image.ts | 3 +- core/field_input.ts | 3 +- core/field_label.ts | 3 +- core/field_label_serializable.ts | 3 +- core/field_multilineinput.ts | 3 +- core/field_number.ts | 3 +- core/field_registry.ts | 3 +- core/field_textinput.ts | 3 +- core/field_variable.ts | 3 +- core/flyout_base.ts | 3 +- core/flyout_button.ts | 3 +- core/flyout_horizontal.ts | 3 +- core/flyout_metrics_manager.ts | 3 +- core/flyout_vertical.ts | 3 +- core/generator.ts | 3 +- core/gesture.ts | 3 +- core/grid.ts | 3 +- core/icons/comment_icon.ts | 3 +- core/icons/mutator_icon.ts | 3 +- core/icons/warning_icon.ts | 3 +- core/inject.ts | 3 +- core/inputs/input.ts | 3 +- core/inputs/input_types.ts | 3 +- core/insertion_marker_manager.ts | 3 +- core/interfaces/i_ast_node_location.ts | 3 +- core/interfaces/i_ast_node_location_svg.ts | 3 +- .../i_ast_node_location_with_block.ts | 3 +- core/interfaces/i_autohideable.ts | 3 +- core/interfaces/i_block_dragger.ts | 3 +- core/interfaces/i_bounded_element.ts | 3 +- core/interfaces/i_bubble.ts | 3 +- core/interfaces/i_collapsible_toolbox_item.ts | 3 +- core/interfaces/i_component.ts | 3 +- core/interfaces/i_connection_checker.ts | 3 +- core/interfaces/i_contextmenu.ts | 3 +- core/interfaces/i_copyable.ts | 3 +- core/interfaces/i_deletable.ts | 3 +- core/interfaces/i_delete_area.ts | 3 +- core/interfaces/i_drag_target.ts | 3 +- core/interfaces/i_draggable.ts | 3 +- core/interfaces/i_flyout.ts | 3 +- core/interfaces/i_keyboard_accessible.ts | 3 +- core/interfaces/i_metrics_manager.ts | 3 +- core/interfaces/i_movable.ts | 3 +- core/interfaces/i_positionable.ts | 3 +- core/interfaces/i_procedure_block.ts | 3 +- core/interfaces/i_registrable.ts | 3 +- core/interfaces/i_selectable.ts | 3 +- core/interfaces/i_selectable_toolbox_item.ts | 3 +- core/interfaces/i_serializer.ts | 3 +- core/interfaces/i_styleable.ts | 3 +- core/interfaces/i_toolbox.ts | 3 +- core/interfaces/i_toolbox_item.ts | 3 +- core/internal_constants.ts | 3 +- core/keyboard_nav/ast_node.ts | 3 +- core/keyboard_nav/basic_cursor.ts | 3 +- core/keyboard_nav/cursor.ts | 3 +- core/keyboard_nav/marker.ts | 3 +- core/keyboard_nav/tab_navigate_cursor.ts | 3 +- core/{main.js => main.ts} | 23 +- core/marker_manager.ts | 3 +- core/menu.ts | 3 +- core/menuitem.ts | 3 +- core/metrics_manager.ts | 3 +- core/msg.ts | 3 +- core/names.ts | 3 +- core/options.ts | 3 +- core/positionable_helpers.ts | 3 +- core/procedures.ts | 3 +- core/registry.ts | 3 +- core/rendered_connection.ts | 3 +- core/renderers/common/block_rendering.ts | 3 +- core/renderers/common/constants.ts | 3 +- core/renderers/common/drawer.ts | 3 +- core/renderers/common/i_path_object.ts | 3 +- core/renderers/common/info.ts | 3 +- core/renderers/common/marker_svg.ts | 3 +- core/renderers/common/path_object.ts | 3 +- core/renderers/common/renderer.ts | 3 +- core/renderers/geras/constants.ts | 3 +- core/renderers/geras/drawer.ts | 3 +- core/renderers/geras/geras.ts | 3 +- core/renderers/geras/highlight_constants.ts | 3 +- core/renderers/geras/highlighter.ts | 3 +- core/renderers/geras/info.ts | 3 +- .../geras/measurables/inline_input.ts | 3 +- .../geras/measurables/statement_input.ts | 3 +- core/renderers/geras/path_object.ts | 3 +- core/renderers/geras/renderer.ts | 3 +- core/renderers/measurables/base.ts | 3 +- core/renderers/measurables/bottom_row.ts | 3 +- core/renderers/measurables/connection.ts | 3 +- .../measurables/external_value_input.ts | 3 +- core/renderers/measurables/field.ts | 3 +- core/renderers/measurables/hat.ts | 3 +- core/renderers/measurables/icon.ts | 3 +- core/renderers/measurables/in_row_spacer.ts | 3 +- core/renderers/measurables/inline_input.ts | 3 +- .../renderers/measurables/input_connection.ts | 3 +- core/renderers/measurables/input_row.ts | 3 +- core/renderers/measurables/jagged_edge.ts | 3 +- core/renderers/measurables/next_connection.ts | 3 +- .../measurables/output_connection.ts | 3 +- .../measurables/previous_connection.ts | 3 +- core/renderers/measurables/round_corner.ts | 3 +- core/renderers/measurables/row.ts | 3 +- core/renderers/measurables/spacer_row.ts | 3 +- core/renderers/measurables/square_corner.ts | 3 +- core/renderers/measurables/statement_input.ts | 3 +- core/renderers/measurables/top_row.ts | 3 +- core/renderers/measurables/types.ts | 3 +- core/renderers/minimalist/constants.ts | 3 +- core/renderers/minimalist/drawer.ts | 3 +- core/renderers/minimalist/info.ts | 3 +- core/renderers/minimalist/minimalist.ts | 3 +- core/renderers/minimalist/renderer.ts | 3 +- core/renderers/thrasos/info.ts | 3 +- core/renderers/thrasos/renderer.ts | 3 +- core/renderers/thrasos/thrasos.ts | 3 +- core/renderers/zelos/constants.ts | 3 +- core/renderers/zelos/drawer.ts | 3 +- core/renderers/zelos/info.ts | 3 +- core/renderers/zelos/marker_svg.ts | 3 +- .../renderers/zelos/measurables/bottom_row.ts | 3 +- core/renderers/zelos/measurables/inputs.ts | 3 +- .../zelos/measurables/row_elements.ts | 3 +- core/renderers/zelos/measurables/top_row.ts | 3 +- core/renderers/zelos/path_object.ts | 3 +- core/renderers/zelos/renderer.ts | 3 +- core/renderers/zelos/zelos.ts | 3 +- core/scrollbar.ts | 3 +- core/scrollbar_pair.ts | 3 +- core/serialization.ts | 3 +- core/serialization/blocks.ts | 3 +- core/serialization/exceptions.ts | 3 +- core/serialization/priorities.ts | 3 +- core/serialization/registry.ts | 3 +- core/serialization/variables.ts | 3 +- core/serialization/workspaces.ts | 3 +- core/shortcut_items.ts | 3 +- core/shortcut_registry.ts | 3 +- core/theme.ts | 3 +- core/theme/classic.ts | 3 +- core/theme/themes.ts | 3 +- core/theme/zelos.ts | 3 +- core/theme_manager.ts | 3 +- core/toolbox/category.ts | 3 +- core/toolbox/collapsible_category.ts | 3 +- core/toolbox/separator.ts | 3 +- core/toolbox/toolbox.ts | 3 +- core/toolbox/toolbox_item.ts | 3 +- core/tooltip.ts | 3 +- core/touch.ts | 3 +- core/trashcan.ts | 3 +- core/utils.ts | 3 +- core/utils/aria.ts | 3 +- core/utils/array.ts | 3 +- core/utils/colour.ts | 3 +- core/utils/coordinate.ts | 3 +- core/utils/deprecation.ts | 3 +- core/utils/dom.ts | 3 +- core/utils/idgenerator.ts | 3 +- core/utils/keycodes.ts | 3 +- core/utils/math.ts | 3 +- core/utils/metrics.ts | 3 +- core/utils/object.ts | 3 +- core/utils/parsing.ts | 3 +- core/utils/rect.ts | 3 +- core/utils/size.ts | 3 +- core/utils/string.ts | 3 +- core/utils/style.ts | 3 +- core/utils/svg.ts | 3 +- core/utils/svg_math.ts | 3 +- core/utils/svg_paths.ts | 3 +- core/utils/toolbox.ts | 3 +- core/utils/useragent.ts | 3 +- core/utils/xml.ts | 3 +- core/variable_map.ts | 3 +- core/variable_model.ts | 3 +- core/variables.ts | 3 +- core/variables_dynamic.ts | 3 +- core/widgetdiv.ts | 3 +- core/workspace.ts | 3 +- core/workspace_audio.ts | 3 +- core/workspace_comment.ts | 3 +- core/workspace_comment_svg.ts | 3 +- core/workspace_dragger.ts | 3 +- core/workspace_svg.ts | 3 +- core/xml.ts | 3 +- core/zoom_controls.ts | 3 +- generators/dart.js | 3 +- generators/dart/colour.js | 3 +- generators/dart/dart_generator.js | 3 +- generators/dart/lists.js | 3 +- generators/dart/logic.js | 3 +- generators/dart/loops.js | 3 +- generators/dart/math.js | 3 +- generators/dart/procedures.js | 3 +- generators/dart/text.js | 3 +- generators/dart/variables.js | 3 +- generators/dart/variables_dynamic.js | 3 +- generators/javascript.js | 3 +- generators/javascript/colour.js | 3 +- generators/javascript/javascript_generator.js | 3 +- generators/javascript/lists.js | 3 +- generators/javascript/logic.js | 3 +- generators/javascript/loops.js | 3 +- generators/javascript/math.js | 3 +- generators/javascript/procedures.js | 3 +- generators/javascript/text.js | 3 +- generators/javascript/variables.js | 3 +- generators/javascript/variables_dynamic.js | 3 +- generators/lua.js | 3 +- generators/lua/colour.js | 3 +- generators/lua/lists.js | 3 +- generators/lua/logic.js | 3 +- generators/lua/loops.js | 3 +- generators/lua/lua_generator.js | 3 +- generators/lua/math.js | 3 +- generators/lua/procedures.js | 3 +- generators/lua/text.js | 3 +- generators/lua/variables.js | 3 +- generators/lua/variables_dynamic.js | 3 +- generators/php.js | 3 +- generators/php/colour.js | 3 +- generators/php/lists.js | 3 +- generators/php/logic.js | 3 +- generators/php/loops.js | 3 +- generators/php/math.js | 3 +- generators/php/php_generator.js | 3 +- generators/php/procedures.js | 3 +- generators/php/text.js | 3 +- generators/php/variables.js | 3 +- generators/php/variables_dynamic.js | 3 +- generators/python.js | 3 +- generators/python/colour.js | 3 +- generators/python/lists.js | 3 +- generators/python/logic.js | 3 +- generators/python/loops.js | 3 +- generators/python/math.js | 3 +- generators/python/procedures.js | 3 +- generators/python/python_generator.js | 3 +- generators/python/text.js | 3 +- generators/python/variables.js | 3 +- generators/python/variables_dynamic.js | 3 +- scripts/gulpfiles/build_tasks.js | 41 +- tests/compile/main.js | 34 +- tests/compile/test_blocks.js | 7 +- 323 files changed, 367 insertions(+), 4782 deletions(-) delete mode 100644 closure/goog/base.js delete mode 100644 closure/goog/base_minimal.js delete mode 100644 closure/goog/goog.js rename core/{main.js => main.ts} (82%) diff --git a/blocks/blocks.ts b/blocks/blocks.ts index 7ac097990..1a35c0130 100644 --- a/blocks/blocks.ts +++ b/blocks/blocks.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks'); +// Former goog.module ID: Blockly.libraryBlocks import * as colour from './colour.js'; import * as lists from './lists.js'; diff --git a/blocks/colour.ts b/blocks/colour.ts index f9fb7e418..e57e4ba9b 100644 --- a/blocks/colour.ts +++ b/blocks/colour.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.colour'); +// Former goog.module ID: Blockly.libraryBlocks.colour import { createBlockDefinitionsFromJsonArray, diff --git a/blocks/lists.ts b/blocks/lists.ts index e1c7c0a7a..a85e39352 100644 --- a/blocks/lists.ts +++ b/blocks/lists.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.lists'); +// Former goog.module ID: Blockly.libraryBlocks.lists import * as fieldRegistry from '../core/field_registry.js'; import * as xmlUtils from '../core/utils/xml.js'; diff --git a/blocks/logic.ts b/blocks/logic.ts index 4c1034261..3e5b807eb 100644 --- a/blocks/logic.ts +++ b/blocks/logic.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.logic'); +// Former goog.module ID: Blockly.libraryBlocks.logic import * as Events from '../core/events/events.js'; import * as Extensions from '../core/extensions.js'; diff --git a/blocks/loops.ts b/blocks/loops.ts index 77090343e..80e2bf305 100644 --- a/blocks/loops.ts +++ b/blocks/loops.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.loops'); +// Former goog.module ID: Blockly.libraryBlocks.loops import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js'; import type {Block} from '../core/block.js'; diff --git a/blocks/math.ts b/blocks/math.ts index 7232888d2..c96aef3c2 100644 --- a/blocks/math.ts +++ b/blocks/math.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.math'); +// Former goog.module ID: Blockly.libraryBlocks.math import * as Extensions from '../core/extensions.js'; import type {FieldDropdown} from '../core/field_dropdown.js'; diff --git a/blocks/procedures.ts b/blocks/procedures.ts index 54d635589..058eb0cd3 100644 --- a/blocks/procedures.ts +++ b/blocks/procedures.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.procedures'); +// Former goog.module ID: Blockly.libraryBlocks.procedures import * as ContextMenu from '../core/contextmenu.js'; import * as Events from '../core/events/events.js'; diff --git a/blocks/text.ts b/blocks/text.ts index fca84a519..eba64d9ab 100644 --- a/blocks/text.ts +++ b/blocks/text.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.texts'); +// Former goog.module ID: Blockly.libraryBlocks.texts import * as Extensions from '../core/extensions.js'; import * as fieldRegistry from '../core/field_registry.js'; diff --git a/blocks/variables.ts b/blocks/variables.ts index d0a2814bd..81c91112c 100644 --- a/blocks/variables.ts +++ b/blocks/variables.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.variables'); +// Former goog.module ID: Blockly.libraryBlocks.variables import * as ContextMenu from '../core/contextmenu.js'; import * as Extensions from '../core/extensions.js'; diff --git a/blocks/variables_dynamic.ts b/blocks/variables_dynamic.ts index fed8cbcb3..3605339f6 100644 --- a/blocks/variables_dynamic.ts +++ b/blocks/variables_dynamic.ts @@ -4,8 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as goog from '../closure/goog/goog.js'; -goog.declareModuleId('Blockly.libraryBlocks.variablesDynamic'); +// Former goog.module ID: Blockly.libraryBlocks.variablesDynamic import * as ContextMenu from '../core/contextmenu.js'; import * as Extensions from '../core/extensions.js'; diff --git a/closure/goog/base.js b/closure/goog/base.js deleted file mode 100644 index 9f5f18a6f..000000000 --- a/closure/goog/base.js +++ /dev/null @@ -1,3882 +0,0 @@ -/** - * @license - * Copyright The Closure Library Authors. - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Bootstrap for the Google JS Library (Closure). - * - * In uncompiled mode base.js will attempt to load Closure's deps file, unless - * the global CLOSURE_NO_DEPS is set to true. This allows projects - * to include their own deps file(s) from different locations. - * - * Avoid including base.js more than once. This is strictly discouraged and not - * supported. goog.require(...) won't work properly in that case. - * - * @provideGoog - */ - - -/** - * @define {boolean} Overridden to true by the compiler. - */ -var COMPILED = false; - - -/** - * Base namespace for the Closure library. Checks to see goog is already - * defined in the current scope before assigning to prevent clobbering if - * base.js is loaded more than once. - * - * @const - */ -var goog = goog || {}; - -/** - * Reference to the global object. - * https://www.ecma-international.org/ecma-262/9.0/index.html#sec-global-object - * - * More info on this implementation here: - * https://docs.google.com/document/d/1NAeW4Wk7I7FV0Y2tcUFvQdGMc89k2vdgSXInw8_nvCI/edit - * - * @const - * @suppress {undefinedVars} self won't be referenced unless `this` is falsy. - * @type {!Global} - */ -goog.global = - // Check `this` first for backwards compatibility. - // Valid unless running as an ES module or in a function wrapper called - // without setting `this` properly. - // Note that base.js can't usefully be imported as an ES module, but it may - // be compiled into bundles that are loadable as ES modules. - this || - // https://developer.mozilla.org/en-US/docs/Web/API/Window/self - // For in-page browser environments and workers. - self; - - -/** - * A hook for overriding the define values in uncompiled mode. - * - * In uncompiled mode, `CLOSURE_UNCOMPILED_DEFINES` may be defined before - * loading base.js. If a key is defined in `CLOSURE_UNCOMPILED_DEFINES`, - * `goog.define` will use the value instead of the default value. This - * allows flags to be overwritten without compilation (this is normally - * accomplished with the compiler's "define" flag). - * - * Example: - *
- *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
- * 
- * - * @type {Object|undefined} - */ -goog.global.CLOSURE_UNCOMPILED_DEFINES; - - -/** - * A hook for overriding the define values in uncompiled or compiled mode, - * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In - * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. - * - * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or - * string literals or the compiler will emit an error. - * - * While any @define value may be set, only those set with goog.define will be - * effective for uncompiled code. - * - * Example: - *
- *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
- * 
- * - * Currently the Closure Compiler will only recognize very simple definitions of - * this value when looking for values to apply to compiled code and ignore all - * other references. Specifically, it looks the value defined at the variable - * declaration, as with the example above. - * - * TODO(user): Improve the recognized definitions. - * - * @type {!Object|null|undefined} - */ -goog.global.CLOSURE_DEFINES; - - -/** - * Builds an object structure for the provided namespace path, ensuring that - * names that already exist are not overwritten. For example: - * "a.b.c" -> a = {};a.b={};a.b.c={}; - * Used by goog.provide and goog.exportSymbol. - * @param {string} name The name of the object that this file defines. - * @param {*=} object The object to expose at the end of the path. - * @param {boolean=} overwriteImplicit If object is set and a previous call - * implicitly constructed the namespace given by name, this parameter - * controls whether object should overwrite the implicitly constructed - * namespace or be merged into it. Defaults to false. - * @param {?Object=} objectToExportTo The object to add the path to; if this - * field is not specified, its value defaults to `goog.global`. - * @private - */ -goog.exportPath_ = function(name, object, overwriteImplicit, objectToExportTo) { - var parts = name.split('.'); - var cur = objectToExportTo || goog.global; - - // Internet Explorer exhibits strange behavior when throwing errors from - // methods externed in this manner. See the testExportSymbolExceptions in - // base_test.html for an example. - if (!(parts[0] in cur) && typeof cur.execScript != 'undefined') { - cur.execScript('var ' + parts[0]); - } - - for (var part; parts.length && (part = parts.shift());) { - if (!parts.length && object !== undefined) { - if (!overwriteImplicit && goog.isObject(object) && - goog.isObject(cur[part])) { - // Merge properties on object (the input parameter) with the existing - // implicitly defined namespace, so as to not clobber previously - // defined child namespaces. - for (var prop in object) { - if (object.hasOwnProperty(prop)) { - cur[part][prop] = object[prop]; - } - } - } else { - // Either there is no existing implicit namespace, or overwriteImplicit - // is set to true, so directly assign object (the input parameter) to - // the namespace. - cur[part] = object; - } - } else if (cur[part] && cur[part] !== Object.prototype[part]) { - cur = cur[part]; - } else { - cur = cur[part] = {}; - } - } -}; - - -/** - * Defines a named value. In uncompiled mode, the value is retrieved from - * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and - * has the property specified, and otherwise used the defined defaultValue. - * When compiled the default can be overridden using the compiler options or the - * value set in the CLOSURE_DEFINES object. Returns the defined value so that it - * can be used safely in modules. Note that the value type MUST be either - * boolean, number, or string. - * - * @param {string} name The distinguished name to provide. - * @param {T} defaultValue - * @return {T} The defined value. - * @template T - */ -goog.define = function(name, defaultValue) { - var value = defaultValue; - if (!COMPILED) { - var uncompiledDefines = goog.global.CLOSURE_UNCOMPILED_DEFINES; - var defines = goog.global.CLOSURE_DEFINES; - if (uncompiledDefines && - // Anti DOM-clobbering runtime check (b/37736576). - /** @type {?} */ (uncompiledDefines).nodeType === undefined && - Object.prototype.hasOwnProperty.call(uncompiledDefines, name)) { - value = uncompiledDefines[name]; - } else if ( - defines && - // Anti DOM-clobbering runtime check (b/37736576). - /** @type {?} */ (defines).nodeType === undefined && - Object.prototype.hasOwnProperty.call(defines, name)) { - value = defines[name]; - } - } - return value; -}; - - -/** - * @define {number} Integer year indicating the set of browser features that are - * guaranteed to be present. This is defined to include exactly features that - * work correctly on all "modern" browsers that are stable on January 1 of the - * specified year. For example, - * ```js - * if (goog.FEATURESET_YEAR >= 2019) { - * // use APIs known to be available on all major stable browsers Jan 1, 2019 - * } else { - * // polyfill for older browsers - * } - * ``` - * This is intended to be the primary define for removing - * unnecessary browser compatibility code (such as ponyfills and workarounds), - * and should inform the default value for most other defines: - * ```js - * const ASSUME_NATIVE_PROMISE = - * goog.define('ASSUME_NATIVE_PROMISE', goog.FEATURESET_YEAR >= 2016); - * ``` - * - * The default assumption is that IE9 is the lowest supported browser, which was - * first available Jan 1, 2012. - * - * TODO(user): Reference more thorough documentation when it's available. - */ -goog.FEATURESET_YEAR = goog.define('goog.FEATURESET_YEAR', 2012); - - -/** - * @define {boolean} DEBUG is provided as a convenience so that debugging code - * that should not be included in a production. It can be easily stripped - * by specifying --define goog.DEBUG=false to the Closure Compiler aka - * JSCompiler. For example, most toString() methods should be declared inside an - * "if (goog.DEBUG)" conditional because they are generally used for debugging - * purposes and it is difficult for the JSCompiler to statically determine - * whether they are used. - */ -goog.DEBUG = goog.define('goog.DEBUG', true); - - -/** - * @define {string} LOCALE defines the locale being used for compilation. It is - * used to select locale specific data to be compiled in js binary. BUILD rule - * can specify this value by "--define goog.LOCALE=" as a compiler - * option. - * - * Take into account that the locale code format is important. You should use - * the canonical Unicode format with hyphen as a delimiter. Language must be - * lowercase, Language Script - Capitalized, Region - UPPERCASE. - * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. - * - * See more info about locale codes here: - * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers - * - * For language codes you should use values defined by ISO 693-1. See it here - * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from - * this rule: the Hebrew language. For legacy reasons the old code (iw) should - * be used instead of the new code (he). - * - */ -goog.LOCALE = goog.define('goog.LOCALE', 'en'); // default to en - - -/** - * Same as `goog.LOCALE`, which should be used instead. - * - * Using this method just makes it harder for closure-compiler to optimize - * your locale-specific code, since it has to take the extra step of inlining - * this function to discover and remove code that is not used for the target - * locale. - * - * @return {string} - * @deprecated use `goog.LOCALE` - */ -goog.getLocale = function() { - return goog.LOCALE; -}; - - -/** - * @define {boolean} Whether this code is running on trusted sites. - * - * On untrusted sites, several native functions can be defined or overridden by - * external libraries like Prototype, Datejs, and JQuery and setting this flag - * to false forces closure to use its own implementations when possible. - * - * If your JavaScript can be loaded by a third party site and you are wary about - * relying on non-standard implementations, specify - * "--define goog.TRUSTED_SITE=false" to the compiler. - */ -goog.TRUSTED_SITE = goog.define('goog.TRUSTED_SITE', true); - - -/** - * @define {boolean} Whether code that calls {@link goog.setTestOnly} should - * be disallowed in the compilation unit. - */ -goog.DISALLOW_TEST_ONLY_CODE = - goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); - - -/** - * @define {boolean} Whether to use a Chrome app CSP-compliant method for - * loading scripts via goog.require. @see appendScriptSrcNode_. - */ -goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING = - goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); - - -/** - * Defines a namespace in Closure. - * - * A namespace may only be defined once in a codebase. It may be defined using - * goog.provide() or goog.module(). - * - * The presence of one or more goog.provide() calls in a file indicates - * that the file defines the given objects/namespaces. - * Provided symbols must not be null or undefined. - * - * In addition, goog.provide() creates the object stubs for a namespace - * (for example, goog.provide("goog.foo.bar") will create the object - * goog.foo.bar if it does not already exist). - * - * Build tools also scan for provide/require/module statements - * to discern dependencies, build dependency files (see deps.js), etc. - * - * @see goog.require - * @see goog.module - * @param {string} name Namespace provided by this file in the form - * "goog.package.part". - * deprecated Use goog.module (see b/159289405) - */ -goog.provide = function(name) { - if (goog.isInModuleLoader_()) { - throw new Error('goog.provide cannot be used within a module.'); - } - if (!COMPILED) { - // Ensure that the same namespace isn't provided twice. - // A goog.module/goog.provide maps a goog.require to a specific file - if (goog.isProvided_(name)) { - throw new Error('Namespace "' + name + '" already declared.'); - } - } - - goog.constructNamespace_(name); -}; - - -/** - * @param {string} name Namespace provided by this file in the form - * "goog.package.part". - * @param {?Object=} object The object to embed in the namespace. - * @param {boolean=} overwriteImplicit If object is set and a previous call - * implicitly constructed the namespace given by name, this parameter - * controls whether opt_obj should overwrite the implicitly constructed - * namespace or be merged into it. Defaults to false. - * @private - */ -goog.constructNamespace_ = function(name, object, overwriteImplicit) { - if (!COMPILED) { - delete goog.implicitNamespaces_[name]; - - var namespace = name; - while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { - if (goog.getObjectByName(namespace)) { - break; - } - goog.implicitNamespaces_[namespace] = true; - } - } - - goog.exportPath_(name, object, overwriteImplicit); -}; - - -/** - * According to the CSP3 spec a nonce must be a valid base64 string. - * @see https://www.w3.org/TR/CSP3/#grammardef-base64-value - * @private @const - */ -goog.NONCE_PATTERN_ = /^[\w+/_-]+[=]{0,2}$/; - - -/** - * Returns CSP nonce, if set for any script tag. - * @param {?Window=} opt_window The window context used to retrieve the nonce. - * Defaults to global context. - * @return {string} CSP nonce or empty string if no nonce is present. - * @private - */ -goog.getScriptNonce_ = function(opt_window) { - var doc = (opt_window || goog.global).document; - var script = doc.querySelector && doc.querySelector('script[nonce]'); - if (script) { - // Try to get the nonce from the IDL property first, because browsers that - // implement additional nonce protection features (currently only Chrome) to - // prevent nonce stealing via CSS do not expose the nonce via attributes. - // See https://github.com/whatwg/html/issues/2369 - var nonce = script['nonce'] || script.getAttribute('nonce'); - if (nonce && goog.NONCE_PATTERN_.test(nonce)) { - return nonce; - } - } - return ''; -}; - - -/** - * Module identifier validation regexp. - * Note: This is a conservative check, it is very possible to be more lenient, - * the primary exclusion here is "/" and "\" and a leading ".", these - * restrictions are intended to leave the door open for using goog.require - * with relative file paths rather than module identifiers. - * @private - */ -goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; - - -/** - * Defines a module in Closure. - * - * Marks that this file must be loaded as a module and claims the namespace. - * - * A namespace may only be defined once in a codebase. It may be defined using - * goog.provide() or goog.module(). - * - * goog.module() has three requirements: - * - goog.module may not be used in the same file as goog.provide. - * - goog.module must be the first statement in the file. - * - only one goog.module is allowed per file. - * - * When a goog.module annotated file is loaded, it is enclosed in - * a strict function closure. This means that: - * - any variables declared in a goog.module file are private to the file - * (not global), though the compiler is expected to inline the module. - * - The code must obey all the rules of "strict" JavaScript. - * - the file will be marked as "use strict" - * - * NOTE: unlike goog.provide, goog.module does not declare any symbols by - * itself. If declared symbols are desired, use - * goog.module.declareLegacyNamespace(). - * - * - * See the public goog.module proposal: http://goo.gl/Va1hin - * - * @param {string} name Namespace provided by this file in the form - * "goog.package.part", is expected but not required. - * @return {void} - */ -goog.module = function(name) { - if (typeof name !== 'string' || !name || - name.search(goog.VALID_MODULE_RE_) == -1) { - throw new Error('Invalid module identifier'); - } - if (!goog.isInGoogModuleLoader_()) { - throw new Error( - 'Module ' + name + ' has been loaded incorrectly. Note, ' + - 'modules cannot be loaded as normal scripts. They require some kind of ' + - 'pre-processing step. You\'re likely trying to load a module via a ' + - 'script tag or as a part of a concatenated bundle without rewriting the ' + - 'module. For more info see: ' + - 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.'); - } - if (goog.moduleLoaderState_.moduleName) { - throw new Error('goog.module may only be called once per module.'); - } - - // Store the module name for the loader. - goog.moduleLoaderState_.moduleName = name; - if (!COMPILED) { - // Ensure that the same namespace isn't provided twice. - // A goog.module/goog.provide maps a goog.require to a specific file - if (goog.isProvided_(name)) { - throw new Error('Namespace "' + name + '" already declared.'); - } - delete goog.implicitNamespaces_[name]; - } -}; - - -/** - * @param {string} name The module identifier. - * @return {?} The module exports for an already loaded module or null. - * - * Note: This is not an alternative to goog.require, it does not - * indicate a hard dependency, instead it is used to indicate - * an optional dependency or to access the exports of a module - * that has already been loaded. - * @suppress {missingProvide} - */ -goog.module.get = function(name) { - return goog.module.getInternal_(name); -}; - - -/** - * @param {string} name The module identifier. - * @return {?} The module exports for an already loaded module or null. - * @private - */ -goog.module.getInternal_ = function(name) { - if (!COMPILED) { - if (name in goog.loadedModules_) { - return goog.loadedModules_[name].exports; - } else if (!goog.implicitNamespaces_[name]) { - var ns = goog.getObjectByName(name); - return ns != null ? ns : null; - } - } - return null; -}; - - -/** - * Types of modules the debug loader can load. - * @enum {string} - */ -goog.ModuleType = { - ES6: 'es6', - GOOG: 'goog' -}; - - -/** - * @private {?{ - * moduleName: (string|undefined), - * declareLegacyNamespace:boolean, - * type: ?goog.ModuleType - * }} - */ -goog.moduleLoaderState_ = null; - - -/** - * @private - * @return {boolean} Whether a goog.module or an es6 module is currently being - * initialized. - */ -goog.isInModuleLoader_ = function() { - return goog.isInGoogModuleLoader_() || goog.isInEs6ModuleLoader_(); -}; - - -/** - * @private - * @return {boolean} Whether a goog.module is currently being initialized. - */ -goog.isInGoogModuleLoader_ = function() { - return !!goog.moduleLoaderState_ && - goog.moduleLoaderState_.type == goog.ModuleType.GOOG; -}; - - -/** - * @private - * @return {boolean} Whether an es6 module is currently being initialized. - */ -goog.isInEs6ModuleLoader_ = function() { - var inLoader = !!goog.moduleLoaderState_ && - goog.moduleLoaderState_.type == goog.ModuleType.ES6; - - if (inLoader) { - return true; - } - - var jscomp = goog.global['$jscomp']; - - if (jscomp) { - // jscomp may not have getCurrentModulePath if this is a compiled bundle - // that has some of the runtime, but not all of it. This can happen if - // optimizations are turned on so the unused runtime is removed but renaming - // and Closure pass are off (so $jscomp is still named $jscomp and the - // goog.provide/require calls still exist). - if (typeof jscomp.getCurrentModulePath != 'function') { - return false; - } - - // Bundled ES6 module. - return !!jscomp.getCurrentModulePath(); - } - - return false; -}; - - -/** - * Provide the module's exports as a globally accessible object under the - * module's declared name. This is intended to ease migration to goog.module - * for files that have existing usages. - * @suppress {missingProvide} - */ -goog.module.declareLegacyNamespace = function() { - if (!COMPILED && !goog.isInGoogModuleLoader_()) { - throw new Error( - 'goog.module.declareLegacyNamespace must be called from ' + - 'within a goog.module'); - } - if (!COMPILED && !goog.moduleLoaderState_.moduleName) { - throw new Error( - 'goog.module must be called prior to ' + - 'goog.module.declareLegacyNamespace.'); - } - goog.moduleLoaderState_.declareLegacyNamespace = true; -}; - - -/** - * Associates an ES6 module with a Closure module ID so that is available via - * goog.require. The associated ID acts like a goog.module ID - it does not - * create any global names, it is merely available via goog.require / - * goog.module.get / goog.forwardDeclare / goog.requireType. goog.require and - * goog.module.get will return the entire module as if it was import *'d. This - * allows Closure files to reference ES6 modules for the sake of migration. - * - * @param {string} namespace - * @suppress {missingProvide} - */ -goog.declareModuleId = function(namespace) { - if (!COMPILED) { - if (!goog.isInEs6ModuleLoader_()) { - throw new Error( - 'goog.declareModuleId may only be called from ' + - 'within an ES6 module'); - } - if (goog.moduleLoaderState_ && goog.moduleLoaderState_.moduleName) { - throw new Error( - 'goog.declareModuleId may only be called once per module.' + - 'This error can also be caused by circular imports, which ' + - 'are not supported by debug module loader.'); - } - if (namespace in goog.loadedModules_) { - throw new Error( - 'Module with namespace "' + namespace + '" already exists.'); - } - } - if (goog.moduleLoaderState_) { - // Not bundled - debug loading. - goog.moduleLoaderState_.moduleName = namespace; - } else { - // Bundled - not debug loading, no module loader state. - var jscomp = goog.global['$jscomp']; - if (!jscomp || typeof jscomp.getCurrentModulePath != 'function') { - throw new Error( - 'Module with namespace "' + namespace + - '" has been loaded incorrectly.'); - } - var exports = jscomp.require(jscomp.getCurrentModulePath()); - goog.loadedModules_[namespace] = { - exports: exports, - type: goog.ModuleType.ES6, - moduleId: namespace - }; - } -}; - - -/** - * Marks that the current file should only be used for testing, and never for - * live code in production. - * - * In the case of unit tests, the message may optionally be an exact namespace - * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra - * provide (if not explicitly defined in the code). - * - * @param {string=} opt_message Optional message to add to the error that's - * raised when used in production code. - */ -goog.setTestOnly = function(opt_message) { - if (goog.DISALLOW_TEST_ONLY_CODE) { - opt_message = opt_message || ''; - throw new Error( - 'Importing test-only code into non-debug environment' + - (opt_message ? ': ' + opt_message : '.')); - } -}; - - -/** - * Forward declares a symbol. This is an indication to the compiler that the - * symbol may be used in the source yet is not required and may not be provided - * in compilation. - * - * The most common usage of forward declaration is code that takes a type as a - * function parameter but does not need to require it. By forward declaring - * instead of requiring, no hard dependency is made, and (if not required - * elsewhere) the namespace may never be required and thus, not be pulled - * into the JavaScript binary. If it is required elsewhere, it will be type - * checked as normal. - * - * Before using goog.forwardDeclare, please read the documentation at - * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to - * understand the options and tradeoffs when working with forward declarations. - * - * @param {string} name The namespace to forward declare in the form of - * "goog.package.part". - * @deprecated See go/noforwarddeclaration, Use `goog.requireType` instead. - */ -goog.forwardDeclare = function(name) {}; - - -/** - * Forward declare type information. Used to assign types to goog.global - * referenced object that would otherwise result in unknown type references - * and thus block property disambiguation. - */ -goog.forwardDeclare('Document'); -goog.forwardDeclare('HTMLScriptElement'); -goog.forwardDeclare('XMLHttpRequest'); - - -if (!COMPILED) { - /** - * Check if the given name has been goog.provided. This will return false for - * names that are available only as implicit namespaces. - * @param {string} name name of the object to look for. - * @return {boolean} Whether the name has been provided. - * @private - */ - goog.isProvided_ = function(name) { - return (name in goog.loadedModules_) || - (!goog.implicitNamespaces_[name] && goog.getObjectByName(name) != null); - }; - - /** - * Namespaces implicitly defined by goog.provide. For example, - * goog.provide('goog.events.Event') implicitly declares that 'goog' and - * 'goog.events' must be namespaces. - * - * @type {!Object} - * @private - */ - goog.implicitNamespaces_ = {'goog.module': true}; - - // NOTE: We add goog.module as an implicit namespace as goog.module is defined - // here and because the existing module package has not been moved yet out of - // the goog.module namespace. This satisfies both the debug loader and - // ahead-of-time dependency management. -} - - -/** - * Returns an object based on its fully qualified external name. The object - * is not found if null or undefined. If you are using a compilation pass that - * renames property names beware that using this function will not find renamed - * properties. - * - * @param {string} name The fully qualified name. - * @param {Object=} opt_obj The object within which to look; default is - * |goog.global|. - * @return {?} The value (object or primitive) or, if not found, null. - */ -goog.getObjectByName = function(name, opt_obj) { - var parts = name.split('.'); - var cur = opt_obj || goog.global; - for (var i = 0; i < parts.length; i++) { - cur = cur[parts[i]]; - if (cur == null) { - return null; - } - } - return cur; -}; - - -/** - * Adds a dependency from a file to the files it requires. - * @param {string} relPath The path to the js file. - * @param {!Array} provides An array of strings with - * the names of the objects this file provides. - * @param {!Array} requires An array of strings with - * the names of the objects this file requires. - * @param {boolean|!Object=} opt_loadFlags Parameters indicating - * how the file must be loaded. The boolean 'true' is equivalent - * to {'module': 'goog'} for backwards-compatibility. Valid properties - * and values include {'module': 'goog'} and {'lang': 'es6'}. - */ -goog.addDependency = function(relPath, provides, requires, opt_loadFlags) { - if (!COMPILED && goog.DEPENDENCIES_ENABLED) { - goog.debugLoader_.addDependency(relPath, provides, requires, opt_loadFlags); - } -}; - - -// NOTE(nnaze): The debug DOM loader was included in base.js as an original way -// to do "debug-mode" development. The dependency system can sometimes be -// confusing, as can the debug DOM loader's asynchronous nature. -// -// With the DOM loader, a call to goog.require() is not blocking -- the script -// will not load until some point after the current script. If a namespace is -// needed at runtime, it needs to be defined in a previous script, or loaded via -// require() with its registered dependencies. -// -// User-defined namespaces may need their own deps file. For a reference on -// creating a deps file, see: -// Externally: https://developers.google.com/closure/library/docs/depswriter -// -// Because of legacy clients, the DOM loader can't be easily removed from -// base.js. Work was done to make it disableable or replaceable for -// different environments (DOM-less JavaScript interpreters like Rhino or V8, -// for example). See bootstrap/ for more information. - - -/** - * @define {boolean} Whether to enable the debug loader. - * - * If enabled, a call to goog.require() will attempt to load the namespace by - * appending a script tag to the DOM (if the namespace has been registered). - * - * If disabled, goog.require() will simply assert that the namespace has been - * provided (and depend on the fact that some outside tool correctly ordered - * the script). - */ -goog.ENABLE_DEBUG_LOADER = goog.define('goog.ENABLE_DEBUG_LOADER', true); - - -/** - * @param {string} msg - * @private - */ -goog.logToConsole_ = function(msg) { - if (goog.global.console) { - goog.global.console['error'](msg); - } -}; - - -/** - * Implements a system for the dynamic resolution of dependencies that works in - * parallel with the BUILD system. - * - * Note that all calls to goog.require will be stripped by the compiler. - * - * @see goog.provide - * @param {string} namespace Namespace (as was given in goog.provide, - * goog.module, or goog.declareModuleId) in the form - * "goog.package.part". - * @return {?} If called within a goog.module or ES6 module file, the associated - * namespace or module otherwise null. - */ -goog.require = function(namespace) { - if (!COMPILED) { - // Might need to lazy load on old IE. - if (goog.ENABLE_DEBUG_LOADER) { - goog.debugLoader_.requested(namespace); - } - - // If the object already exists we do not need to do anything. - if (goog.isProvided_(namespace)) { - if (goog.isInModuleLoader_()) { - return goog.module.getInternal_(namespace); - } - } else if (goog.ENABLE_DEBUG_LOADER) { - var moduleLoaderState = goog.moduleLoaderState_; - goog.moduleLoaderState_ = null; - try { - goog.debugLoader_.load_(namespace); - } finally { - goog.moduleLoaderState_ = moduleLoaderState; - } - } - - return null; - } -}; - - -/** - * Requires a symbol for its type information. This is an indication to the - * compiler that the symbol may appear in type annotations, yet it is not - * referenced at runtime. - * - * When called within a goog.module or ES6 module file, the return value may be - * assigned to or destructured into a variable, but it may not be otherwise used - * in code outside of a type annotation. - * - * Note that all calls to goog.requireType will be stripped by the compiler. - * - * @param {string} namespace Namespace (as was given in goog.provide, - * goog.module, or goog.declareModuleId) in the form - * "goog.package.part". - * @return {?} - */ -goog.requireType = function(namespace) { - // Return an empty object so that single-level destructuring of the return - // value doesn't crash at runtime when using the debug loader. Multi-level - // destructuring isn't supported. - return {}; -}; - - -/** - * Path for included scripts. - * @type {string} - */ -goog.basePath = ''; - - -/** - * A hook for overriding the base path. - * @type {string|undefined} - */ -goog.global.CLOSURE_BASE_PATH; - - -/** - * Whether to attempt to load Closure's deps file. By default, when uncompiled, - * deps files will attempt to be loaded. - * @type {boolean|undefined} - */ -goog.global.CLOSURE_NO_DEPS; - - -/** - * A function to import a single script. This is meant to be overridden when - * Closure is being run in non-HTML contexts, such as web workers. It's defined - * in the global scope so that it can be set before base.js is loaded, which - * allows deps.js to be imported properly. - * - * The first parameter the script source, which is a relative URI. The second, - * optional parameter is the script contents, in the event the script needed - * transformation. It should return true if the script was imported, false - * otherwise. - * @type {(function(string, string=): boolean)|undefined} - */ -goog.global.CLOSURE_IMPORT_SCRIPT; - - -/** - * When defining a class Foo with an abstract method bar(), you can do: - * Foo.prototype.bar = goog.abstractMethod - * - * Now if a subclass of Foo fails to override bar(), an error will be thrown - * when bar() is invoked. - * - * @type {!Function} - * @throws {Error} when invoked to indicate the method should be overridden. - * @deprecated Use "@abstract" annotation instead of goog.abstractMethod in new - * code. See - * https://github.com/google/closure-compiler/wiki/@abstract-classes-and-methods - */ -goog.abstractMethod = function() { - throw new Error('unimplemented abstract method'); -}; - - -/** - * Adds a `getInstance` static method that always returns the same - * instance object. - * @param {!Function} ctor The constructor for the class to add the static - * method to. - * @suppress {missingProperties} 'instance_' isn't a property on 'Function' - * but we don't have a better type to use here. - */ -goog.addSingletonGetter = function(ctor) { - // instance_ is immediately set to prevent issues with sealed constructors - // such as are encountered when a constructor is returned as the export object - // of a goog.module in unoptimized code. - // Delcare type to avoid conformance violations that ctor.instance_ is unknown - /** @type {undefined|!Object} @suppress {underscore} */ - ctor.instance_ = undefined; - ctor.getInstance = function() { - if (ctor.instance_) { - return ctor.instance_; - } - if (goog.DEBUG) { - // NOTE: JSCompiler can't optimize away Array#push. - goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; - } - // Cast to avoid conformance violations that ctor.instance_ is unknown - return /** @type {!Object|undefined} */ (ctor.instance_) = new ctor; - }; -}; - - -/** - * All singleton classes that have been instantiated, for testing. Don't read - * it directly, use the `goog.testing.singleton` module. The compiler - * removes this variable if unused. - * @type {!Array} - * @private - */ -goog.instantiatedSingletons_ = []; - - -/** - * @define {boolean} Whether to load goog.modules using `eval` when using - * the debug loader. This provides a better debugging experience as the - * source is unmodified and can be edited using Chrome Workspaces or similar. - * However in some environments the use of `eval` is banned - * so we provide an alternative. - */ -goog.LOAD_MODULE_USING_EVAL = goog.define('goog.LOAD_MODULE_USING_EVAL', true); - - -/** - * @define {boolean} Whether the exports of goog.modules should be sealed when - * possible. - */ -goog.SEAL_MODULE_EXPORTS = goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); - - -/** - * The registry of initialized modules: - * The module identifier or path to module exports map. - * @private @const {!Object} - */ -goog.loadedModules_ = {}; - - -/** - * True if the debug loader enabled and used. - * @const {boolean} - */ -goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; - - -/** - * @define {string} How to decide whether to transpile. Valid values - * are 'always', 'never', and 'detect'. The default ('detect') is to - * use feature detection to determine which language levels need - * transpilation. - */ -// NOTE(sdh): we could expand this to accept a language level to bypass -// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but -// would leave ES3 and ES5 files alone. -goog.TRANSPILE = goog.define('goog.TRANSPILE', 'detect'); - -/** - * @define {boolean} If true assume that ES modules have already been - * transpiled by the jscompiler (in the same way that transpile.js would - * transpile them - to jscomp modules). Useful only for servers that wish to use - * the debug loader and transpile server side. Thus this is only respected if - * goog.TRANSPILE is "never". - */ -goog.ASSUME_ES_MODULES_TRANSPILED = - goog.define('goog.ASSUME_ES_MODULES_TRANSPILED', false); - - -/** - * @define {string} If a file needs to be transpiled what the output language - * should be. By default this is the highest language level this file detects - * the current environment supports. Generally this flag should not be set, but - * it could be useful to override. Example: If the current environment supports - * ES6 then by default ES7+ files will be transpiled to ES6, unless this is - * overridden. - * - * Valid values include: es3, es5, es6, es7, and es8. Anything not recognized - * is treated as es3. - * - * Note that setting this value does not force transpilation. Just if - * transpilation occurs this will be the output. So this is most useful when - * goog.TRANSPILE is set to 'always' and then forcing the language level to be - * something lower than what the environment detects. - */ -goog.TRANSPILE_TO_LANGUAGE = goog.define('goog.TRANSPILE_TO_LANGUAGE', ''); - - -/** - * @define {string} Path to the transpiler. Executing the script at this - * path (relative to base.js) should define a function $jscomp.transpile. - */ -goog.TRANSPILER = goog.define('goog.TRANSPILER', 'transpile.js'); - - -/** - * @define {string} Trusted Types policy name. If non-empty then Closure will - * use Trusted Types. - */ -goog.TRUSTED_TYPES_POLICY_NAME = - goog.define('goog.TRUSTED_TYPES_POLICY_NAME', 'goog'); - - -/** - * @package {?boolean} - * Visible for testing. - */ -goog.hasBadLetScoping = null; - - -/** - * @param {function(?):?|string} moduleDef The module definition. - */ -goog.loadModule = function(moduleDef) { - // NOTE: we allow function definitions to be either in the from - // of a string to eval (which keeps the original source intact) or - // in a eval forbidden environment (CSP) we allow a function definition - // which in its body must call `goog.module`, and return the exports - // of the module. - var previousState = goog.moduleLoaderState_; - try { - goog.moduleLoaderState_ = { - moduleName: '', - declareLegacyNamespace: false, - type: goog.ModuleType.GOOG - }; - var origExports = {}; - var exports = origExports; - if (typeof moduleDef === 'function') { - exports = moduleDef.call(undefined, exports); - } else if (typeof moduleDef === 'string') { - exports = goog.loadModuleFromSource_.call(undefined, exports, moduleDef); - } else { - throw new Error('Invalid module definition'); - } - - var moduleName = goog.moduleLoaderState_.moduleName; - if (typeof moduleName === 'string' && moduleName) { - // Don't seal legacy namespaces as they may be used as a parent of - // another namespace - if (goog.moduleLoaderState_.declareLegacyNamespace) { - // Whether exports was overwritten via default export assignment. - // This is important for legacy namespaces as it dictates whether - // previously a previously loaded implicit namespace should be clobbered - // or not. - var isDefaultExport = origExports !== exports; - goog.constructNamespace_(moduleName, exports, isDefaultExport); - } else if ( - goog.SEAL_MODULE_EXPORTS && Object.seal && - typeof exports == 'object' && exports != null) { - Object.seal(exports); - } - - var data = { - exports: exports, - type: goog.ModuleType.GOOG, - moduleId: goog.moduleLoaderState_.moduleName - }; - goog.loadedModules_[moduleName] = data; - } else { - throw new Error('Invalid module name \"' + moduleName + '\"'); - } - } finally { - goog.moduleLoaderState_ = previousState; - } -}; - - -/** - * @private @const - */ -goog.loadModuleFromSource_ = - /** @type {function(!Object, string):?} */ (function(exports) { - // NOTE: we avoid declaring parameters or local variables here to avoid - // masking globals or leaking values into the module definition. - 'use strict'; - eval(goog.CLOSURE_EVAL_PREFILTER_.createScript(arguments[1])); - return exports; - }); - - -/** - * Normalize a file path by removing redundant ".." and extraneous "." file - * path components. - * @param {string} path - * @return {string} - * @private - */ -goog.normalizePath_ = function(path) { - var components = path.split('/'); - var i = 0; - while (i < components.length) { - if (components[i] == '.') { - components.splice(i, 1); - } else if ( - i && components[i] == '..' && components[i - 1] && - components[i - 1] != '..') { - components.splice(--i, 2); - } else { - i++; - } - } - return components.join('/'); -}; - - -/** - * Provides a hook for loading a file when using Closure's goog.require() API - * with goog.modules. In particular this hook is provided to support Node.js. - * - * @type {(function(string):string)|undefined} - */ -goog.global.CLOSURE_LOAD_FILE_SYNC; - - -/** - * Loads file by synchronous XHR. Should not be used in production environments. - * @param {string} src Source URL. - * @return {?string} File contents, or null if load failed. - * @private - */ -goog.loadFileSync_ = function(src) { - if (goog.global.CLOSURE_LOAD_FILE_SYNC) { - return goog.global.CLOSURE_LOAD_FILE_SYNC(src); - } else { - try { - /** @type {XMLHttpRequest} */ - var xhr = new goog.global['XMLHttpRequest'](); - xhr.open('get', src, false); - xhr.send(); - // NOTE: Successful http: requests have a status of 200, but successful - // file: requests may have a status of zero. Any other status, or a - // thrown exception (particularly in case of file: requests) indicates - // some sort of error, which we treat as a missing or unavailable file. - return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null; - } catch (err) { - // No need to rethrow or log, since errors should show up on their own. - return null; - } - } -}; - - -/** - * Lazily retrieves the transpiler and applies it to the source. - * @param {string} code JS code. - * @param {string} path Path to the code. - * @param {string} target Language level output. - * @return {string} The transpiled code. - * @private - */ -goog.transpile_ = function(code, path, target) { - var jscomp = goog.global['$jscomp']; - if (!jscomp) { - goog.global['$jscomp'] = jscomp = {}; - } - var transpile = jscomp.transpile; - if (!transpile) { - var transpilerPath = goog.basePath + goog.TRANSPILER; - var transpilerCode = goog.loadFileSync_(transpilerPath); - if (transpilerCode) { - // This must be executed synchronously, since by the time we know we - // need it, we're about to load and write the ES6 code synchronously, - // so a normal script-tag load will be too slow. Wrapped in a function - // so that code is eval'd in the global scope. - (function() { - (0, eval)(transpilerCode + '\n//# sourceURL=' + transpilerPath); - }).call(goog.global); - // Even though the transpiler is optional, if $gwtExport is found, it's - // a sign the transpiler was loaded and the $jscomp.transpile *should* - // be there. - if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] && - !goog.global['$gwtExport']['$jscomp']['transpile']) { - throw new Error( - 'The transpiler did not properly export the "transpile" ' + - 'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport'])); - } - // transpile.js only exports a single $jscomp function, transpile. We - // grab just that and add it to the existing definition of $jscomp which - // contains the polyfills. - goog.global['$jscomp'].transpile = - goog.global['$gwtExport']['$jscomp']['transpile']; - jscomp = goog.global['$jscomp']; - transpile = jscomp.transpile; - } - } - if (!transpile) { - // The transpiler is an optional component. If it's not available then - // replace it with a pass-through function that simply logs. - var suffix = ' requires transpilation but no transpiler was found.'; - transpile = jscomp.transpile = function(code, path) { - // TODO(sdh): figure out some way to get this error to show up - // in test results, noting that the failure may occur in many - // different ways, including in loadModule() before the test - // runner even comes up. - goog.logToConsole_(path + suffix); - return code; - }; - } - // Note: any transpilation errors/warnings will be logged to the console. - return transpile(code, path, target); -}; - -//============================================================================== -// Language Enhancements -//============================================================================== - - -/** - * This is a "fixed" version of the typeof operator. It differs from the typeof - * operator in such a way that null returns 'null' and arrays return 'array'. - * @param {?} value The value to get the type of. - * @return {string} The name of the type. - */ -goog.typeOf = function(value) { - var s = typeof value; - - if (s != 'object') { - return s; - } - - if (!value) { - return 'null'; - } - - if (Array.isArray(value)) { - return 'array'; - } - return s; -}; - - -/** - * Returns true if the object looks like an array. To qualify as array like - * the value needs to be either a NodeList or an object with a Number length - * property. Note that for this function neither strings nor functions are - * considered "array-like". - * - * @param {?} val Variable to test. - * @return {boolean} Whether variable is an array. - */ -goog.isArrayLike = function(val) { - var type = goog.typeOf(val); - // We do not use goog.isObject here in order to exclude function values. - return type == 'array' || type == 'object' && typeof val.length == 'number'; -}; - - -/** - * Returns true if the object looks like a Date. To qualify as Date-like the - * value needs to be an object and have a getFullYear() function. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is a like a Date. - */ -goog.isDateLike = function(val) { - return goog.isObject(val) && typeof val.getFullYear == 'function'; -}; - - -/** - * Returns true if the specified value is an object. This includes arrays and - * functions. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is an object. - */ -goog.isObject = function(val) { - var type = typeof val; - return type == 'object' && val != null || type == 'function'; - // return Object(val) === val also works, but is slower, especially if val is - // not an object. -}; - - -/** - * Gets a unique ID for an object. This mutates the object so that further calls - * with the same object as a parameter returns the same value. The unique ID is - * guaranteed to be unique across the current session amongst objects that are - * passed into `getUid`. There is no guarantee that the ID is unique or - * consistent across sessions. It is unsafe to generate unique ID for function - * prototypes. - * - * @param {Object} obj The object to get the unique ID for. - * @return {number} The unique ID for the object. - */ -goog.getUid = function(obj) { - // TODO(arv): Make the type stricter, do not accept null. - return Object.prototype.hasOwnProperty.call(obj, goog.UID_PROPERTY_) && - obj[goog.UID_PROPERTY_] || - (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_); -}; - - -/** - * Whether the given object is already assigned a unique ID. - * - * This does not modify the object. - * - * @param {!Object} obj The object to check. - * @return {boolean} Whether there is an assigned unique id for the object. - */ -goog.hasUid = function(obj) { - return !!obj[goog.UID_PROPERTY_]; -}; - - -/** - * Removes the unique ID from an object. This is useful if the object was - * previously mutated using `goog.getUid` in which case the mutation is - * undone. - * @param {Object} obj The object to remove the unique ID field from. - */ -goog.removeUid = function(obj) { - // TODO(arv): Make the type stricter, do not accept null. - - // In IE, DOM nodes are not instances of Object and throw an exception if we - // try to delete. Instead we try to use removeAttribute. - if (obj !== null && 'removeAttribute' in obj) { - obj.removeAttribute(goog.UID_PROPERTY_); - } - - try { - delete obj[goog.UID_PROPERTY_]; - } catch (ex) { - } -}; - - -/** - * Name for unique ID property. Initialized in a way to help avoid collisions - * with other closure JavaScript on the same page. - * @type {string} - * @private - */ -goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0); - - -/** - * Counter for UID. - * @type {number} - * @private - */ -goog.uidCounter_ = 0; - - -/** - * Clones a value. The input may be an Object, Array, or basic type. Objects and - * arrays will be cloned recursively. - * - * WARNINGS: - * goog.cloneObject does not detect reference loops. Objects that - * refer to themselves will cause infinite recursion. - * - * goog.cloneObject is unaware of unique identifiers, and copies - * UIDs created by getUid into cloned results. - * - * @param {*} obj The value to clone. - * @return {*} A clone of the input value. - * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods. - */ -goog.cloneObject = function(obj) { - var type = goog.typeOf(obj); - if (type == 'object' || type == 'array') { - if (typeof obj.clone === 'function') { - return obj.clone(); - } - if (typeof Map !== 'undefined' && obj instanceof Map) { - return new Map(obj); - } else if (typeof Set !== 'undefined' && obj instanceof Set) { - return new Set(obj); - } - var clone = type == 'array' ? [] : {}; - for (var key in obj) { - clone[key] = goog.cloneObject(obj[key]); - } - return clone; - } - - return obj; -}; - - -/** - * A native implementation of goog.bind. - * @param {?function(this:T, ...)} fn A function to partially apply. - * @param {T} selfObj Specifies the object which this should point to when the - * function is run. - * @param {...*} var_args Additional arguments that are partially applied to the - * function. - * @return {!Function} A partially-applied form of the function goog.bind() was - * invoked as a method of. - * @template T - * @private - */ -goog.bindNative_ = function(fn, selfObj, var_args) { - return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); -}; - - -/** - * A pure-JS implementation of goog.bind. - * @param {?function(this:T, ...)} fn A function to partially apply. - * @param {T} selfObj Specifies the object which this should point to when the - * function is run. - * @param {...*} var_args Additional arguments that are partially applied to the - * function. - * @return {!Function} A partially-applied form of the function goog.bind() was - * invoked as a method of. - * @template T - * @private - */ -goog.bindJs_ = function(fn, selfObj, var_args) { - if (!fn) { - throw new Error(); - } - - if (arguments.length > 2) { - var boundArgs = Array.prototype.slice.call(arguments, 2); - return function() { - // Prepend the bound arguments to the current arguments. - var newArgs = Array.prototype.slice.call(arguments); - Array.prototype.unshift.apply(newArgs, boundArgs); - return fn.apply(selfObj, newArgs); - }; - - } else { - return function() { - return fn.apply(selfObj, arguments); - }; - } -}; - - -/** - * Partially applies this function to a particular 'this object' and zero or - * more arguments. The result is a new function with some arguments of the first - * function pre-filled and the value of this 'pre-specified'. - * - * Remaining arguments specified at call-time are appended to the pre-specified - * ones. - * - * Also see: {@link #partial}. - * - * Usage: - *
var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');
- * barMethBound('arg3', 'arg4');
- * - * @param {?function(this:T, ...)} fn A function to partially apply. - * @param {T} selfObj Specifies the object which this should point to when the - * function is run. - * @param {...*} var_args Additional arguments that are partially applied to the - * function. - * @return {!Function} A partially-applied form of the function goog.bind() was - * invoked as a method of. - * @template T - * @suppress {deprecated} See above. - * @deprecated use `=> {}` or Function.prototype.bind instead. - */ -goog.bind = function(fn, selfObj, var_args) { - // TODO(nicksantos): narrow the type signature. - if (Function.prototype.bind && - // NOTE(nicksantos): Somebody pulled base.js into the default Chrome - // extension environment. This means that for Chrome extensions, they get - // the implementation of Function.prototype.bind that calls goog.bind - // instead of the native one. Even worse, we don't want to introduce a - // circular dependency between goog.bind and Function.prototype.bind, so - // we have to hack this to make sure it works correctly. - Function.prototype.bind.toString().indexOf('native code') != -1) { - goog.bind = goog.bindNative_; - } else { - goog.bind = goog.bindJs_; - } - return goog.bind.apply(null, arguments); -}; - - -/** - * Like goog.bind(), except that a 'this object' is not required. Useful when - * the target function is already bound. - * - * Usage: - * var g = goog.partial(f, arg1, arg2); - * g(arg3, arg4); - * - * @param {Function} fn A function to partially apply. - * @param {...*} var_args Additional arguments that are partially applied to fn. - * @return {!Function} A partially-applied form of the function goog.partial() - * was invoked as a method of. - */ -goog.partial = function(fn, var_args) { - var args = Array.prototype.slice.call(arguments, 1); - return function() { - // Clone the array (with slice()) and append additional arguments - // to the existing arguments. - var newArgs = args.slice(); - newArgs.push.apply(newArgs, arguments); - return fn.apply(/** @type {?} */ (this), newArgs); - }; -}; - - -/** - * @return {number} An integer value representing the number of milliseconds - * between midnight, January 1, 1970 and the current time. - * @deprecated Use Date.now - */ -goog.now = function() { - return Date.now(); -}; - - -/** - * Evals JavaScript in the global scope. - * - * Throws an exception if neither execScript or eval is defined. - * @param {string|!TrustedScript} script JavaScript string. - */ -goog.globalEval = function(script) { - (0, eval)(script); -}; - - -/** - * Optional map of CSS class names to obfuscated names used with - * goog.getCssName(). - * @private {!Object|undefined} - * @see goog.setCssNameMapping - */ -goog.cssNameMapping_; - - -/** - * Optional obfuscation style for CSS class names. Should be set to either - * 'BY_WHOLE' or 'BY_PART' if defined. - * @type {string|undefined} - * @private - * @see goog.setCssNameMapping - */ -goog.cssNameMappingStyle_; - - - -/** - * A hook for modifying the default behavior goog.getCssName. The function - * if present, will receive the standard output of the goog.getCssName as - * its input. - * - * @type {(function(string):string)|undefined} - */ -goog.global.CLOSURE_CSS_NAME_MAP_FN; - - -/** - * Handles strings that are intended to be used as CSS class names. - * - * This function works in tandem with @see goog.setCssNameMapping. - * - * Without any mapping set, the arguments are simple joined with a hyphen and - * passed through unaltered. - * - * When there is a mapping, there are two possible styles in which these - * mappings are used. In the BY_PART style, each part (i.e. in between hyphens) - * of the passed in css name is rewritten according to the map. In the BY_WHOLE - * style, the full css name is looked up in the map directly. If a rewrite is - * not specified by the map, the compiler will output a warning. - * - * When the mapping is passed to the compiler, it will replace calls to - * goog.getCssName with the strings from the mapping, e.g. - * var x = goog.getCssName('foo'); - * var y = goog.getCssName(this.baseClass, 'active'); - * becomes: - * var x = 'foo'; - * var y = this.baseClass + '-active'; - * - * If one argument is passed it will be processed, if two are passed only the - * modifier will be processed, as it is assumed the first argument was generated - * as a result of calling goog.getCssName. - * - * @param {string} className The class name. - * @param {string=} opt_modifier A modifier to be appended to the class name. - * @return {string} The class name or the concatenation of the class name and - * the modifier. - */ -goog.getCssName = function(className, opt_modifier) { - // String() is used for compatibility with compiled soy where the passed - // className can be non-string objects. - if (String(className).charAt(0) == '.') { - throw new Error( - 'className passed in goog.getCssName must not start with ".".' + - ' You passed: ' + className); - } - - var getMapping = function(cssName) { - return goog.cssNameMapping_[cssName] || cssName; - }; - - var renameByParts = function(cssName) { - // Remap all the parts individually. - var parts = cssName.split('-'); - var mapped = []; - for (var i = 0; i < parts.length; i++) { - mapped.push(getMapping(parts[i])); - } - return mapped.join('-'); - }; - - var rename; - if (goog.cssNameMapping_) { - rename = - goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts; - } else { - rename = function(a) { - return a; - }; - } - - var result = - opt_modifier ? className + '-' + rename(opt_modifier) : rename(className); - - // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further - // processing of the class name. - if (goog.global.CLOSURE_CSS_NAME_MAP_FN) { - return goog.global.CLOSURE_CSS_NAME_MAP_FN(result); - } - - return result; -}; - - -/** - * Sets the map to check when returning a value from goog.getCssName(). Example: - *
- * goog.setCssNameMapping({
- *   "goog": "a",
- *   "disabled": "b",
- * });
- *
- * var x = goog.getCssName('goog');
- * // The following evaluates to: "a a-b".
- * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
- * 
- * When declared as a map of string literals to string literals, the JSCompiler - * will replace all calls to goog.getCssName() using the supplied map if the - * --process_closure_primitives flag is set. - * - * @param {!Object} mapping A map of strings to strings where keys are possible - * arguments to goog.getCssName() and values are the corresponding values - * that should be returned. - * @param {string=} opt_style The style of css name mapping. There are two valid - * options: 'BY_PART', and 'BY_WHOLE'. - * @see goog.getCssName for a description. - */ -goog.setCssNameMapping = function(mapping, opt_style) { - goog.cssNameMapping_ = mapping; - goog.cssNameMappingStyle_ = opt_style; -}; - - -/** - * To use CSS renaming in compiled mode, one of the input files should have a - * call to goog.setCssNameMapping() with an object literal that the JSCompiler - * can extract and use to replace all calls to goog.getCssName(). In uncompiled - * mode, JavaScript code should be loaded before this base.js file that declares - * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is - * to ensure that the mapping is loaded before any calls to goog.getCssName() - * are made in uncompiled mode. - * - * A hook for overriding the CSS name mapping. - * @type {!Object|undefined} - */ -goog.global.CLOSURE_CSS_NAME_MAPPING; - - -if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) { - // This does not call goog.setCssNameMapping() because the JSCompiler - // requires that goog.setCssNameMapping() be called with an object literal. - goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING; -} - -/** - * Options bag type for `goog.getMsg()` third argument. - * - * It is important to note that these options need to be known at compile time, - * so they must always be provided to `goog.getMsg()` as an actual object - * literal in the function call. Otherwise, closure-compiler will report an - * error. - * @record - */ -goog.GetMsgOptions = function() {}; - -/** - * If `true`, escape '<' in the message string to '<'. - * - * Used by Closure Templates where the generated code size and performance is - * critical which is why {@link goog.html.SafeHtmlFormatter} is not used. - * The value must be literal `true` or `false`. - * @type {boolean|undefined} - */ -goog.GetMsgOptions.prototype.html; - -/** - * If `true`, unescape common html entities: >, <, ', " and - * &. - * - * Used for messages not in HTML context, such as with the `textContent` - * property. - * The value must be literal `true` or `false`. - * @type {boolean|undefined} - */ -goog.GetMsgOptions.prototype.unescapeHtmlEntities; - -/** - * Associates placeholder names with strings showing how their values are - * obtained. - * - * This field is intended for use in automatically generated JS code. - * Human-written code should use meaningful placeholder names instead. - * - * closure-compiler uses this as the contents of the `` tag in the - * XMB file it generates or defaults to `-` for historical reasons. - * - * Must be an object literal. - * Ignored at runtime. - * Keys are placeholder names. - * Values are string literals indicating how the value is obtained. - * Typically this is a snippet of source code. - * @type {!Object|undefined} - */ -goog.GetMsgOptions.prototype.original_code; - -/** - * Associates placeholder names with example values. - * - * closure-compiler uses this as the contents of the `` tag in the - * XMB file it generates or defaults to `-` for historical reasons. - * - * Must be an object literal. - * Ignored at runtime. - * Keys are placeholder names. - * Values are string literals containing example placeholder values. - * (e.g. "George McFly" for a name placeholder) - * @type {!Object|undefined} - */ -goog.GetMsgOptions.prototype.example; - -/** - * Gets a localized message. - * - * This function is a compiler primitive. If you give the compiler a localized - * message bundle, it will replace the string at compile-time with a localized - * version, and expand goog.getMsg call to a concatenated string. - * - * Messages must be initialized in the form: - * - * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'}); - * - * - * This function produces a string which should be treated as plain text. Use - * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to - * produce SafeHtml. - * - * @param {string} str Translatable string, places holders in the form {$foo}. - * @param {!Object=} opt_values Maps place holder name to value. - * @param {!goog.GetMsgOptions=} opt_options see `goog.GetMsgOptions` - * @return {string} message with placeholders filled. - */ -goog.getMsg = function(str, opt_values, opt_options) { - if (opt_options && opt_options.html) { - // Note that '&' is not replaced because the translation can contain HTML - // entities. - str = str.replace(/') - .replace(/'/g, '\'') - .replace(/"/g, '"') - .replace(/&/g, '&'); - } - if (opt_values) { - str = str.replace(/\{\$([^}]+)}/g, function(match, key) { - return (opt_values != null && key in opt_values) ? opt_values[key] : - match; - }); - } - return str; -}; - - -/** - * Gets a localized message. If the message does not have a translation, gives a - * fallback message. - * - * This is useful when introducing a new message that has not yet been - * translated into all languages. - * - * This function is a compiler primitive. Must be used in the form: - * var x = goog.getMsgWithFallback(MSG_A, MSG_B); - * where MSG_A and MSG_B were initialized with goog.getMsg. - * - * @param {string} a The preferred message. - * @param {string} b The fallback message. - * @return {string} The best translated message. - */ -goog.getMsgWithFallback = function(a, b) { - return a; -}; - - -/** - * Exposes an unobfuscated global namespace path for the given object. - * Note that fields of the exported object *will* be obfuscated, unless they are - * exported in turn via this function or goog.exportProperty. - * - * Also handy for making public items that are defined in anonymous closures. - * - * ex. goog.exportSymbol('public.path.Foo', Foo); - * - * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction); - * public.path.Foo.staticFunction(); - * - * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod', - * Foo.prototype.myMethod); - * new public.path.Foo().myMethod(); - * - * @param {string} publicPath Unobfuscated name to export. - * @param {*} object Object the name should point to. - * @param {?Object=} objectToExportTo The object to add the path to; default - * is goog.global. - */ -goog.exportSymbol = function(publicPath, object, objectToExportTo) { - goog.exportPath_( - publicPath, object, /* overwriteImplicit= */ true, objectToExportTo); -}; - - -/** - * Exports a property unobfuscated into the object's namespace. - * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction); - * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod); - * @param {Object} object Object whose static property is being exported. - * @param {string} publicName Unobfuscated name to export. - * @param {*} symbol Object the name should point to. - */ -goog.exportProperty = function(object, publicName, symbol) { - object[publicName] = symbol; -}; - - -/** - * Inherit the prototype methods from one constructor into another. - * - * Usage: - *
- * function ParentClass(a, b) { }
- * ParentClass.prototype.foo = function(a) { };
- *
- * function ChildClass(a, b, c) {
- *   ChildClass.base(this, 'constructor', a, b);
- * }
- * goog.inherits(ChildClass, ParentClass);
- *
- * var child = new ChildClass('a', 'b', 'see');
- * child.foo(); // This works.
- * 
- * - * @param {!Function} childCtor Child class. - * @param {!Function} parentCtor Parent class. - * @suppress {strictMissingProperties} superClass_ and base is not defined on - * Function. - * @deprecated Use ECMAScript class syntax instead. - */ -goog.inherits = function(childCtor, parentCtor) { - /** @constructor */ - function tempCtor() {} - tempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new tempCtor(); - /** @override */ - childCtor.prototype.constructor = childCtor; - - /** - * Calls superclass constructor/method. - * - * This function is only available if you use goog.inherits to - * express inheritance relationships between classes. - * - * NOTE: This is a replacement for goog.base and for superClass_ - * property defined in childCtor. - * - * @param {!Object} me Should always be "this". - * @param {string} methodName The method name to call. Calling - * superclass constructor can be done with the special string - * 'constructor'. - * @param {...*} var_args The arguments to pass to superclass - * method/constructor. - * @return {*} The return value of the superclass method/constructor. - */ - childCtor.base = function(me, methodName, var_args) { - // Copying using loop to avoid deop due to passing arguments object to - // function. This is faster in many JS engines as of late 2014. - var args = new Array(arguments.length - 2); - for (var i = 2; i < arguments.length; i++) { - args[i - 2] = arguments[i]; - } - return parentCtor.prototype[methodName].apply(me, args); - }; -}; - - -/** - * Allow for aliasing within scope functions. This function exists for - * uncompiled code - in compiled code the calls will be inlined and the aliases - * applied. In uncompiled code the function is simply run since the aliases as - * written are valid JavaScript. - * - * - * @param {function()} fn Function to call. This function can contain aliases - * to namespaces (e.g. "var dom = goog.dom") or classes - * (e.g. "var Timer = goog.Timer"). - * @deprecated Use goog.module instead. - */ -goog.scope = function(fn) { - if (goog.isInModuleLoader_()) { - throw new Error('goog.scope is not supported within a module.'); - } - fn.call(goog.global); -}; - - -/* - * To support uncompiled, strict mode bundles that use eval to divide source - * like so: - * eval('someSource;//# sourceUrl sourcefile.js'); - * We need to export the globally defined symbols "goog" and "COMPILED". - * Exporting "goog" breaks the compiler optimizations, so we required that - * be defined externally. - * NOTE: We don't use goog.exportSymbol here because we don't want to trigger - * extern generation when that compiler option is enabled. - */ -if (!COMPILED) { - goog.global['COMPILED'] = COMPILED; -} - - -//============================================================================== -// goog.defineClass implementation -//============================================================================== - - -/** - * Creates a restricted form of a Closure "class": - * - from the compiler's perspective, the instance returned from the - * constructor is sealed (no new properties may be added). This enables - * better checks. - * - the compiler will rewrite this definition to a form that is optimal - * for type checking and optimization (initially this will be a more - * traditional form). - * - * @param {Function} superClass The superclass, Object or null. - * @param {goog.defineClass.ClassDescriptor} def - * An object literal describing - * the class. It may have the following properties: - * "constructor": the constructor function - * "statics": an object literal containing methods to add to the constructor - * as "static" methods or a function that will receive the constructor - * function as its only parameter to which static properties can - * be added. - * all other properties are added to the prototype. - * @return {!Function} The class constructor. - * @deprecated Use ECMAScript class syntax instead. - */ -goog.defineClass = function(superClass, def) { - // TODO(johnlenz): consider making the superClass an optional parameter. - var constructor = def.constructor; - var statics = def.statics; - // Wrap the constructor prior to setting up the prototype and static methods. - if (!constructor || constructor == Object.prototype.constructor) { - constructor = function() { - throw new Error( - 'cannot instantiate an interface (no constructor defined).'); - }; - } - - var cls = goog.defineClass.createSealingConstructor_(constructor, superClass); - if (superClass) { - goog.inherits(cls, superClass); - } - - // Remove all the properties that should not be copied to the prototype. - delete def.constructor; - delete def.statics; - - goog.defineClass.applyProperties_(cls.prototype, def); - if (statics != null) { - if (statics instanceof Function) { - statics(cls); - } else { - goog.defineClass.applyProperties_(cls, statics); - } - } - - return cls; -}; - - -/** - * @typedef {{ - * constructor: (!Function|undefined), - * statics: (Object|undefined|function(Function):void) - * }} - */ -goog.defineClass.ClassDescriptor; - - -/** - * @define {boolean} Whether the instances returned by goog.defineClass should - * be sealed when possible. - * - * When sealing is disabled the constructor function will not be wrapped by - * goog.defineClass, making it incompatible with ES6 class methods. - */ -goog.defineClass.SEAL_CLASS_INSTANCES = - goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG); - - -/** - * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is - * defined, this function will wrap the constructor in a function that seals the - * results of the provided constructor function. - * - * @param {!Function} ctr The constructor whose results maybe be sealed. - * @param {Function} superClass The superclass constructor. - * @return {!Function} The replacement constructor. - * @private - */ -goog.defineClass.createSealingConstructor_ = function(ctr, superClass) { - if (!goog.defineClass.SEAL_CLASS_INSTANCES) { - // Do now wrap the constructor when sealing is disabled. Angular code - // depends on this for injection to work properly. - return ctr; - } - - // NOTE: The sealing behavior has been removed - - /** - * @this {Object} - * @return {?} - */ - var wrappedCtr = function() { - // Don't seal an instance of a subclass when it calls the constructor of - // its super class as there is most likely still setup to do. - var instance = ctr.apply(this, arguments) || this; - instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_]; - - return instance; - }; - - return wrappedCtr; -}; - - - -// TODO(johnlenz): share these values with the goog.object -/** - * The names of the fields that are defined on Object.prototype. - * @type {!Array} - * @private - * @const - */ -goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [ - 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', - 'toLocaleString', 'toString', 'valueOf' -]; - - -// TODO(johnlenz): share this function with the goog.object -/** - * @param {!Object} target The object to add properties to. - * @param {!Object} source The object to copy properties from. - * @private - */ -goog.defineClass.applyProperties_ = function(target, source) { - // TODO(johnlenz): update this to support ES5 getters/setters - - var key; - for (key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - - // For IE the for-in-loop does not contain any properties that are not - // enumerable on the prototype object (for example isPrototypeOf from - // Object.prototype) and it will also not include 'replace' on objects that - // extend String and change 'replace' (not that it is common for anyone to - // extend anything except Object). - for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) { - key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i]; - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } -}; - -/** - * Returns the parameter. - * @param {string} s - * @return {string} - * @private - */ -goog.identity_ = function(s) { - return s; -}; - - -/** - * Creates Trusted Types policy if Trusted Types are supported by the browser. - * The policy just blesses any string as a Trusted Type. It is not visibility - * restricted because anyone can also call trustedTypes.createPolicy directly. - * However, the allowed names should be restricted by a HTTP header and the - * reference to the created policy should be visibility restricted. - * @param {string} name - * @return {?TrustedTypePolicy} - */ -goog.createTrustedTypesPolicy = function(name) { - var policy = null; - var policyFactory = goog.global.trustedTypes; - if (!policyFactory || !policyFactory.createPolicy) { - return policy; - } - // trustedTypes.createPolicy throws if called with a name that is already - // registered, even in report-only mode. Until the API changes, catch the - // error not to break the applications functionally. In such case, the code - // will fall back to using regular Safe Types. - // TODO(koto): Remove catching once createPolicy API stops throwing. - try { - policy = policyFactory.createPolicy(name, { - createHTML: goog.identity_, - createScript: goog.identity_, - createScriptURL: goog.identity_ - }); - } catch (e) { - goog.logToConsole_(e.message); - } - return policy; -}; - -// There's a bug in the compiler where without collapse properties the -// Closure namespace defines do not guard code correctly. To help reduce code -// size also check for !COMPILED even though it redundant until this is fixed. -if (!COMPILED && goog.DEPENDENCIES_ENABLED) { - - - /** - * Tries to detect whether the current browser is Edge, based on the user - * agent. This matches only pre-Chromium Edge. - * @see https://docs.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-string - * @return {boolean} True if the current browser is Edge. - * @private - */ - goog.isEdge_ = function() { - var userAgent = goog.global.navigator && goog.global.navigator.userAgent ? - goog.global.navigator.userAgent : - ''; - var edgeRe = /Edge\/(\d+)(\.\d)*/i; - return !!userAgent.match(edgeRe); - }; - - - /** - * Tries to detect whether is in the context of an HTML document. - * @return {boolean} True if it looks like HTML document. - * @private - */ - goog.inHtmlDocument_ = function() { - /** @type {!Document} */ - var doc = goog.global.document; - return doc != null && 'write' in doc; // XULDocument misses write. - }; - - - /** - * We'd like to check for if the document readyState is 'loading'; however - * there are bugs on IE 10 and below where the readyState being anything other - * than 'complete' is not reliable. - * @return {boolean} - * @private - */ - goog.isDocumentLoading_ = function() { - // attachEvent is available on IE 6 thru 10 only, and thus can be used to - // detect those browsers. - /** @type {!HTMLDocument} */ - var doc = goog.global.document; - return doc.attachEvent ? doc.readyState != 'complete' : - doc.readyState == 'loading'; - }; - - - /** - * Tries to detect the base path of base.js script that bootstraps Closure. - * @private - */ - goog.findBasePath_ = function() { - if (goog.global.CLOSURE_BASE_PATH != undefined && - // Anti DOM-clobbering runtime check (b/37736576). - typeof goog.global.CLOSURE_BASE_PATH === 'string') { - goog.basePath = goog.global.CLOSURE_BASE_PATH; - return; - } else if (!goog.inHtmlDocument_()) { - return; - } - /** @type {!Document} */ - var doc = goog.global.document; - // If we have a currentScript available, use it exclusively. - var currentScript = doc.currentScript; - if (currentScript) { - var scripts = [currentScript]; - } else { - var scripts = doc.getElementsByTagName('SCRIPT'); - } - // Search backwards since the current script is in almost all cases the one - // that has base.js. - for (var i = scripts.length - 1; i >= 0; --i) { - var script = /** @type {!HTMLScriptElement} */ (scripts[i]); - var src = script.src; - var qmark = src.lastIndexOf('?'); - var l = qmark == -1 ? src.length : qmark; - if (src.slice(l - 7, l) == 'base.js') { - goog.basePath = src.slice(0, l - 7); - return; - } - } - }; - - goog.findBasePath_(); - - /** @struct @constructor @final */ - goog.Transpiler = function() { - /** @private {?Object} */ - this.requiresTranspilation_ = null; - /** @private {string} */ - this.transpilationTarget_ = goog.TRANSPILE_TO_LANGUAGE; - }; - /** - * Returns a newly created map from language mode string to a boolean - * indicating whether transpilation should be done for that mode as well as - * the highest level language that this environment supports. - * - * Guaranteed invariant: - * For any two modes, l1 and l2 where l2 is a newer mode than l1, - * `map[l1] == true` implies that `map[l2] == true`. - * - * Note this method is extracted and used elsewhere, so it cannot rely on - * anything external (it should easily be able to be transformed into a - * standalone, top level function). - * - * @private - * @return {{ - * target: string, - * map: !Object - * }} - */ - goog.Transpiler.prototype.createRequiresTranspilation_ = function() { - var transpilationTarget = 'es3'; - var /** !Object */ requiresTranspilation = {'es3': false}; - var transpilationRequiredForAllLaterModes = false; - - /** - * Adds an entry to requiresTranspliation for the given language mode. - * - * IMPORTANT: Calls must be made in order from oldest to newest language - * mode. - * @param {string} modeName - * @param {function(): boolean} isSupported Returns true if the JS engine - * supports the given mode. - */ - function addNewerLanguageTranspilationCheck(modeName, isSupported) { - if (transpilationRequiredForAllLaterModes) { - requiresTranspilation[modeName] = true; - } else if (isSupported()) { - transpilationTarget = modeName; - requiresTranspilation[modeName] = false; - } else { - requiresTranspilation[modeName] = true; - transpilationRequiredForAllLaterModes = true; - } - } - - /** - * Does the given code evaluate without syntax errors and return a truthy - * result? - */ - function /** boolean */ evalCheck(/** string */ code) { - try { - return !!eval(goog.CLOSURE_EVAL_PREFILTER_.createScript(code)); - } catch (ignored) { - return false; - } - } - - // Identify ES3-only browsers by their incorrect treatment of commas. - addNewerLanguageTranspilationCheck('es5', function() { - return evalCheck('[1,].length==1'); - }); - addNewerLanguageTranspilationCheck('es6', function() { - // Edge has a non-deterministic (i.e., not reproducible) bug with ES6: - // https://github.com/Microsoft/ChakraCore/issues/1496. - if (goog.isEdge_()) { - // The Reflect.construct test below is flaky on Edge. It can sometimes - // pass or fail on 40 15.15063, so just exit early for Edge and treat - // it as ES5. Until we're on a more up to date version just always use - // ES5. See https://github.com/Microsoft/ChakraCore/issues/3217. - return false; - } - // Test es6: [FF50 (?), Edge 14 (?), Chrome 50] - // (a) default params (specifically shadowing locals), - // (b) destructuring, (c) block-scoped functions, - // (d) for-of (const), (e) new.target/Reflect.construct - var es6fullTest = - 'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' + - 'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' + - 'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' + - 'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' + - '==3}'; - - return evalCheck('(()=>{"use strict";' + es6fullTest + '})()'); - }); - // ** and **= are the only new features in 'es7' - addNewerLanguageTranspilationCheck('es7', function() { - return evalCheck('2**3==8'); - }); - // async functions are the only new features in 'es8' - addNewerLanguageTranspilationCheck('es8', function() { - return evalCheck('async()=>1,1'); - }); - addNewerLanguageTranspilationCheck('es9', function() { - return evalCheck('({...rest}={}),1'); - }); - // optional catch binding, unescaped unicode paragraph separator in strings - addNewerLanguageTranspilationCheck('es_2019', function() { - return evalCheck('let r;try{r="\u2029"}catch{};r'); - }); - // optional chaining, nullish coalescing - // untested/unsupported: bigint, import meta - addNewerLanguageTranspilationCheck('es_2020', function() { - return evalCheck('null?.x??1'); - }); - addNewerLanguageTranspilationCheck('es_next', function() { - return false; // assume it always need to transpile - }); - return {target: transpilationTarget, map: requiresTranspilation}; - }; - - - /** - * Determines whether the given language needs to be transpiled. - * @param {string} lang - * @param {string|undefined} module - * @return {boolean} - */ - goog.Transpiler.prototype.needsTranspile = function(lang, module) { - if (goog.TRANSPILE == 'always') { - return true; - } else if (goog.TRANSPILE == 'never') { - return false; - } else if (!this.requiresTranspilation_) { - var obj = this.createRequiresTranspilation_(); - this.requiresTranspilation_ = obj.map; - this.transpilationTarget_ = this.transpilationTarget_ || obj.target; - } - if (lang in this.requiresTranspilation_) { - if (this.requiresTranspilation_[lang]) { - return true; - } else if ( - goog.inHtmlDocument_() && module == 'es6' && - !('noModule' in goog.global.document.createElement('script'))) { - return true; - } else { - return false; - } - } else { - throw new Error('Unknown language mode: ' + lang); - } - }; - - - /** - * Lazily retrieves the transpiler and applies it to the source. - * @param {string} code JS code. - * @param {string} path Path to the code. - * @return {string} The transpiled code. - */ - goog.Transpiler.prototype.transpile = function(code, path) { - // TODO(johnplaisted): We should delete goog.transpile_ and just have this - // function. But there's some compile error atm where goog.global is being - // stripped incorrectly without this. - return goog.transpile_(code, path, this.transpilationTarget_); - }; - - - /** @private @final {!goog.Transpiler} */ - goog.transpiler_ = new goog.Transpiler(); - - /** - * Rewrites closing script tags in input to avoid ending an enclosing script - * tag. - * - * @param {string} str - * @return {string} - * @private - */ - goog.protectScriptTag_ = function(str) { - return str.replace(/<\/(SCRIPT)/ig, '\\x3c/$1'); - }; - - - /** - * A debug loader is responsible for downloading and executing javascript - * files in an unbundled, uncompiled environment. - * - * This can be custimized via the setDependencyFactory method, or by - * CLOSURE_IMPORT_SCRIPT/CLOSURE_LOAD_FILE_SYNC. - * - * @struct @constructor @final @private - */ - goog.DebugLoader_ = function() { - /** @private @const {!Object} */ - this.dependencies_ = {}; - /** @private @const {!Object} */ - this.idToPath_ = {}; - /** @private @const {!Object} */ - this.written_ = {}; - /** @private @const {!Array} */ - this.loadingDeps_ = []; - /** @private {!Array} */ - this.depsToLoad_ = []; - /** @private {boolean} */ - this.paused_ = false; - /** @private {!goog.DependencyFactory} */ - this.factory_ = new goog.DependencyFactory(goog.transpiler_); - /** @private @const {!Object} */ - this.deferredCallbacks_ = {}; - /** @private @const {!Array} */ - this.deferredQueue_ = []; - }; - - /** - * @param {!Array} namespaces - * @param {function(): undefined} callback Function to call once all the - * namespaces have loaded. - */ - goog.DebugLoader_.prototype.bootstrap = function(namespaces, callback) { - var cb = callback; - function resolve() { - if (cb) { - goog.global.setTimeout(cb, 0); - cb = null; - } - } - - if (!namespaces.length) { - resolve(); - return; - } - - var deps = []; - for (var i = 0; i < namespaces.length; i++) { - var path = this.getPathFromDeps_(namespaces[i]); - if (!path) { - throw new Error('Unregonized namespace: ' + namespaces[i]); - } - deps.push(this.dependencies_[path]); - } - - var require = goog.require; - var loaded = 0; - for (var i = 0; i < namespaces.length; i++) { - require(namespaces[i]); - deps[i].onLoad(function() { - if (++loaded == namespaces.length) { - resolve(); - } - }); - } - }; - - - /** - * Loads the Closure Dependency file. - * - * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base - * loaded, setDependencyFactory called, and then this called. i.e. allows - * custom loading of the deps file. - */ - goog.DebugLoader_.prototype.loadClosureDeps = function() { - // Circumvent addDependency, which would try to transpile deps.js if - // transpile is set to always. - var relPath = 'deps.js'; - this.depsToLoad_.push(this.factory_.createDependency( - goog.normalizePath_(goog.basePath + relPath), relPath, [], [], {}, - false)); - this.loadDeps_(); - }; - - - /** - * Notifies the debug loader when a dependency has been requested. - * - * @param {string} absPathOrId Path of the dependency or goog id. - * @param {boolean=} opt_force - */ - goog.DebugLoader_.prototype.requested = function(absPathOrId, opt_force) { - var path = this.getPathFromDeps_(absPathOrId); - if (path && - (opt_force || this.areDepsLoaded_(this.dependencies_[path].requires))) { - var callback = this.deferredCallbacks_[path]; - if (callback) { - delete this.deferredCallbacks_[path]; - callback(); - } - } - }; - - - /** - * Sets the dependency factory, which can be used to create custom - * goog.Dependency implementations to control how dependencies are loaded. - * - * @param {!goog.DependencyFactory} factory - */ - goog.DebugLoader_.prototype.setDependencyFactory = function(factory) { - this.factory_ = factory; - }; - - - /** - * Travserses the dependency graph and queues the given dependency, and all of - * its transitive dependencies, for loading and then starts loading if not - * paused. - * - * @param {string} namespace - * @private - */ - goog.DebugLoader_.prototype.load_ = function(namespace) { - if (!this.getPathFromDeps_(namespace)) { - var errorMessage = 'goog.require could not find: ' + namespace; - goog.logToConsole_(errorMessage); - } else { - var loader = this; - - var deps = []; - - /** @param {string} namespace */ - var visit = function(namespace) { - var path = loader.getPathFromDeps_(namespace); - - if (!path) { - throw new Error('Bad dependency path or symbol: ' + namespace); - } - - if (loader.written_[path]) { - return; - } - - loader.written_[path] = true; - - var dep = loader.dependencies_[path]; - for (var i = 0; i < dep.requires.length; i++) { - if (!goog.isProvided_(dep.requires[i])) { - visit(dep.requires[i]); - } - } - - deps.push(dep); - }; - - visit(namespace); - - var wasLoading = !!this.depsToLoad_.length; - this.depsToLoad_ = this.depsToLoad_.concat(deps); - - if (!this.paused_ && !wasLoading) { - this.loadDeps_(); - } - } - }; - - - /** - * Loads any queued dependencies until they are all loaded or paused. - * - * @private - */ - goog.DebugLoader_.prototype.loadDeps_ = function() { - var loader = this; - var paused = this.paused_; - - while (this.depsToLoad_.length && !paused) { - (function() { - var loadCallDone = false; - var dep = loader.depsToLoad_.shift(); - - var loaded = false; - loader.loading_(dep); - - var controller = { - pause: function() { - if (loadCallDone) { - throw new Error('Cannot call pause after the call to load.'); - } else { - paused = true; - } - }, - resume: function() { - if (loadCallDone) { - loader.resume_(); - } else { - // Some dep called pause and then resume in the same load call. - // Just keep running this same loop. - paused = false; - } - }, - loaded: function() { - if (loaded) { - throw new Error('Double call to loaded.'); - } - - loaded = true; - loader.loaded_(dep); - }, - pending: function() { - // Defensive copy. - var pending = []; - for (var i = 0; i < loader.loadingDeps_.length; i++) { - pending.push(loader.loadingDeps_[i]); - } - return pending; - }, - /** - * @param {goog.ModuleType} type - */ - setModuleState: function(type) { - goog.moduleLoaderState_ = { - type: type, - moduleName: '', - declareLegacyNamespace: false - }; - }, - /** @type {function(string, string, string=)} */ - registerEs6ModuleExports: function( - path, exports, opt_closureNamespace) { - if (opt_closureNamespace) { - goog.loadedModules_[opt_closureNamespace] = { - exports: exports, - type: goog.ModuleType.ES6, - moduleId: opt_closureNamespace || '' - }; - } - }, - /** @type {function(string, ?)} */ - registerGoogModuleExports: function(moduleId, exports) { - goog.loadedModules_[moduleId] = { - exports: exports, - type: goog.ModuleType.GOOG, - moduleId: moduleId - }; - }, - clearModuleState: function() { - goog.moduleLoaderState_ = null; - }, - defer: function(callback) { - if (loadCallDone) { - throw new Error( - 'Cannot register with defer after the call to load.'); - } - loader.defer_(dep, callback); - }, - areDepsLoaded: function() { - return loader.areDepsLoaded_(dep.requires); - } - }; - - try { - dep.load(controller); - } finally { - loadCallDone = true; - } - })(); - } - - if (paused) { - this.pause_(); - } - }; - - - /** @private */ - goog.DebugLoader_.prototype.pause_ = function() { - this.paused_ = true; - }; - - - /** @private */ - goog.DebugLoader_.prototype.resume_ = function() { - if (this.paused_) { - this.paused_ = false; - this.loadDeps_(); - } - }; - - - /** - * Marks the given dependency as loading (load has been called but it has not - * yet marked itself as finished). Useful for dependencies that want to know - * what else is loading. Example: goog.modules cannot eval if there are - * loading dependencies. - * - * @param {!goog.Dependency} dep - * @private - */ - goog.DebugLoader_.prototype.loading_ = function(dep) { - this.loadingDeps_.push(dep); - }; - - - /** - * Marks the given dependency as having finished loading and being available - * for require. - * - * @param {!goog.Dependency} dep - * @private - */ - goog.DebugLoader_.prototype.loaded_ = function(dep) { - for (var i = 0; i < this.loadingDeps_.length; i++) { - if (this.loadingDeps_[i] == dep) { - this.loadingDeps_.splice(i, 1); - break; - } - } - - for (var i = 0; i < this.deferredQueue_.length; i++) { - if (this.deferredQueue_[i] == dep.path) { - this.deferredQueue_.splice(i, 1); - break; - } - } - - if (this.loadingDeps_.length == this.deferredQueue_.length && - !this.depsToLoad_.length) { - // Something has asked to load these, but they may not be directly - // required again later, so load them now that we know we're done loading - // everything else. e.g. a goog module entry point. - while (this.deferredQueue_.length) { - this.requested(this.deferredQueue_.shift(), true); - } - } - - dep.loaded(); - }; - - - /** - * @param {!Array} pathsOrIds - * @return {boolean} - * @private - */ - goog.DebugLoader_.prototype.areDepsLoaded_ = function(pathsOrIds) { - for (var i = 0; i < pathsOrIds.length; i++) { - var path = this.getPathFromDeps_(pathsOrIds[i]); - if (!path || - (!(path in this.deferredCallbacks_) && - !goog.isProvided_(pathsOrIds[i]))) { - return false; - } - } - - return true; - }; - - - /** - * @param {string} absPathOrId - * @return {?string} - * @private - */ - goog.DebugLoader_.prototype.getPathFromDeps_ = function(absPathOrId) { - if (absPathOrId in this.idToPath_) { - return this.idToPath_[absPathOrId]; - } else if (absPathOrId in this.dependencies_) { - return absPathOrId; - } else { - return null; - } - }; - - - /** - * @param {!goog.Dependency} dependency - * @param {!Function} callback - * @private - */ - goog.DebugLoader_.prototype.defer_ = function(dependency, callback) { - this.deferredCallbacks_[dependency.path] = callback; - this.deferredQueue_.push(dependency.path); - }; - - - /** - * Interface for goog.Dependency implementations to have some control over - * loading of dependencies. - * - * @record - */ - goog.LoadController = function() {}; - - - /** - * Tells the controller to halt loading of more dependencies. - */ - goog.LoadController.prototype.pause = function() {}; - - - /** - * Tells the controller to resume loading of more dependencies if paused. - */ - goog.LoadController.prototype.resume = function() {}; - - - /** - * Tells the controller that this dependency has finished loading. - * - * This causes this to be removed from pending() and any load callbacks to - * fire. - */ - goog.LoadController.prototype.loaded = function() {}; - - - /** - * List of dependencies on which load has been called but which have not - * called loaded on their controller. This includes the current dependency. - * - * @return {!Array} - */ - goog.LoadController.prototype.pending = function() {}; - - - /** - * Registers an object as an ES6 module's exports so that goog.modules may - * require it by path. - * - * @param {string} path Full path of the module. - * @param {?} exports - * @param {string=} opt_closureNamespace Closure namespace to associate with - * this module. - */ - goog.LoadController.prototype.registerEs6ModuleExports = function( - path, exports, opt_closureNamespace) {}; - - - /** - * Sets the current module state. - * - * @param {goog.ModuleType} type Type of module. - */ - goog.LoadController.prototype.setModuleState = function(type) {}; - - - /** - * Clears the current module state. - */ - goog.LoadController.prototype.clearModuleState = function() {}; - - - /** - * Registers a callback to call once the dependency is actually requested - * via goog.require + all of the immediate dependencies have been loaded or - * all other files have been loaded. Allows for lazy loading until - * require'd without pausing dependency loading, which is needed on old IE. - * - * @param {!Function} callback - */ - goog.LoadController.prototype.defer = function(callback) {}; - - - /** - * @return {boolean} - */ - goog.LoadController.prototype.areDepsLoaded = function() {}; - - - /** - * Basic super class for all dependencies Closure Library can load. - * - * This default implementation is designed to load untranspiled, non-module - * scripts in a web broswer. - * - * For transpiled non-goog.module files {@see goog.TranspiledDependency}. - * For goog.modules see {@see goog.GoogModuleDependency}. - * For untranspiled ES6 modules {@see goog.Es6ModuleDependency}. - * - * @param {string} path Absolute path of this script. - * @param {string} relativePath Path of this script relative to goog.basePath. - * @param {!Array} provides goog.provided or goog.module symbols - * in this file. - * @param {!Array} requires goog symbols or relative paths to Closure - * this depends on. - * @param {!Object} loadFlags - * @struct @constructor - */ - goog.Dependency = function( - path, relativePath, provides, requires, loadFlags) { - /** @const */ - this.path = path; - /** @const */ - this.relativePath = relativePath; - /** @const */ - this.provides = provides; - /** @const */ - this.requires = requires; - /** @const */ - this.loadFlags = loadFlags; - /** @private {boolean} */ - this.loaded_ = false; - /** @private {!Array} */ - this.loadCallbacks_ = []; - }; - - - /** - * @return {string} The pathname part of this dependency's path if it is a - * URI. - */ - goog.Dependency.prototype.getPathName = function() { - var pathName = this.path; - var protocolIndex = pathName.indexOf('://'); - if (protocolIndex >= 0) { - pathName = pathName.substring(protocolIndex + 3); - var slashIndex = pathName.indexOf('/'); - if (slashIndex >= 0) { - pathName = pathName.substring(slashIndex + 1); - } - } - return pathName; - }; - - - /** - * @param {function()} callback Callback to fire as soon as this has loaded. - * @final - */ - goog.Dependency.prototype.onLoad = function(callback) { - if (this.loaded_) { - callback(); - } else { - this.loadCallbacks_.push(callback); - } - }; - - - /** - * Marks this dependency as loaded and fires any callbacks registered with - * onLoad. - * @final - */ - goog.Dependency.prototype.loaded = function() { - this.loaded_ = true; - var callbacks = this.loadCallbacks_; - this.loadCallbacks_ = []; - for (var i = 0; i < callbacks.length; i++) { - callbacks[i](); - } - }; - - - /** - * Whether or not document.written / appended script tags should be deferred. - * - * @private {boolean} - */ - goog.Dependency.defer_ = false; - - - /** - * Map of script ready / state change callbacks. Old IE cannot handle putting - * these properties on goog.global. - * - * @private @const {!Object} - */ - goog.Dependency.callbackMap_ = {}; - - - /** - * @param {function(...?):?} callback - * @return {string} - * @private - */ - goog.Dependency.registerCallback_ = function(callback) { - var key = Math.random().toString(32); - goog.Dependency.callbackMap_[key] = callback; - return key; - }; - - - /** - * @param {string} key - * @private - */ - goog.Dependency.unregisterCallback_ = function(key) { - delete goog.Dependency.callbackMap_[key]; - }; - - - /** - * @param {string} key - * @param {...?} var_args - * @private - * @suppress {unusedPrivateMembers} - */ - goog.Dependency.callback_ = function(key, var_args) { - if (key in goog.Dependency.callbackMap_) { - var callback = goog.Dependency.callbackMap_[key]; - var args = []; - for (var i = 1; i < arguments.length; i++) { - args.push(arguments[i]); - } - callback.apply(undefined, args); - } else { - var errorMessage = 'Callback key ' + key + - ' does not exist (was base.js loaded more than once?).'; - throw Error(errorMessage); - } - }; - - - /** - * Starts loading this dependency. This dependency can pause loading if it - * needs to and resume it later via the controller interface. - * - * When this is loaded it should call controller.loaded(). Note that this will - * end up calling the loaded method of this dependency; there is no need to - * call it explicitly. - * - * @param {!goog.LoadController} controller - */ - goog.Dependency.prototype.load = function(controller) { - if (goog.global.CLOSURE_IMPORT_SCRIPT) { - if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) { - controller.loaded(); - } else { - controller.pause(); - } - return; - } - - if (!goog.inHtmlDocument_()) { - goog.logToConsole_( - 'Cannot use default debug loader outside of HTML documents.'); - if (this.relativePath == 'deps.js') { - // Some old code is relying on base.js auto loading deps.js failing with - // no error before later setting CLOSURE_IMPORT_SCRIPT. - // CLOSURE_IMPORT_SCRIPT should be set *before* base.js is loaded, or - // CLOSURE_NO_DEPS set to true. - goog.logToConsole_( - 'Consider setting CLOSURE_IMPORT_SCRIPT before loading base.js, ' + - 'or setting CLOSURE_NO_DEPS to true.'); - controller.loaded(); - } else { - controller.pause(); - } - return; - } - - /** @type {!HTMLDocument} */ - var doc = goog.global.document; - - // If the user tries to require a new symbol after document load, - // something has gone terribly wrong. Doing a document.write would - // wipe out the page. This does not apply to the CSP-compliant method - // of writing script tags. - if (doc.readyState == 'complete' && - !goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) { - // Certain test frameworks load base.js multiple times, which tries - // to write deps.js each time. If that happens, just fail silently. - // These frameworks wipe the page between each load of base.js, so this - // is OK. - var isDeps = /\bdeps.js$/.test(this.path); - if (isDeps) { - controller.loaded(); - return; - } else { - throw Error('Cannot write "' + this.path + '" after document load'); - } - } - - var nonce = goog.getScriptNonce_(); - if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING && - goog.isDocumentLoading_()) { - var key; - var callback = function(script) { - if (script.readyState && script.readyState != 'complete') { - script.onload = callback; - return; - } - goog.Dependency.unregisterCallback_(key); - controller.loaded(); - }; - key = goog.Dependency.registerCallback_(callback); - - var defer = goog.Dependency.defer_ ? ' defer' : ''; - var nonceAttr = nonce ? ' nonce="' + nonce + '"' : ''; - var script = '