diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js
index 07bb92a79..f2d2fe374 100644
--- a/blockly_uncompressed.js
+++ b/blockly_uncompressed.js
@@ -109,6 +109,10 @@ goog.addDependency("../../core/renderers/measurables/inputs.js", ['Blockly.block
goog.addDependency("../../core/renderers/measurables/row_elements.js", ['Blockly.blockRendering.Field', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.Icon', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.JaggedEdge', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.SquareCorner'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object']);
goog.addDependency("../../core/renderers/measurables/rows.js", ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow'], ['Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.Types', 'Blockly.utils.object']);
goog.addDependency("../../core/renderers/measurables/types.js", ['Blockly.blockRendering.Types'], []);
+goog.addDependency("../../core/renderers/sample/constants.js", ['Blockly.sample.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object']);
+goog.addDependency("../../core/renderers/sample/drawer.js", ['Blockly.sample.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.utils.object', 'Blockly.sample.RenderInfo']);
+goog.addDependency("../../core/renderers/sample/info.js", ['Blockly.sample', 'Blockly.sample.RenderInfo'], ['Blockly.utils.object']);
+goog.addDependency("../../core/renderers/sample/renderer.js", ['Blockly.sample.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.utils.object', 'Blockly.sample.ConstantProvider', 'Blockly.sample.Drawer', 'Blockly.sample.RenderInfo']);
goog.addDependency("../../core/renderers/thrasos/info.js", ['Blockly.thrasos', 'Blockly.thrasos.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.object']);
goog.addDependency("../../core/renderers/thrasos/renderer.js", ['Blockly.thrasos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.thrasos.RenderInfo', 'Blockly.utils.object']);
goog.addDependency("../../core/renderers/zelos/constants.js", ['Blockly.zelos.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object', 'Blockly.utils.svgPaths']);
@@ -155,7 +159,7 @@ goog.addDependency("../../core/variables.js", ['Blockly.Variables'], ['Blockly.B
goog.addDependency("../../core/variables_dynamic.js", ['Blockly.VariablesDynamic'], ['Blockly.Variables', 'Blockly.Blocks', 'Blockly.Msg', 'Blockly.utils.xml', 'Blockly.VariableModel']);
goog.addDependency("../../core/warning.js", ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils.dom', 'Blockly.utils.object']);
goog.addDependency("../../core/widgetdiv.js", ['Blockly.WidgetDiv'], ['Blockly.utils.style']);
-goog.addDependency("../../core/workspace.js", ['Blockly.Workspace'], ['Blockly.Cursor', 'Blockly.MarkerCursor', 'Blockly.Events', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.utils', 'Blockly.utils.math', 'Blockly.VariableMap', 'Blockly.WorkspaceComment']);
+goog.addDependency("../../core/workspace.js", ['Blockly.Workspace'], ['Blockly.Cursor', 'Blockly.MarkerCursor', 'Blockly.Events', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.utils', 'Blockly.utils.math', 'Blockly.VariableMap']);
goog.addDependency("../../core/workspace_audio.js", ['Blockly.WorkspaceAudio'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.userAgent']);
goog.addDependency("../../core/workspace_comment.js", ['Blockly.WorkspaceComment'], ['Blockly.Events', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.xml']);
goog.addDependency("../../core/workspace_comment_render_svg.js", ['Blockly.WorkspaceCommentSvg.render'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']);
@@ -165,7 +169,7 @@ goog.addDependency("../../core/workspace_dragger.js", ['Blockly.WorkspaceDragger
goog.addDependency("../../core/workspace_events.js", ['Blockly.Events.FinishedLoading'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.object']);
goog.addDependency("../../core/workspace_svg.js", ['Blockly.WorkspaceSvg'], ['Blockly.blockRendering', 'Blockly.ConnectionDB', 'Blockly.constants', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.Msg', 'Blockly.Options', 'Blockly.TouchGesture', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Rect', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml']);
goog.addDependency("../../core/ws_comment_events.js", ['Blockly.Events.CommentBase', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml']);
-goog.addDependency("../../core/xml.js", ['Blockly.Xml'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.VarCreate', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.xml', 'Blockly.WorkspaceComment', 'Blockly.WorkspaceCommentSvg']);
+goog.addDependency("../../core/xml.js", ['Blockly.Xml'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.VarCreate', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.xml']);
goog.addDependency("../../core/zoom_controls.js", ['Blockly.ZoomControls'], ['Blockly.Css', 'Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.utils.dom']);
goog.addDependency("base.js", [], []);
diff --git a/core/renderers/sample/constants.js b/core/renderers/sample/constants.js
new file mode 100644
index 000000000..f2909fe1c
--- /dev/null
+++ b/core/renderers/sample/constants.js
@@ -0,0 +1,43 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2019 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview An object that provides constants for rendering blocks in the
+ * sample renderer.
+ */
+'use strict';
+
+goog.provide('Blockly.sample.ConstantProvider');
+
+goog.require('Blockly.blockRendering.ConstantProvider');
+goog.require('Blockly.utils.object');
+
+
+/**
+ * An object that provides constants for rendering blocks in the sample.
+ * @constructor
+ * @package
+ * @extends {Blockly.blockRendering.ConstantProvider}
+ */
+Blockly.sample.ConstantProvider = function() {
+ Blockly.sample.ConstantProvider.superClass_.constructor.call(this);
+};
+Blockly.utils.object.inherits(Blockly.sample.ConstantProvider,
+ Blockly.blockRendering.ConstantProvider);
diff --git a/core/renderers/sample/drawer.js b/core/renderers/sample/drawer.js
new file mode 100644
index 000000000..032d853e2
--- /dev/null
+++ b/core/renderers/sample/drawer.js
@@ -0,0 +1,47 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2019 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Sample rendering drawer.
+ */
+'use strict';
+
+goog.provide('Blockly.sample.Drawer');
+
+goog.require('Blockly.blockRendering.Drawer');
+goog.require('Blockly.utils.object');
+goog.require('Blockly.sample.RenderInfo');
+
+
+/**
+ * An object that draws a block based on the given rendering information.
+ * @param {!Blockly.BlockSvg} block The block to render.
+ * @param {!Blockly.sample.RenderInfo} info An object containing all
+ * information needed to render this block.
+ * @package
+ * @constructor
+ * @extends {Blockly.blockRendering.Drawer}
+ */
+Blockly.sample.Drawer = function(block, info) {
+ Blockly.sample.Drawer.superClass_.constructor.call(this, block, info);
+};
+Blockly.utils.object.inherits(Blockly.sample.Drawer,
+ Blockly.blockRendering.Drawer);
+
diff --git a/core/renderers/sample/info.js b/core/renderers/sample/info.js
new file mode 100644
index 000000000..3a2ca81a6
--- /dev/null
+++ b/core/renderers/sample/info.js
@@ -0,0 +1,59 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2019 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Sample render info object.
+ */
+'use strict';
+
+goog.provide('Blockly.sample');
+goog.provide('Blockly.sample.RenderInfo');
+
+goog.require('Blockly.utils.object');
+
+
+/**
+ * An object containing all sizing information needed to draw this block.
+ *
+ * This measure pass does not propagate changes to the block (although fields
+ * may choose to rerender when getSize() is called). However, calling it
+ * repeatedly may be expensive.
+ *
+ * @param {!Blockly.sample.Renderer} renderer The renderer in use.
+ * @param {!Blockly.BlockSvg} block The block to measure.
+ * @constructor
+ * @package
+ * @extends {Blockly.blockRendering.RenderInfo}
+ */
+Blockly.sample.RenderInfo = function(renderer, block) {
+ Blockly.sample.RenderInfo.superClass_.constructor.call(this, renderer, block);
+
+};
+Blockly.utils.object.inherits(Blockly.sample.RenderInfo,
+ Blockly.blockRendering.RenderInfo);
+
+/**
+ * Get the block renderer in use.
+ * @return {!Blockly.sample.Renderer} The block renderer in use.
+ * @package
+ */
+Blockly.sample.RenderInfo.prototype.getRenderer = function() {
+ return /** @type {!Blockly.sample.Renderer} */ (this.renderer_);
+};
diff --git a/core/renderers/sample/renderer.js b/core/renderers/sample/renderer.js
new file mode 100644
index 000000000..94ffba38e
--- /dev/null
+++ b/core/renderers/sample/renderer.js
@@ -0,0 +1,83 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2019 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Sample renderer.
+ */
+'use strict';
+
+goog.provide('Blockly.sample.Renderer');
+
+goog.require('Blockly.blockRendering');
+goog.require('Blockly.blockRendering.Renderer');
+goog.require('Blockly.utils.object');
+goog.require('Blockly.sample.ConstantProvider');
+goog.require('Blockly.sample.Drawer');
+goog.require('Blockly.sample.RenderInfo');
+
+
+/**
+ * The sample renderer.
+ * @package
+ * @constructor
+ * @extends {Blockly.blockRendering.Renderer}
+ */
+Blockly.sample.Renderer = function() {
+ Blockly.sample.Renderer.superClass_.constructor.call(this);
+};
+Blockly.utils.object.inherits(Blockly.sample.Renderer,
+ Blockly.blockRendering.Renderer);
+
+/**
+ * Create a new instance of the renderer's constant provider.
+ * @return {!Blockly.sample.ConstantProvider} The constant provider.
+ * @protected
+ * @override
+ */
+Blockly.sample.Renderer.prototype.makeConstants_ = function() {
+ return new Blockly.sample.ConstantProvider();
+};
+
+/**
+ * Create a new instance of the renderer's render info object.
+ * @param {!Blockly.BlockSvg} block The block to measure.
+ * @return {!Blockly.sample.RenderInfo} The render info object.
+ * @protected
+ * @override
+ */
+Blockly.sample.Renderer.prototype.makeRenderInfo_ = function(block) {
+ return new Blockly.sample.RenderInfo(this, block);
+};
+
+/**
+ * Create a new instance of the renderer's drawer.
+ * @param {!Blockly.BlockSvg} block The block to render.
+ * @param {!Blockly.blockRendering.RenderInfo} info An object containing all
+ * information needed to render this block.
+ * @return {!Blockly.sample.Drawer} The drawer.
+ * @protected
+ * @override
+ */
+Blockly.sample.Renderer.prototype.makeDrawer_ = function(block, info) {
+ return new Blockly.sample.Drawer(block,
+ /** @type {!Blockly.sample.RenderInfo} */ (info));
+};
+
+Blockly.blockRendering.register('sample', Blockly.sample.Renderer);
diff --git a/tests/jsunit/index.html b/tests/jsunit/index.html
index ab42cf45a..095593cb4 100644
--- a/tests/jsunit/index.html
+++ b/tests/jsunit/index.html
@@ -34,7 +34,8 @@
-
+
+
diff --git a/tests/playground.html b/tests/playground.html
index b1c4f5689..8c70d6c0a 100644
--- a/tests/playground.html
+++ b/tests/playground.html
@@ -69,6 +69,7 @@
@@ -566,6 +567,7 @@ var spaghettiXml = [
+