diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js index 373e3e42f9..19fe551290 100644 --- a/blocks_vertical/procedures.js +++ b/blocks_vertical/procedures.js @@ -97,11 +97,17 @@ Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation = function(xmlEleme this.procCode_ = xmlElement.getAttribute('proccode'); this.warp_ = JSON.parse(xmlElement.getAttribute('warp')); + var prevArgIds = this.argumentIds_; + var prevDisplayNames = this.displayNames_; + this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')); this.displayNames_ = JSON.parse(xmlElement.getAttribute('argumentnames')); this.argumentDefaults_ = JSON.parse( xmlElement.getAttribute('argumentdefaults')); this.updateDisplay_(); + if (this.updateArgumentReporterNames_) { + this.updateArgumentReporterNames_(prevArgIds, prevDisplayNames); + } }; // End of serialization and deserialization. @@ -708,6 +714,62 @@ Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ = function( } }; +/** + * Update argument reporter field values after an edit to the prototype mutation + * using previous argument ids and names. + * Because the argument reporters only store names and not which argument ids they + * are linked to, it would not be safe to update all argument reporters on the workspace + * since they may be argument reporters with the same name from a different procedure. + * Until there is a more explicit way of identifying argument reporter blocks using ids, + * be conservative and only update argument reporters that are used in the + * stack below the prototype, ie the definition. + * @param {!Array} prevArgIds The previous ordering of argument ids. + * @param {!Array} prevDisplayNames The previous argument names. + * @this Blockly.Block + */ +Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_ = function(prevArgIds, prevDisplayNames) { + var nameChanges = []; + var argReporters = []; + var definitionBlock = this.getParent(); + if (!definitionBlock) return; + + // Create a list of argument reporters that are descendants of the definition stack (see above comment) + var allBlocks = definitionBlock.getDescendants(false); + for (var i = 0; i < allBlocks.length; i++) { + var block = allBlocks[i]; + if ((block.type === 'argument_reporter_string_number' || + block.type === 'argument_reporter_boolean') && + !block.isShadow()) { // Exclude arg reporters in the prototype block, which are shadows. + argReporters.push(block); + } + } + + // Create a list of "name changes", including the new name and blocks matching the old name + // Only search over the current set of argument ids, ignore args that have been removed + for (var i = 0, id; id = this.argumentIds_[i]; i++) { + // Find the previous index of this argument id. Could be -1 if it is newly added. + var prevIndex = prevArgIds.indexOf(id); + if (prevIndex == -1) continue; // Newly added argument, no corresponding previous argument to update. + var prevName = prevDisplayNames[prevIndex]; + if (prevName != this.displayNames_[i]) { + nameChanges.push({ + newName: this.displayNames_[i], + blocks: argReporters.filter(function(block) { + return block.getFieldValue('VALUE') == prevName; + }) + }); + } + } + + // Finally update the blocks for each name change. + // Do this after creating the lists to avoid cycles of renaming. + for (var j = 0, nameChange; nameChange = nameChanges[j]; j++) { + for (var k = 0, block; block = nameChange.blocks[k]; k++) { + block.setFieldValue(nameChange.newName, 'VALUE'); + } + } +}; + Blockly.Blocks['procedures_definition'] = { /** * Block for defining a procedure with no return value. @@ -792,7 +854,8 @@ Blockly.Blocks['procedures_prototype'] = { addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelField_, // Only exists on procedures_prototype. - createArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_ + createArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_, + updateArgumentReporterNames_: Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_ }; Blockly.Blocks['procedures_declaration'] = { diff --git a/core/block_events.js b/core/block_events.js index 9f7e0b4073..31b8b8a38b 100644 --- a/core/block_events.js +++ b/core/block_events.js @@ -538,13 +538,14 @@ Blockly.Events.Move.prototype.run = function(forward) { * @extends {Blockly.Events.BlockBase} * @constructor */ -Blockly.Events.HintClick = function(hint) { +Blockly.Events.HintClick = function(hint, interactionType) { if (!hint) { return; // Blank event to be populated by fromJson. } Blockly.Events.HintClick.superClass_.constructor.call(this); - this.hintId = hint.getText(); + this.hintId = hint.getText()||hint.getId(); this.recordUndo = false; + this.interactionType = interactionType; }; goog.inherits(Blockly.Events.HintClick, Blockly.Events.Abstract); diff --git a/core/block_svg.js b/core/block_svg.js index 6aa2ecfe44..cb794e477c 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -222,9 +222,34 @@ Blockly.BlockSvg.prototype.unselect = function() { * Glow only this particular block, to highlight it visually as if it's running. * @param {boolean} isGlowingBlock Whether the block should glow. */ -Blockly.BlockSvg.prototype.setGlowBlock = function(isGlowingBlock) { +Blockly.BlockSvg.prototype.setGlowBlock = function(isGlowingBlock, refactor=false) { this.isGlowingBlock_ = isGlowingBlock; - this.updateColour(); + + if( refactor === false ){ + this.updateColour(); + } + else{ + // Update the applied SVG filter if the property has changed + var svg = this.getSvgRoot(); + if (this.isGlowingBlock_ && !svg.hasAttribute('filter')) { + svg.setAttribute('filter', 'url(#blocklyMessyBlockGlowFilter)'); + } else if (!this.isGlowingBlock_ && svg.hasAttribute('filter')) { + svg.removeAttribute('filter'); + } + } +} + +/** + * Set whether the block is highlighted or not. + * @param {boolean} highlighted True if highlighted. + */ +Blockly.BlockSvg.prototype.setHighlighted = function(highlighted) { + var svg = this.getSvgRoot(); + if (highlighted && !svg.hasAttribute('filter')) { + svg.setAttribute('filter', 'url(#blocklyMessyBlockGlowFilter)'); + } else if (svg.hasAttribute('filter')) { + svg.removeAttribute('filter'); + } }; /** @@ -391,6 +416,35 @@ Blockly.BlockSvg.prototype.moveBy = function(dx, dy) { this.workspace.resizeContents(); }; +/** + * Move a block along with its connected blocks and attach it to given parent + * @param {Blockly.BlockSvg} parent + */ +Blockly.BlockSvg.prototype.attachToParent = function(parent) { + goog.asserts.assert(parent, 'Parent not provided'); + this.parentBlock_=null; + this.workspace.addTopBlock(this); + this.setParent(parent); +}; + + +/** + * Connect block to given block + * Note: any block below this block will be disconnected + * @param {Blockly.BlockSvg} block + */ +Blockly.BlockSvg.prototype.connectToBlock = function(block) { + var previousConn = this.nextConnection; + if(previousConn.targetConnection) previousConn.disconnect(); + var nextConn = block.previousConnection; + if(nextConn && nextConn.targetConnection) nextConn.disconnect(); + if (previousConn.checkType_(nextConn)) { + // Attach the given block to this block. + previousConn.connect(nextConn); + } +}; + + /** * Transforms a block by setting the translation on the transform attribute * of the block's SVG. diff --git a/core/block_transformer.js b/core/block_transformer.js index dfc0b186ff..a76e60ab21 100644 --- a/core/block_transformer.js +++ b/core/block_transformer.js @@ -19,19 +19,21 @@ Blockly.BlockTransformer.prototype.doTransform = function (refactorable) { }; Blockly.BlockTransformer.prototype.executeAction = function (action) { + let result = true; try { - this.apply(action); + result = this.apply(action); + Blockly.Events.fireNow_(); } catch (err) { throw "failed to apply transformation:" + JSON.stringify(action) + "\n" + err.message; } - return true; + return result; }; Blockly.BlockTransformer.prototype.apply = function (action) { const actionType = action.type; - this[actionType](action); + return this[actionType](action); }; Blockly.BlockTransformer.prototype.VarDeclareAction = function (action) { @@ -39,7 +41,8 @@ Blockly.BlockTransformer.prototype.VarDeclareAction = function (action) { type: "var_create", varType: "", varName: action.name, - varId: action.id + varId: action.id, + isLocal: action.target==='_stage_'?false:true }; let varCreateEvent = new Blockly.Events.VarCreate(null); @@ -49,10 +52,10 @@ Blockly.BlockTransformer.prototype.VarDeclareAction = function (action) { return true; } -Blockly.BlockTransformer.prototype.VarRename = function (action) { +Blockly.BlockTransformer.prototype.VarRenameAction = function (action) { let varRenameJson = { type: "var_rename", - varId: action.id, + varId: action.var_id, oldName: action.oldName, newName: action.newName }; @@ -67,7 +70,11 @@ Blockly.BlockTransformer.prototype.VarRename = function (action) { Blockly.BlockTransformer.prototype.BlockCreateAction = function (action) { let dom = Blockly.Xml.textToDom(action.block_xml).firstChild; let block = Blockly.Xml.domToBlock(dom, this.workspace); - return true; + if (block.type === "procedures_definition") { + return block; + // Blockly.Procedures.editProcedureCallback_(block); + } + return true }; Blockly.BlockTransformer.prototype.InsertBlockAction = function (action) { @@ -75,7 +82,7 @@ Blockly.BlockTransformer.prototype.InsertBlockAction = function (action) { try { let targetBlock = this.workspace.getBlockById(action.target_block); let insertedBlock = this.workspace.getBlockById(action.inserted_block); - let previousBlock = targetBlock.previousConnection.targetBlock(); + let previousBlock = targetBlock.previousConnection?targetBlock.previousConnection.targetBlock():null; let moveEventJsonSpec = null; @@ -206,5 +213,21 @@ Blockly.BlockTransformer.prototype.ReplaceAction = function (action) { }; +Blockly.BlockTransformer.prototype.VarDeleteAction = function (action) { + let varDeleteJson = { + type: "var_delete", + varId: action.var_id, + // var_name: action.var_name, + // target: action.target + }; + + let varDeleteEvent = new Blockly.Events.VarDelete(null); + varDeleteEvent.id = action.var_id; + varDeleteEvent.fromJson(varDeleteJson); + varDeleteEvent.workspaceId = this.workspace.id; + varDeleteEvent.run(true); + return true; +} + // Blockly.Workspace.prototype.createVariable // Blockly.Workspace.prototype.deleteVariableById \ No newline at end of file diff --git a/core/colours.js b/core/colours.js index 09f8ce51fd..2ddd588bef 100644 --- a/core/colours.js +++ b/core/colours.js @@ -99,6 +99,7 @@ Blockly.Colours = { "stackGlowSize": 4, "stackGlowOpacity": 1, "replacementGlow": "#FFFFFF", + "messyBlockGlow": "#ff0000", "replacementGlowSize": 2, "replacementGlowOpacity": 1, "colourPickerStroke": "#FFFFFF", diff --git a/core/css.js b/core/css.js index a630879938..95e5eaa80c 100644 --- a/core/css.js +++ b/core/css.js @@ -1033,7 +1033,7 @@ Blockly.Css.CONTENT = [ 'outline: none;', 'padding: 4px 0;', 'position: absolute;', - 'overflow-y: auto;', + 'overflow-y: hidden;', 'overflow-x: hidden;', 'z-index: 20000;', /* Arbitrary, but some apps depend on it... */ '}', diff --git a/core/flyout_base.js b/core/flyout_base.js index fe5b3fcd4e..1a70ad2bae 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -146,6 +146,14 @@ Blockly.Flyout = function(workspaceOptions) { */ this.recycleBlocks_ = []; + /** + * List of current highlight boxes drawn in flyout. Highlight boxes are often used to + * visually mark certain group of variables. + * @type !Array. + * @private + */ + this.highlightBoxs_ = []; + }; /** @@ -921,3 +929,39 @@ Blockly.Flyout.prototype.recycleBlock_ = function(block) { block.moveBy(-xy.x, -xy.y); this.recycleBlocks_.push(block); }; + +/** + * Display highlighting box for given variables in the workspace. + * @param {array} ids IDs of variables to find. + */ +Blockly.Flyout.prototype.drawHighlightBox = function(ids) { + for ( var i=0; i< ids.length; i++ ) { + var id = ids[i]; + var block = null; + var height = 0; + if (id) { + block = this.workspace_.getBlockById(id); + if (!block) { + throw 'Tried to highlight variable that does not exist.'; + } + } + height = block.getHeightWidth().height; + this.highlightBoxs_.push(Blockly.utils.createSvgElement('rect', + {'height': height * this.workspace_.scale, + 'width' : (block.getHeightWidth().width+10) * this.workspace_.scale, + 'style' : "fill: none;stroke-width:5;stroke:rgb(255,0,0);", + 'x' : (block.getBoundingRectangle().topLeft.x-5) * this.workspace_.scale, + 'y' : (block.getBoundingRectangle().topLeft.y-this.getScrollPos()) * this.workspace_.scale + }, + this.svgGroup_)); + } +}; + +/** + * Remove all highlighting boxes for given variables in the workspace. + */ +Blockly.Flyout.prototype.removeHighlightBox = function() { + for ( var i=0; i + * @private + */ + this.highlightBoxs_ = []; + /** * Object in charge of loading, storing, and playing audio for a workspace. * @type {Blockly.WorkspaceAudio} @@ -700,6 +708,10 @@ Blockly.WorkspaceSvg.prototype.resize = function() { if (this.scrollbar) { this.scrollbar.resize(); } + if (this.workspaceHint_) { + this.workspaceHint_.position(); + } + this.updateScreenCalculations_(); }; @@ -954,6 +966,125 @@ Blockly.WorkspaceSvg.prototype.glowBlock = function(id, isGlowingBlock) { block.setGlowBlock(isGlowingBlock); }; +/** ++ * Glow/unglow a messy block in the workspace. ++ * @param {array} ids IDs of block to find. ++ * @param {boolean} isGlowingBlock Whether to glow the block. ++ */ +Blockly.WorkspaceSvg.prototype.glowMessyBlock = function(ids, isMessyGlowingBlock) { + + for ( var i=0; i< ids.length; i++ ) { + var id = ids[i]; + var block = null; + if (id) { + block = this.getBlockById(id); + if (!block) { + throw 'Tried to glow block that does not exist.'; + } + } + if(!isMessyGlowingBlock){ + Blockly.DropDownDiv.hideWithoutAnimation(); + } + block.setGlowBlock(isMessyGlowingBlock, true); + } +}; + + +/** + * Display highlighting box for given block/blocks in the workspace. + * @param {?string} start id ID of block to find. + * @param {?string} (optional) end id ID of block to find. + */ +Blockly.WorkspaceSvg.prototype.drawHighlightBox = function (id, id2 = null, options) { + var svg = null; + var block1 = null, block2 = null; + + if (id) { + block1 = this.getBlockById(id); + if (block1.getFirstStatementConnection()&&!block2) { + svg = Blockly.utils.createSvgElement('path', + { + 'd': Blockly.utils.getBoundingPathForControlBlock(block1), + 'class': 'blocklyBlockBackground', + 'fill': 'black', + 'fill-opacity': '0', + 'stroke': options?options.color:'lightgreen', + 'stroke-width': options?'7px':'1px' + }, + block1.getSvgRoot()); + if(!options){ + svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); + } + this.highlightBoxs_.push(svg); + return; + } + if (!block1) { + throw 'Tried to highlight block that does not exist.'; + } + } + if (id2) { + block2 = this.getBlockById(id2); + if (!block2) { + throw 'Tried to highlight block that does not exist.'; + } + } + + svg = Blockly.utils.createSvgElement('path', + { + 'd': Blockly.utils.getBoundingPath(block1, block2), + 'class': 'blocklyBlockBackground', + 'fill': 'black', + 'fill-opacity': '0', + 'stroke': 'lightgreen', + 'stroke-width': options?'7px':'1px' + }, + block1.getSvgRoot()); + if(!options){ + svg.setAttribute('filter', 'url(#' + 'blocklyFocusBlocksGlowFilter' + ')'); + } + + this.highlightBoxs_.push(svg); + +}; + + +/** + * Remove all highlighting boxes for given block/blocks in the workspace. + */ +Blockly.WorkspaceSvg.prototype.removeHighlightBox = function () { + for (var i = 0; i < this.highlightBoxs_.length; i++) { + if(this.highlightBoxs_[i].nodeName==='g'){ + //just remove the effect if it's the block rootSvg + this.highlightBoxs_[i].removeAttribute('filter'); + }else{ + // remove the path element itself + this.highlightBoxs_[i].remove(); + } + } +}; + + +Blockly.WorkspaceSvg.prototype.highlightField = function (shadowId) { + var shadowBlock = null; + if(!shadowBlock){ + shadowBlock = this.getBlockById(shadowId); + var fieldPath = shadowBlock.getSvgRoot().querySelector('path'); + var highlightFieldStyle = document.createAttribute("style"); + highlightFieldStyle.value = "stroke: limegreen; stroke-width: 5;"; + fieldPath.attributes.setNamedItem(highlightFieldStyle); + } +}; + +Blockly.WorkspaceSvg.prototype.unHighlightField = function (shadowId) { + var shadowBlock = null; + if(!shadowBlock){ + shadowBlock = this.getBlockById(shadowId); + var fieldPath = shadowBlock.getSvgRoot().querySelector('path'); + fieldPath.attributes.removeNamedItem('style'); + } +}; + + /** * Glow/unglow a stack in the workspace. * @param {?string} id ID of block which starts the stack. @@ -970,6 +1101,10 @@ Blockly.WorkspaceSvg.prototype.glowStack = function(id, isGlowingStack) { block.setGlowStack(isGlowingStack); }; +Blockly.WorkspaceSvg.prototype.reportSelectedBlock = function() { + return Blockly.selected; +}; + /** * Visually report a value associated with a block. * In Scratch, appears as a pop-up next to the block when a reporter block is clicked. @@ -995,6 +1130,131 @@ Blockly.WorkspaceSvg.prototype.reportValue = function(id, value) { Blockly.DropDownDiv.showPositionedByBlock(this, block); }; +/** + * To display the explanation for highlighted blocks + * Use "\n" for new line. Make sure to add spaces before and after "\n" + * Eg. explainHighlightBox("Line 1 \n Line 2") + * Will always display to the left of highlighted blocks + * @param {?string} value String value to visually report. + * + * + * + * + * + */ +Blockly.WorkspaceSvg.prototype.explainHighlightBox = function(value, toPointVariables=0, noPath=0) { + + var d = 'M 0 0 l -20 -20 l 0 40 z'; + var transform = 'translate(735,300)'; + var str = ""; + if(toPointVariables==1){ + d = 'M 0 0 l 20 -20 l 0 40 z'; + transform = 'translate(295,600)'; + } + + this.drawBox(500,400); + + + this.highlightBoxs_.push(Blockly.utils.createSvgElement('image', + {'href':'http://research.cs.vt.edu/quality4blocks/img/quality-hound.png', + 'height': 180, + 'width' : 180, + 'x' : 575, + 'y' : 10 + }, + this.highlightBoxs_[this.highlightBoxs_.length-4])); + + var y = 250; + var width = 350; + var lineHeight = 10; + + + if(noPath==0){ + this.highlightBoxs_.push(Blockly.utils.createSvgElement('path', + {'d': d, + 'transform':transform + }, + this.highlightBoxs_[this.highlightBoxs_.length-5])); + } + + /** + this.highlightBoxs_.push(Blockly.utils.createSvgElement('text', + {'style' : "fill: black;font-family: 'Helvetica Neue', Helvetica, sans-serif;font-weight: bold;font-size:2rem;", + 'x' : 340, + 'y' : 200 + }, + this.svgGroup_)); + var textNode1 = document.createTextNode("**sniff** **sniff**"); + this.highlightBoxs_[this.highlightBoxs_.length - 1].appendChild(textNode1); + **/ + + /* split the words into array */ + var words = value.split(' '); + var line = ''; + + for (var i = 0; i < words.length; i++) { + var testLine = line + words[i] + ' '; + var isNewLine = (words[i]=="\n"); + var isLastWord = (i==words.length-1); + if(testLine.length>=45 || isNewLine || isLastWord){ + this.highlightBoxs_.push(Blockly.utils.createSvgElement('text', + {'style' : "fill: black;font-family: 'Helvetica Neue', Helvetica, sans-serif;font-weight: bold;font-size:0.95rem;", + 'x' : 340, + 'y' : y + }, + this.svgGroup_)); + if(isLastWord) line = line + words[i]; + var textNode = document.createTextNode(line); + this.highlightBoxs_[this.highlightBoxs_.length - 1].appendChild(textNode); + line = (isNewLine)? '' : words[i] + ' '; + y = (isNewLine)? y+40 : y+20; + } else { + line = line + words[i] + ' '; + } + } + +}; + +/** + * To draw basic box for pop up with options g for adding buttons etc. + * Will always display to the left + * @param {?string} value String value to visually report. + * + * + * + * + */ +Blockly.WorkspaceSvg.prototype.drawBox = function(height, width){ + this.highlightBoxs_.push(Blockly.utils.createSvgElement('g', + { + }, + this.svgGroup_)); + + this.highlightBoxs_.push(Blockly.utils.createSvgElement('rect', + {'height': height, + 'width' : width, + 'rx' : 15, + 'ry' : 15, + 'style' : "fill: rgb(240,248,255);stroke-width:5;stroke:rgb(0,0,0);", + 'x' : 315, + 'y' : 150 + }, + this.highlightBoxs_[this.highlightBoxs_.length-1])); + + this.highlightBoxs_.push(Blockly.utils.createSvgElement('g', + {'id': 'options', + 'transform': 'translate(350,400)' + }, + this.highlightBoxs_[this.highlightBoxs_.length-2])); + + this.highlightBoxs_.push(Blockly.utils.createSvgElement('g', + {'id': 'options2', + 'transform': 'translate(320,200)' + }, + this.highlightBoxs_[this.highlightBoxs_.length-3])); +} + + /** * Paste the provided block onto the workspace. * @param {!Element} xmlBlock XML block element. diff --git a/tests/vertical_playground.html b/tests/vertical_playground.html index eb26d39024..c50b960dce 100644 --- a/tests/vertical_playground.html +++ b/tests/vertical_playground.html @@ -112,10 +112,25 @@ // Restore flyout event logging state. state = sessionStorage.getItem('logFlyoutEvents'); logFlyoutEvents(Boolean(state)); - showHintSetup(); + // showHintSetup(); + highlightBlocksSetup(); + // highlighFieldSetup(); + // svgSetup(); + // transformSetup(); } } + function svgSetup(){ + var svg = document.getElementById('testSvg'); //Get svg element + var newElement = document.createElementNS("http://www.w3.org/2000/svg", 'path'); //Create a path in SVG's namespace + newElement.setAttribute("d","M 0 0 L 10 10"); //Set path's data + newElement.setAttribute("id", "testPath"); + newElement.style.stroke = "#000"; //Set stroke colour + newElement.style.strokeWidth = "5px"; //Set stroke width + svg.appendChild(newElement); + + } + function getToolboxElement() { var match = location.search.match(/toolbox=([^&]+)/); return document.getElementById('toolbox-' + (match ? match[1] : 'categories')); @@ -308,18 +323,49 @@ } } - function showHintSetup(){ + function transformSetup(){ var input = document.getElementById('importExport'); - input.value = "10"; + input.value = + "900036810361610"; + fromXml(); + } + + function highlightBlocksSetup(){ + var input = document.getElementById('importExport'); + input.value = + // "1090101500" + //"101010_mouse_1010" + // "1010" + // "10_mouse_" + "15101010" + ; fromXml(); - Blockly.selected = workspace.getBlockById('*`#p/~R4kNb@uT|}iU@u'); - if (Blockly.selected) { - Blockly.selected.setHintText("abc"); - Blockly.selected.hint.setVisible(true); - } - - } +// Blockly.selected = workspace.getBlockById('*`#p/~R4kNb@uT|}iU@u'); +// if (Blockly.selected) { +// Blockly.selected.setHintText("abc"); +// Blockly.selected.hint.setVisible(true); +// } + // workspace.drawHighlightBox('`!ttdW7uQ~+?pJ#xb@n`','1TQo0bqgwh%R+%QOA50o'); + workspace.drawHighlightBox('Dy;gx(37]2W3a!e[wJQY','|e^K(I@{c_CD)8fhn%+`') + // workspace.drawHighlightBox('target',null,{color:'#3C91E6'}) + } + + function highlighFieldSetup(){ + var input = document.getElementById('importExport'); + input.value = + "COLOR185" + ; + fromXml(); + const shadowId = "qPI5(v*?$FK4-a6xxFY2"; + var block = workspace.getBlockById("N$(lwZyb{)yHn|!6*sDM"); + window.block = block; + + // highlight + workspace.highlightField('qPI5(v*?$FK4-a6xxFY2'); + // workspace.unHighlightField('qPI5(v*?$FK4-a6xxFY2'); + + } function setLocale(locale) { workspace.getFlyout().setRecyclingEnabled(false); @@ -600,5 +646,6 @@

Vertical Blocks

+ diff --git a/tests/vertical_playground_compact_xml.html b/tests/vertical_playground_compact_xml.html new file mode 100644 index 0000000000..e2f2261a43 --- /dev/null +++ b/tests/vertical_playground_compact_xml.html @@ -0,0 +1,613 @@ + + + + + + + Vertical Playground + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +

Vertical Blocks

+

+ Show + - Hide +

+ +
+ + + + +
+ +

+ +   + +
+ +
+ +

+ +
+ +

+ Log events:   + +

+ +

+ Stress test:   + + +

+ +

+ Glows:   + + + + +

+ +

+ Log flyout events:   + +

+ +

+ Enable sounds (after refresh):   + +

+ +

+ + +

+ +

+ Report: + + +

+ +