From 6e1ffe09c218bfaaac8c988181172ba7d2e78744 Mon Sep 17 00:00:00 2001 From: Andrew n marshall Date: Fri, 3 Mar 2017 08:40:00 -0800 Subject: [PATCH] Adding Blockly.Xml.appendDomToWorkspace() (#962) This is a copy (with additional comments) of PR #822 (and also #961) by @qnoirhomme with unrelated files removed. See #822 for full review. --- core/xml.js | 58 +++++++++++++++++++++++ tests/jsunit/xml_test.js | 31 ++++++++++++ tests/workspace_svg/workspace_svg_test.js | 23 +++++++++ 3 files changed, 112 insertions(+) diff --git a/core/xml.js b/core/xml.js index a3e8b6b2d..198fae004 100644 --- a/core/xml.js +++ b/core/xml.js @@ -279,6 +279,7 @@ Blockly.Xml.textToDom = function(text) { * Decode an XML DOM and create blocks on the workspace. * @param {!Element} xml XML DOM. * @param {!Blockly.Workspace} workspace The workspace. + * @return {Array.} An array containing new block ids. */ Blockly.Xml.domToWorkspace = function(xml, workspace) { if (xml instanceof Blockly.Workspace) { @@ -292,6 +293,7 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) { if (workspace.RTL) { width = workspace.getWidth(); } + var newBlockIds = []; // A list of block ids added by this call. Blockly.Field.startCache(); // Safari 7.1.3 is known to provide node lists with extra references to // children beyond the lists' length. Trust the length, do not use the @@ -315,6 +317,7 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) { // that means an undo is in progress. Such a block is expected // to be moved to a nested destination in the next operation. var block = Blockly.Xml.domToBlock(xmlChild, workspace); + newBlockIds.push(block.id); var blockX = parseInt(xmlChild.getAttribute('x'), 10); var blockY = parseInt(xmlChild.getAttribute('y'), 10); if (!isNaN(blockX) && !isNaN(blockY)) { @@ -334,6 +337,61 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) { if (workspace.setResizesEnabled) { workspace.setResizesEnabled(true); } + return newBlockIds; +}; + +/** + * Decode an XML DOM and create blocks on the workspace. Position the new + * blocks immediately below prior blocks, aligned by their starting edge. + * @param {!Element} xml The XML DOM. + * @param {!Blockly.Workspace} workspace The workspace to add to. + * @return {Array.} An array containing new block ids. + */ +Blockly.Xml.appendDomToWorkspace = function(xml, workspace) { + var bbox; //bounding box of the current blocks + // first check if we have a workspaceSvg otherwise the block have no shape + // and the position does not matter + if (workspace.hasOwnProperty('scale')) { + var savetab = Blockly.BlockSvg.TAB_WIDTH; + try { + Blockly.BlockSvg.TAB_WIDTH = 0; + var bbox = workspace.getBlocksBoundingBox(); + } finally { + Blockly.BlockSvg.TAB_WIDTH = savetab; + } + } + // load the new blocks into the workspace and get the ids of the new blocks + var newBlockIds = Blockly.Xml.domToWorkspace(xml,workspace); + if (bbox && bbox.height) { // check if any previous block + var offsetY = 0; // offset to add to y of the new block + var offsetX = 0; + var farY = bbox.y + bbox.height; //bottom position + var topX = bbox.x; // x of bounding box + // check position of the new blocks + var newX = Infinity; // x of top corner + var newY = Infinity; // y of top corner + for (var i = 0; i < newBlockIds.length; i++) { + var blockXY = workspace.getBlockById(newBlockIds[i]).getRelativeToSurfaceXY(); + if (blockXY.y < newY) { + newY = blockXY.y; + } + if (blockXY.x < newX) { //if we align also on x + newX = blockXY.x; + } + } + offsetY = farY - newY + Blockly.BlockSvg.SEP_SPACE_Y; + offsetX = topX - newX; + // move the new blocks to append them at the bottom + var width; // Not used in LTR. + if (workspace.RTL) { + width = workspace.getWidth(); + } + for (var i = 0; i < newBlockIds.length; i++) { + var block = workspace.getBlockById(newBlockIds[i]); + block.moveBy(workspace.RTL ? width - offsetX : offsetX, offsetY); + } + } + return newBlockIds; }; /** diff --git a/tests/jsunit/xml_test.js b/tests/jsunit/xml_test.js index 7365a850a..0b4e052bb 100644 --- a/tests/jsunit/xml_test.js +++ b/tests/jsunit/xml_test.js @@ -90,3 +90,34 @@ function test_domToPrettyText() { assertEquals('Round trip', XML_TEXT.replace(/\s+/g, ''), text.replace(/\s+/g, '')); } + +/** + * Tests the that appendDomToWorkspace works in a headless mode. + * Also see test_appendDomToWorkspace() in workspace_svg_test.js. + */ +unction test_appendDomToWorkspace() { + Blockly.Blocks.test_block = { + init: function() { + this.jsonInit({ + message0: 'test', + }); + } + }; + + try { + var dom = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ''); + var workspace = new Blockly.Workspace(); + Blockly.Xml.appendDomToWorkspace(dom, workspace); + assertEquals('Block count', 1, workspace.getAllBlocks().length); + var newBlockIds = Blockly.Xml.appendDomToWorkspace(dom, workspace); + assertEquals('Block count', 2, workspace.getAllBlocks().length); + assertEquals('Number of new block ids',1,newBlockIds.length); + } finally { + delete Blockly.Blocks.test_block; + workspace.dispose(); + } +} diff --git a/tests/workspace_svg/workspace_svg_test.js b/tests/workspace_svg/workspace_svg_test.js index ea25eaef3..f5d924f5b 100644 --- a/tests/workspace_svg/workspace_svg_test.js +++ b/tests/workspace_svg/workspace_svg_test.js @@ -70,3 +70,26 @@ function test_flatWorkspace() { workspace.dispose(); } } + +/** Tests the alignment of appendDomToWorkspace with WorkspaceSvg. */ +function test_appendDomToWorkspace() { + var workspace = helper_createWorkspaceWithToolbox(); + try { + var dom = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ''); + Blockly.Xml.appendDomToWorkspace(dom, workspace); + assertEquals('Block count', 1, workspace.getAllBlocks().length); + Blockly.Xml.appendDomToWorkspace(dom, workspace); + assertEquals('Block count', 2, workspace.getAllBlocks().length); + var blocks = workspace.getAllBlocks(); + assertEquals('Block 1 position x',21,blocks[0].getRelativeToSurfaceXY().x); + assertEquals('Block 1 position y',23,blocks[0].getRelativeToSurfaceXY().y); + assertEquals('Block 2 position x',21,blocks[1].getRelativeToSurfaceXY().x); + assertEquals('Block 2 position y',23 + blocks[0].getHeightWidth().height + Blockly.BlockSvg.SEP_SPACE_Y,blocks[1].getRelativeToSurfaceXY().y); + } finally { + workspace.dispose(); + } +}