diff --git a/core/requires.js b/core/requires.js index 1ce08dddcac..974db99fedc 100644 --- a/core/requires.js +++ b/core/requires.js @@ -83,5 +83,4 @@ goog.require('Blockly.zelos.Renderer'); // Classic is the default theme. goog.require('Blockly.Themes.Classic'); -goog.require('Blockly.serialization.blocks'); -goog.require('Blockly.serialization.variables'); +goog.require('Blockly.serialization.workspaces'); diff --git a/core/serialization/blocks.js b/core/serialization/blocks.js index 4ab79a3a45b..86a1bc33334 100644 --- a/core/serialization/blocks.js +++ b/core/serialization/blocks.js @@ -17,6 +17,8 @@ goog.module.declareLegacyNamespace(); const Block = goog.requireType('Blockly.Block'); // eslint-disable-next-line no-unused-vars const Connection = goog.requireType('Blockly.Connection'); +// eslint-disable-next-line no-unused-vars +const Workspace = goog.requireType('Blockly.Workspace'); const Xml = goog.require('Blockly.Xml'); const inputTypes = goog.require('Blockly.inputTypes'); @@ -255,3 +257,14 @@ const saveConnection = function(connection) { } return state; }; + +/** + * Loads the block represented by the given state into the given workspace. + * @param {!State} state The state of a block to deserialize into the workspace. + * @param {!Workspace} workspace The workspace to add the block to. + */ +// eslint-disable-next-line no-unused-vars +const load = function(state, workspace) { + // Temporarily NOP while connecting things together. +}; +exports.load = load; diff --git a/core/serialization/serialization.js b/core/serialization/serialization.js deleted file mode 100644 index c53a2eb6432..00000000000 --- a/core/serialization/serialization.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Contains top-level functions for serialization of the workspace - * to JavaScript objects. - */ -'use strict'; - -/** - * The top level namespace for JavaScript Object serialization. - * @namespace Blockly.serialization - */ -goog.module('Blockly.serialization'); -goog.module.declareLegacyNamespace(); diff --git a/core/serialization/workspaces.js b/core/serialization/workspaces.js new file mode 100644 index 00000000000..b4600f9879a --- /dev/null +++ b/core/serialization/workspaces.js @@ -0,0 +1,80 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Contains top-level functions for serializing workspaces to + * plain JavaScript objects. + */ +'use strict'; + +goog.module('Blockly.serialization.workspaces'); +goog.module.declareLegacyNamespace(); + +// eslint-disable-next-line no-unused-vars +const Workspace = goog.require('Blockly.Workspace'); +const blocks = goog.require('Blockly.serialization.blocks'); +const variables = goog.require('Blockly.serialization.variables'); + + +/** + * Returns the state of the workspace as a plain JavaScript object. + * @param {!Workspace} workspace The workspace to serialize. + * @return {!Object} The serialized state of the workspace. + */ +const save = function(workspace) { + const state = Object.create(null); + + // TODO: Switch this to use plugin serialization system (once it is built). + const variableState = []; + const vars = workspace.getAllVariables(); + for (let i = 0; i < vars.length; i++) { + variableState.push(variables.save(vars[i])); + } + if (variableState.length) { + state['variables'] = variableState; + } + + const blockState = []; + for (let block of workspace.getTopBlocks(false)) { + blockState.push( + blocks.save(block, {addCoordinates: true})); + } + if (blockState.length) { + // This is an object to support adding language version later. + state['blocks'] = { + 'blocks': blockState + }; + } + + return state; +}; +exports.save = save; + +/** + * Loads the variable represented by the given state into the given workspace. + * @param {!Object} state The state of the workspace to deserialize + * into the workspace. + * @param {!Workspace} workspace The workspace to add the new state to. + */ +const load = function(state, workspace) { + // TODO: Switch this to use plugin serialization system (once it is built). + // TODO: Add something for clearing the state before deserializing. + + if (state['variables']) { + const variableStates = state['variables']; + for (let i = 0; i < variableStates.length; i++) { + variables.load(variableStates[i], workspace); + } + } + + if (state['blocks']) { + const blockStates = state['blocks']['blocks']; + for (let i = 0; i < blockStates.length; i++) { + blocks.load(blockStates[i], workspace); + } + } +}; +exports.load = load; diff --git a/tests/deps.js b/tests/deps.js index 9dfa475c3df..6de793267bf 100644 --- a/tests/deps.js +++ b/tests/deps.js @@ -151,11 +151,11 @@ goog.addDependency('../../core/renderers/zelos/measurables/row_elements.js', ['B goog.addDependency('../../core/renderers/zelos/measurables/rows.js', ['Blockly.zelos.BottomRow', 'Blockly.zelos.TopRow'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.TopRow', 'Blockly.utils.object']); goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider']); goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo']); -goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.ContextMenuItems', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.ShortcutItems', 'Blockly.Themes.Classic', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.serialization.blocks', 'Blockly.serialization.variables', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer']); +goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.ContextMenuItems', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.ShortcutItems', 'Blockly.Themes.Classic', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.serialization.workspaces', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer']); goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Events', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.Svg', 'Blockly.utils.dom']); goog.addDependency('../../core/serialization/blocks.js', ['Blockly.serialization.blocks'], ['Blockly.Xml', 'Blockly.inputTypes'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/serialization/serialization.js', ['Blockly.serialization'], [], {'module': 'goog'}); goog.addDependency('../../core/serialization/variables.js', ['Blockly.serialization.variables'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/workspaces.js', ['Blockly.serialization.workspaces'], ['Blockly.Workspace', 'Blockly.serialization.blocks', 'Blockly.serialization.variables'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/shortcut_items.js', ['Blockly.ShortcutItems'], ['Blockly.Gesture', 'Blockly.ShortcutRegistry', 'Blockly.utils.KeyCodes']); goog.addDependency('../../core/shortcut_registry.js', ['Blockly.ShortcutRegistry'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object']); goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.registry', 'Blockly.utils', 'Blockly.utils.object']); diff --git a/tests/mocha/serializer_test.js b/tests/mocha/serializer_test.js index 8a77e704d71..8bd3e672d88 100644 --- a/tests/mocha/serializer_test.js +++ b/tests/mocha/serializer_test.js @@ -1726,14 +1726,16 @@ Serializer.testSuites = [ ]; var runSerializerTestSuite = (serializer, deserializer, testSuite) => { + const workspaces = Blockly.serialization.workspaces; const createTestFunction = function(test) { return function() { Blockly.Xml.domToWorkspace( Blockly.Xml.textToDom(test.xml), this.workspace); if (serializer && deserializer) { - // TODO: Add support for custom serializeers and deserializers. - // Will be added once we have the JSO format working. + const save = serializer(workspaces.save(this.workspace)); + this.workspace.clear(); + workspaces.load(deserializer(save), this.workspace); } var newXml = Blockly.Xml.workspaceToDom(this.workspace); chai.assert.equal(Blockly.Xml.domToText(newXml), test.xml); @@ -1766,3 +1768,5 @@ var runSerializerTestSuite = (serializer, deserializer, testSuite) => { }; runSerializerTestSuite(null, null, Serializer); +Serializer.skip = true; +runSerializerTestSuite(state => state, state => state, Serializer);