diff --git a/core/xml.js b/core/xml.js
index 0120ed279..45b3e0ca6 100644
--- a/core/xml.js
+++ b/core/xml.js
@@ -77,10 +77,20 @@ Blockly.Xml.variablesToDom = function(variableList) {
* @return {!Element} Tree of XML elements.
*/
Blockly.Xml.blockToDomWithXY = function(block, opt_noId) {
+ if (block.isInsertionMarker()) { // Skip over insertion markers.
+ block = block.getChildren(false)[0];
+ if (!block) {
+ // Disappears when appended. Cast to ANY b/c DocumentFragment -> Element
+ // is invalid. We have to cast to ANY in between.
+ return /** @type{?} */ (new DocumentFragment());
+ }
+ }
+
var width; // Not used in LTR.
if (block.workspace.RTL) {
width = block.workspace.getWidth();
}
+
var element = Blockly.Xml.blockToDom(block, opt_noId);
var xy = block.getRelativeToSurfaceXY();
element.setAttribute('x',
@@ -131,6 +141,19 @@ Blockly.Xml.allFieldsToDom_ = function(block, element) {
* @return {!Element} Tree of XML elements.
*/
Blockly.Xml.blockToDom = function(block, opt_noId) {
+ // Skip over insertion markers.
+ if (block.isInsertionMarker()) {
+ var child = block.getChildren(false)[0];
+ if (child) {
+ return Blockly.Xml.blockToDom(child);
+ } else {
+ // Disappears when appended. Cast to ANY b/c DocumentFragment -> Element
+ // is invalid. We have to cast to ANY in between.
+ return /** @type{?} */ (new DocumentFragment());
+ }
+ }
+
+
var element =
Blockly.utils.xml.createElement(block.isShadow() ? 'shadow' : 'block');
element.setAttribute('type', block.type);
@@ -186,8 +209,11 @@ Blockly.Xml.blockToDom = function(block, opt_noId) {
container.appendChild(Blockly.Xml.cloneShadow_(shadow, opt_noId));
}
if (childBlock) {
- container.appendChild(Blockly.Xml.blockToDom(childBlock, opt_noId));
- empty = false;
+ var elem = Blockly.Xml.blockToDom(childBlock, opt_noId);
+ if (elem.nodeType == Blockly.utils.dom.NodeType.ELEMENT_NODE) {
+ container.appendChild(elem);
+ empty = false;
+ }
}
}
container.setAttribute('name', input.name);
@@ -217,9 +243,12 @@ Blockly.Xml.blockToDom = function(block, opt_noId) {
var nextBlock = block.getNextBlock();
if (nextBlock) {
- var container = Blockly.utils.xml.createElement('next');
- container.appendChild(Blockly.Xml.blockToDom(nextBlock, opt_noId));
- element.appendChild(container);
+ var elem = Blockly.Xml.blockToDom(nextBlock, opt_noId);
+ if (elem.nodeType == Blockly.utils.dom.NodeType.ELEMENT_NODE) {
+ var container = Blockly.utils.xml.createElement('next');
+ container.appendChild(elem);
+ element.appendChild(container);
+ }
}
var shadow = block.nextConnection && block.nextConnection.getShadowDom();
if (shadow && (!nextBlock || !nextBlock.isShadow())) {
diff --git a/tests/mocha/insertion_marker_test.js b/tests/mocha/insertion_marker_test.js
index fd3a5bc6b..afe34f2c7 100644
--- a/tests/mocha/insertion_marker_test.js
+++ b/tests/mocha/insertion_marker_test.js
@@ -6,7 +6,7 @@
suite('InsertionMarkers', function() {
setup(function() {
- this.workspace = new Blockly.Workspace();
+ this.workspace = Blockly.inject('blocklyDiv', {});
Blockly.defineBlocksWithJsonArray([
{
"type": "stack_block",
@@ -200,4 +200,187 @@ suite('InsertionMarkers', function() {
this.assertGen(xml, 'stack[a];\n');
});
});
+ suite('Serialization', function() {
+ setup(function() {
+ this.assertXml = function(xml, expectXml) {
+ Blockly.Xml.domToWorkspace(xml, this.workspace);
+ var block = this.workspace.getBlockById('insertion');
+ block.setInsertionMarker(true);
+ var xml = Blockly.Xml.workspaceToDom(this.workspace);
+ Blockly.Xml.domToWorkspace(xml, this.workspace);
+ xml = Blockly.Xml.domToText(xml);
+ chai.assert.equal(xml, expectXml);
+ };
+ });
+ teardown(function() {
+ delete this.assertXml;
+ });
+ test('Marker Surrounds', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ // Note how the x and y are not 20, they are slightly lower and end-er
+ // because these are the coords of the wrapped block.
+ this.assertXml(xml,
+ '' +
+ '' +
+ '');
+ });
+ test('Marker Enclosed', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ this.assertXml(xml,
+ '' +
+ '' +
+ '');
+ });
+ test('Marker Enclosed and Surrounds', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ this.assertXml(xml,
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '');
+ });
+ test('Marker Prev', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ // Note how the y coord is not at 20, it is lower. This is because these
+ // are the coords of the next block.
+ this.assertXml(xml,
+ '' +
+ '' +
+ '');
+ });
+ test('Marker Next', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ this.assertXml(xml,
+ '' +
+ '' +
+ '');
+ });
+ test('Marker Middle of Stack', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ this.assertXml(xml,
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '');
+ });
+ test('Marker On Output', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ // Note how the x value is not at 20. This is because these are the coords
+ // of the wrapped block.
+ this.assertXml(xml,
+ '' +
+ '' +
+ '');
+ });
+ test('Marker On Input', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ this.assertXml(xml,
+ '' +
+ '' +
+ '');
+ });
+ test('Marker Middle of Row', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '');
+ this.assertXml(xml,
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '');
+ });
+ test('Marker Detatched', function() {
+ var xml = Blockly.Xml.textToDom(
+ '' +
+ ' ' +
+ ' ' +
+ '');
+ this.assertXml(xml,
+ '' +
+ '' +
+ '');
+ });
+ });
});