diff --git a/core/block_svg.js b/core/block_svg.js index 08e5addcb63..9233acdbd0a 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -963,18 +963,28 @@ Blockly.BlockSvg.prototype.toCopyData = function() { if (this.isInsertionMarker_) { return null; } - var xml = /** @type {!Element} */ (Blockly.Xml.blockToDom(this, true)); - // Copy only the selected block and internal blocks. - Blockly.Xml.deleteNext(xml); - // Encode start position in XML. - var xy = this.getRelativeToSurfaceXY(); - xml.setAttribute('x', this.RTL ? -xy.x : xy.x); - xml.setAttribute('y', xy.y); - return { - xml: xml, - source: this.workspace, - typeCounts: Blockly.utils.getBlockTypeCounts(this, true) - }; + if (this.mutationToDom && !this.saveExtraState) { + var xml = /** @type {!Element} */ (Blockly.Xml.blockToDom(this, true)); + // Copy only the selected block and internal blocks. + Blockly.Xml.deleteNext(xml); + // Encode start position in XML. + var xy = this.getRelativeToSurfaceXY(); + xml.setAttribute('x', this.RTL ? -xy.x : xy.x); + xml.setAttribute('y', xy.y); + return { + saveInfo: xml, + source: this.workspace, + typeCounts: Blockly.utils.getBlockTypeCounts(this, true) + }; + } else { + return { + saveInfo: /** @type {!Blockly.serialization.blocks.State} */ + (Blockly.serialization.blocks.save( + this, {addCoordinates: true, addNextBlocks: false})), + source: this.workspace, + typeCounts: Blockly.utils.getBlockTypeCounts(this, true) + }; + } }; /** diff --git a/core/blockly.js b/core/blockly.js index 92716e4d1ea..da643fff58c 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -92,24 +92,9 @@ Blockly.draggingConnections = []; /** * Contents of the local clipboard. - * @type {Element} - * @private - */ -Blockly.clipboardXml_ = null; - -/** - * Source of the local clipboard. - * @type {Blockly.WorkspaceSvg} - * @private - */ -Blockly.clipboardSource_ = null; - -/** - * Map of types to type counts for the clipboard object and descendants. - * @type {Object} - * @private + * @type {?Blockly.ICopyable.CopyData} */ -Blockly.clipboardTypeCounts_ = null; +Blockly.clipboardData_ = null; /** * Cached value for whether 3D is supported. @@ -234,12 +219,7 @@ Blockly.deleteBlock = function(selected) { * @package */ Blockly.copy = function(toCopy) { - var data = toCopy.toCopyData(); - if (data) { - Blockly.clipboardXml_ = data.xml; - Blockly.clipboardSource_ = data.source; - Blockly.clipboardTypeCounts_ = data.typeCounts; - } + Blockly.clipboardData_ = toCopy.toCopyData(); }; /** @@ -248,19 +228,19 @@ Blockly.copy = function(toCopy) { * @package */ Blockly.paste = function() { - if (!Blockly.clipboardXml_) { + if (!Blockly.clipboardData_) { return false; } // Pasting always pastes to the main workspace, even if the copy // started in a flyout workspace. - var workspace = Blockly.clipboardSource_; + var workspace = Blockly.clipboardData_.source; if (workspace.isFlyout) { workspace = workspace.targetWorkspace; } - if (Blockly.clipboardTypeCounts_ && - workspace.isCapacityAvailable(Blockly.clipboardTypeCounts_)) { + if (Blockly.clipboardData_.typeCounts && + workspace.isCapacityAvailable(Blockly.clipboardData_.typeCounts)) { Blockly.Events.setGroup(true); - workspace.paste(Blockly.clipboardXml_); + workspace.paste(Blockly.clipboardData_.saveInfo); Blockly.Events.setGroup(false); return true; } @@ -274,17 +254,10 @@ Blockly.paste = function() { * @package */ Blockly.duplicate = function(toDuplicate) { - // Save the clipboard. - var clipboardXml = Blockly.clipboardXml_; - var clipboardSource = Blockly.clipboardSource_; - - // Create a duplicate via a copy/paste operation. + var data = Blockly.clipboardData_; Blockly.copy(toDuplicate); - toDuplicate.workspace.paste(Blockly.clipboardXml_); - - // Restore the clipboard. - Blockly.clipboardXml_ = clipboardXml; - Blockly.clipboardSource_ = clipboardSource; + toDuplicate.workspace.paste(Blockly.clipboardData_.saveInfo); + Blockly.clipboardData_ = data; }; /** diff --git a/core/interfaces/i_copyable.js b/core/interfaces/i_copyable.js index 4aeac977649..1deff27d16a 100644 --- a/core/interfaces/i_copyable.js +++ b/core/interfaces/i_copyable.js @@ -26,15 +26,17 @@ Blockly.ICopyable = function() {}; /** * Encode for copying. * @return {?Blockly.ICopyable.CopyData} Copy metadata. + * @package */ Blockly.ICopyable.prototype.toCopyData; /** * Copy Metadata. * @typedef {{ - * xml:!Element, + * saveInfo:(!Object|!Element), * source:Blockly.WorkspaceSvg, * typeCounts:?Object * }} + * @package */ Blockly.ICopyable.CopyData; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 21ee217ae77..f2b8495f931 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1466,40 +1466,58 @@ Blockly.WorkspaceSvg.prototype.highlightBlock = function(id, opt_state) { }; /** - * Paste the provided block onto the workspace. - * @param {!Element|!DocumentFragment} xmlBlock XML block element or an empty - * DocumentFragment if the block was an insertion marker. + * Paste the provided block or workspace comment onto the workspace. + * @param {!Object|!Element|!DocumentFragment} state + * The representation of the thing to paste. */ -Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) { - if (!this.rendered || !xmlBlock.tagName || xmlBlock.getElementsByTagName('block').length >= - this.remainingCapacity()) { +Blockly.WorkspaceSvg.prototype.paste = function(state) { + if (!this.rendered) { return; } - // The check above for tagName rules out the possibility of this being a DocumentFragment. - xmlBlock = /** @type {!Element} */ (xmlBlock); - if (this.currentGesture_) { - this.currentGesture_.cancel(); // Dragging while pasting? No. - } - if (xmlBlock.tagName.toLowerCase() == 'comment') { - this.pasteWorkspaceComment_(xmlBlock); - } else { - this.pasteBlock_(xmlBlock); + if (state['type']) { + if (this.currentGesture_) { + // Dragging while pasting? No. + this.currentGesture_.cancel(); + } + this.pasteBlock_( + null, /** @type {!Blockly.serialization.blocks.State} */ (state)); + } else if (state.tagName) { // Ignore document fragments. + if (this.currentGesture_) { + // Dragging while pasting? No. + this.currentGesture_.cancel(); + } + var xml = /** @type {!Element} */ (state); + if (xml.tagName.toLowerCase() == 'comment') { + this.pasteWorkspaceComment_(xml); + } else { + this.pasteBlock_(xml, null); + } } }; /** * Paste the provided block onto the workspace. - * @param {!Element} xmlBlock XML block element. + * @param {?Element} xmlBlock XML block element. + * @param {?Blockly.serialization.blocks.State} jsonBlock JSON block + * representation. * @private */ -Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock) { +Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock, jsonBlock) { Blockly.Events.disable(); try { - var block = Blockly.Xml.domToBlock(xmlBlock, this); + var block; + var blockX; + var blockY; + if (xmlBlock) { + block = Blockly.Xml.domToBlock(xmlBlock, this); + blockX = parseInt(xmlBlock.getAttribute('x'), 10); + blockY = parseInt(xmlBlock.getAttribute('y'), 10); + } else if (jsonBlock) { + block = Blockly.serialization.blocks.load(jsonBlock, this); + blockX = jsonBlock['x']; + blockY = jsonBlock['y']; + } - // Move the duplicate to original position. - var blockX = parseInt(xmlBlock.getAttribute('x'), 10); - var blockY = parseInt(xmlBlock.getAttribute('y'), 10); if (!isNaN(blockX) && !isNaN(blockY)) { if (this.RTL) { blockX = -blockX; @@ -1538,7 +1556,7 @@ Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock) { blockY += Blockly.SNAP_RADIUS * 2; } } while (collide); - block.moveBy(blockX, blockY); + block.moveTo({x: blockX, y: blockY}); } } finally { Blockly.Events.enable();