From d55d9cbd9ff308ff5e361cf77acabb61a4bf4695 Mon Sep 17 00:00:00 2001 From: Tim Dawborn Date: Fri, 27 Jan 2017 15:15:09 +1100 Subject: [PATCH] Attempt to work around the IE/Edge bug where `getComputedTextLength()` throws an exception when the SVG node is not visible. This workaround forces a re-render, which in turn, forces a re-calculation of the node width once a block is inserted into the workspace SVG. This workaround is only executed on IE and Edge. See https://groups.google.com/forum/#!topic/blockly/T8IR4t4xAIY for the initial discussion of this issue. --- core/block_render_svg.js | 9 ++++++++ core/field.js | 47 +++++++++++++++++++++++++++++----------- core/xml.js | 6 +++++ 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/core/block_render_svg.js b/core/block_render_svg.js index ba99c6594..adc3ca055 100644 --- a/core/block_render_svg.js +++ b/core/block_render_svg.js @@ -29,6 +29,8 @@ goog.provide('Blockly.BlockSvg.render'); goog.require('Blockly.BlockSvg'); +goog.require('goog.userAgent'); + // UI constants for rendering blocks. /** @@ -314,6 +316,13 @@ Blockly.BlockSvg.prototype.renderFields_ = if (!root) { continue; } + + // Force a width re-calculation on IE and Edge to get around the issue + // described in Blockly.Field.getCachedWidth + if (goog.userAgent.IE || goog.userAgent.EDGE) { + field.updateWidth(); + } + if (this.RTL) { cursorX -= field.renderSep + field.renderWidth; root.setAttribute('transform', diff --git a/core/field.js b/core/field.js index c4596627d..d89c9c779 100644 --- a/core/field.js +++ b/core/field.js @@ -295,6 +295,15 @@ Blockly.Field.prototype.render_ = function() { var textNode = document.createTextNode(this.getDisplayText_()); this.textElement_.appendChild(textNode); + this.updateWidth(); +}; + +/** + * Updates thw width of the field. This calls getCachedWidth which won't cache + * the approximated width on IE/Edge when `getComputedTextLength` fails. Once + * it eventually does succeed, the result will be cached. + **/ +Blockly.Field.prototype.updateWidth = function() { var width = Blockly.Field.getCachedWidth(this.textElement_); if (this.borderRect_) { this.borderRect_.setAttribute('width', @@ -306,24 +315,36 @@ Blockly.Field.prototype.render_ = function() { /** * Gets the width of a text element, caching it in the process. * @param {!Element} textElement An SVG 'text' element. - * @retur {number} Width of element. + * @return {number} Width of element. */ Blockly.Field.getCachedWidth = function(textElement) { var key = textElement.textContent + '\n' + textElement.className.baseVal; - if (Blockly.Field.cacheWidths_ && Blockly.Field.cacheWidths_[key]) { - var width = Blockly.Field.cacheWidths_[key]; - } else { - try { - var width = textElement.getComputedTextLength(); - } catch (e) { - // MSIE 11 is known to throw "Unexpected call to method or property - // access." if Blockly is hidden. - var width = textElement.textContent.length * 8; - } - if (Blockly.Field.cacheWidths_) { - Blockly.Field.cacheWidths_[key] = width; + var width; + + // Return the cached width if it exists. + if (Blockly.Field.cacheWidths_) { + width = Blockly.Field.cacheWidths_[key]; + if (width) { + return width; } } + + // Attempt to compute fetch the width of the SVG text element. + try { + width = textElement.getComputedTextLength(); + } catch (e) { + // MSIE 11 and Edge are known to throw "Unexpected call to method or + // property access." if the block is hidden. Instead, use an + // approximation and do not cache the result. At some later point in time + // when the block is inserted into the visible DOM, this method will be + // called again and, at that point in time, will not throw an exception. + return textElement.textContent.length * 8; + } + + // Cache the computed width and return. + if (Blockly.Field.cacheWidths_) { + Blockly.Field.cacheWidths_[key] = width; + } return width; }; diff --git a/core/xml.js b/core/xml.js index ecfbe1ed8..4911218d3 100644 --- a/core/xml.js +++ b/core/xml.js @@ -28,6 +28,7 @@ goog.provide('Blockly.Xml'); goog.require('goog.asserts'); goog.require('goog.dom'); +goog.require('goog.userAgent'); /** @@ -367,6 +368,11 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) { setTimeout(function() { if (topBlock.workspace) { // Check that the block hasn't been deleted. topBlock.setConnectionsHidden(false); + // Force a render on IE and Edge to get around the issue described in + // Blockly.Field.getCachedWidth + if (goog.userAgent.IE || goog.userAgent.EDGE) { + topBlock.render(); + } } }, 1); topBlock.updateDisabled();