Skip to content

Commit

Permalink
Merge pull request #5075 from gonfunko/dom
Browse files Browse the repository at this point in the history
Migrate core/utils/dom.js to goog.module syntax
  • Loading branch information
gonfunko authored Jul 29, 2021
2 parents a827df0 + f0fc360 commit c9a0333
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 80 deletions.
177 changes: 98 additions & 79 deletions core/utils/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,37 @@
* @name Blockly.utils.dom
* @namespace
*/
goog.provide('Blockly.utils.dom');
goog.module('Blockly.utils.dom');
goog.module.declareLegacyNamespace();

goog.require('Blockly.utils.Svg');
goog.require('Blockly.utils.userAgent');
const Svg = goog.require('Blockly.utils.Svg');
const userAgent = goog.require('Blockly.utils.userAgent');


/**
* Required name space for SVG elements.
* @const
*/
Blockly.utils.dom.SVG_NS = 'http://www.w3.org/2000/svg';
const SVG_NS = 'http://www.w3.org/2000/svg';

/**
* Required name space for HTML elements.
* @const
*/
Blockly.utils.dom.HTML_NS = 'http://www.w3.org/1999/xhtml';
const HTML_NS = 'http://www.w3.org/1999/xhtml';

/**
* Required name space for XLINK elements.
* @const
*/
Blockly.utils.dom.XLINK_NS = 'http://www.w3.org/1999/xlink';
const XLINK_NS = 'http://www.w3.org/1999/xlink';

/**
* Node type constants.
* https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
* @enum {number}
*/
Blockly.utils.dom.NodeType = {
const NodeType = {
ELEMENT_NODE: 1,
TEXT_NODE: 3,
COMMENT_NODE: 8,
Expand All @@ -57,36 +58,35 @@ Blockly.utils.dom.NodeType = {
* @type {Object}
* @private
*/
Blockly.utils.dom.cacheWidths_ = null;
let cacheWidths = null;

/**
* Number of current references to cache.
* @type {number}
* @private
*/
Blockly.utils.dom.cacheReference_ = 0;
let cacheReference = 0;

/**
* A HTML canvas context used for computing text width.
* @type {CanvasRenderingContext2D}
* @private
*/
Blockly.utils.dom.canvasContext_ = null;
let canvasContext = null;

/**
* Helper method for creating SVG elements.
* @param {string|Blockly.utils.Svg<T>} name Element's tag name.
* @param {string|Svg<T>} name Element's tag name.
* @param {!Object} attrs Dictionary of attribute names and values.
* @param {Element=} opt_parent Optional parent on which to append the element.
* @return {T} Newly created SVG element. The return type is {!SVGElement} if
* name is a string or a more specific type if it a member of
* Blockly.utils.Svg
* name is a string or a more specific type if it a member of Svg.
* @template T
*/
Blockly.utils.dom.createSvgElement = function(name, attrs, opt_parent) {
var e = /** @type {T} */
(document.createElementNS(Blockly.utils.dom.SVG_NS, String(name)));
for (var key in attrs) {
const createSvgElement = function(name, attrs, opt_parent) {
const e = /** @type {T} */
(document.createElementNS(SVG_NS, String(name)));
for (const key in attrs) {
e.setAttribute(key, attrs[key]);
}
// IE defines a unique attribute "runtimeStyle", it is NOT applied to
Expand All @@ -108,8 +108,8 @@ Blockly.utils.dom.createSvgElement = function(name, attrs, opt_parent) {
* @param {string} className Name of class to add.
* @return {boolean} True if class was added, false if already present.
*/
Blockly.utils.dom.addClass = function(element, className) {
var classes = element.getAttribute('class') || '';
const addClass = function(element, className) {
let classes = element.getAttribute('class') || '';
if ((' ' + classes + ' ').indexOf(' ' + className + ' ') != -1) {
return false;
}
Expand All @@ -126,11 +126,10 @@ Blockly.utils.dom.addClass = function(element, className) {
* @param {string} classNames A string of one or multiple class names for an
* element.
*/
Blockly.utils.dom.removeClasses = function(element, classNames) {
var classList = classNames.split(' ');
for (var i = 0; i < classList.length; i++) {
var cssName = classList[i];
Blockly.utils.dom.removeClass(element, cssName);
const removeClasses = function(element, classNames) {
const classList = classNames.split(' ');
for (let i = 0; i < classList.length; i++) {
removeClass(element, classList[i]);
}
};

Expand All @@ -141,13 +140,13 @@ Blockly.utils.dom.removeClasses = function(element, classNames) {
* @param {string} className Name of class to remove.
* @return {boolean} True if class was removed, false if never present.
*/
Blockly.utils.dom.removeClass = function(element, className) {
var classes = element.getAttribute('class');
const removeClass = function(element, className) {
const classes = element.getAttribute('class');
if ((' ' + classes + ' ').indexOf(' ' + className + ' ') == -1) {
return false;
}
var classList = classes.split(/\s+/);
for (var i = 0; i < classList.length; i++) {
const classList = classes.split(/\s+/);
for (let i = 0; i < classList.length; i++) {
if (!classList[i] || classList[i] == className) {
classList.splice(i, 1);
i--;
Expand All @@ -168,8 +167,8 @@ Blockly.utils.dom.removeClass = function(element, className) {
* @param {string} className Name of class to check.
* @return {boolean} True if class exists, false otherwise.
*/
Blockly.utils.dom.hasClass = function(element, className) {
var classes = element.getAttribute('class');
const hasClass = function(element, className) {
const classes = element.getAttribute('class');
return (' ' + classes + ' ').indexOf(' ' + className + ' ') != -1;
};

Expand All @@ -179,7 +178,7 @@ Blockly.utils.dom.hasClass = function(element, className) {
* @return {?Node} The node removed if removed; else, null.
*/
// Copied from Closure goog.dom.removeNode
Blockly.utils.dom.removeNode = function(node) {
const removeNode = function(node) {
return node && node.parentNode ? node.parentNode.removeChild(node) : null;
};

Expand All @@ -189,9 +188,9 @@ Blockly.utils.dom.removeNode = function(node) {
* @param {!Element} newNode New element to insert.
* @param {!Element} refNode Existing element to precede new node.
*/
Blockly.utils.dom.insertAfter = function(newNode, refNode) {
var siblingNode = refNode.nextSibling;
var parentNode = refNode.parentNode;
const insertAfter = function(newNode, refNode) {
const siblingNode = refNode.nextSibling;
const parentNode = refNode.parentNode;
if (!parentNode) {
throw Error('Reference node has no parent.');
}
Expand All @@ -208,9 +207,10 @@ Blockly.utils.dom.insertAfter = function(newNode, refNode) {
* @param {!Node} descendant The node to test presence of.
* @return {boolean} Whether the parent node contains the descendant node.
*/
Blockly.utils.dom.containsNode = function(parent, descendant) {
return !!(parent.compareDocumentPosition(descendant) &
Blockly.utils.dom.NodeType.DOCUMENT_POSITION_CONTAINED_BY);
const containsNode = function(parent, descendant) {
return !!(
parent.compareDocumentPosition(descendant) &
NodeType.DOCUMENT_POSITION_CONTAINED_BY);
};

/**
Expand All @@ -220,7 +220,7 @@ Blockly.utils.dom.containsNode = function(parent, descendant) {
* @param {!Element} element Element to which the CSS transform will be applied.
* @param {string} transform The value of the CSS `transform` property.
*/
Blockly.utils.dom.setCssTransform = function(element, transform) {
const setCssTransform = function(element, transform) {
element.style['transform'] = transform;
element.style['-webkit-transform'] = transform;
};
Expand All @@ -229,21 +229,21 @@ Blockly.utils.dom.setCssTransform = function(element, transform) {
* Start caching text widths. Every call to this function MUST also call
* stopTextWidthCache. Caches must not survive between execution threads.
*/
Blockly.utils.dom.startTextWidthCache = function() {
Blockly.utils.dom.cacheReference_++;
if (!Blockly.utils.dom.cacheWidths_) {
Blockly.utils.dom.cacheWidths_ = Object.create(null);
const startTextWidthCache = function() {
cacheReference++;
if (!cacheWidths) {
cacheWidths = Object.create(null);
}
};

/**
* Stop caching field widths. Unless caching was already on when the
* corresponding call to startTextWidthCache was made.
*/
Blockly.utils.dom.stopTextWidthCache = function() {
Blockly.utils.dom.cacheReference_--;
if (!Blockly.utils.dom.cacheReference_) {
Blockly.utils.dom.cacheWidths_ = null;
const stopTextWidthCache = function() {
cacheReference--;
if (!cacheReference) {
cacheWidths = null;
}
};

Expand All @@ -252,21 +252,21 @@ Blockly.utils.dom.stopTextWidthCache = function() {
* @param {!Element} textElement An SVG 'text' element.
* @return {number} Width of element.
*/
Blockly.utils.dom.getTextWidth = function(textElement) {
var key = textElement.textContent + '\n' + textElement.className.baseVal;
var width;
const getTextWidth = function(textElement) {
const key = textElement.textContent + '\n' + textElement.className.baseVal;
let width;

// Return the cached width if it exists.
if (Blockly.utils.dom.cacheWidths_) {
width = Blockly.utils.dom.cacheWidths_[key];
if (cacheWidths) {
width = cacheWidths[key];
if (width) {
return width;
}
}

// Attempt to compute fetch the width of the SVG text element.
try {
if (Blockly.utils.userAgent.IE || Blockly.utils.userAgent.EDGE) {
if (userAgent.IE || userAgent.EDGE) {
width = textElement.getBBox().width;
} else {
width = textElement.getComputedTextLength();
Expand All @@ -280,8 +280,8 @@ Blockly.utils.dom.getTextWidth = function(textElement) {
}

// Cache the computed width and return.
if (Blockly.utils.dom.cacheWidths_) {
Blockly.utils.dom.cacheWidths_[key] = width;
if (cacheWidths) {
cacheWidths[key] = width;
}
return width;
};
Expand All @@ -296,10 +296,10 @@ Blockly.utils.dom.getTextWidth = function(textElement) {
* @param {string} fontFamily The font family to use.
* @return {number} Width of element.
*/
Blockly.utils.dom.getFastTextWidth = function(textElement,
fontSize, fontWeight, fontFamily) {
return Blockly.utils.dom.getFastTextWidthWithSizeString(textElement,
fontSize + 'pt', fontWeight, fontFamily);
const getFastTextWidth = function(
textElement, fontSize, fontWeight, fontFamily) {
return getFastTextWidthWithSizeString(
textElement, fontSize + 'pt', fontWeight, fontFamily);
};

/**
Expand All @@ -314,41 +314,40 @@ Blockly.utils.dom.getFastTextWidth = function(textElement,
* @param {string} fontFamily The font family to use.
* @return {number} Width of element.
*/
Blockly.utils.dom.getFastTextWidthWithSizeString = function(textElement,
fontSize, fontWeight, fontFamily) {
var text = textElement.textContent;
var key = text + '\n' + textElement.className.baseVal;
var width;
const getFastTextWidthWithSizeString = function(
textElement, fontSize, fontWeight, fontFamily) {
const text = textElement.textContent;
const key = text + '\n' + textElement.className.baseVal;
let width;

// Return the cached width if it exists.
if (Blockly.utils.dom.cacheWidths_) {
width = Blockly.utils.dom.cacheWidths_[key];
if (cacheWidths) {
width = cacheWidths[key];
if (width) {
return width;
}
}

if (!Blockly.utils.dom.canvasContext_) {
if (!canvasContext) {
// Inject the canvas element used for computing text widths.
var computeCanvas = document.createElement('canvas');
const computeCanvas = document.createElement('canvas');
computeCanvas.className = 'blocklyComputeCanvas';
document.body.appendChild(computeCanvas);

// Initialize the HTML canvas context and set the font.
// The context font must match blocklyText's fontsize and font-family
// set in CSS.
Blockly.utils.dom.canvasContext_ = computeCanvas.getContext('2d');
canvasContext = computeCanvas.getContext('2d');
}
// Set the desired font size and family.
Blockly.utils.dom.canvasContext_.font =
fontWeight + ' ' + fontSize + ' ' + fontFamily;
canvasContext.font = fontWeight + ' ' + fontSize + ' ' + fontFamily;

// Measure the text width using the helper canvas context.
width = Blockly.utils.dom.canvasContext_.measureText(text).width;
width = canvasContext.measureText(text).width;

// Cache the computed width and return.
if (Blockly.utils.dom.cacheWidths_) {
Blockly.utils.dom.cacheWidths_[key] = width;
if (cacheWidths) {
cacheWidths[key] = width;
}
return width;
};
Expand All @@ -361,18 +360,16 @@ Blockly.utils.dom.getFastTextWidthWithSizeString = function(textElement,
* @param {string} fontFamily The font family to use.
* @return {{height: number, baseline: number}} Font measurements.
*/
Blockly.utils.dom.measureFontMetrics = function(text, fontSize, fontWeight,
fontFamily) {

var span = document.createElement('span');
const measureFontMetrics = function(text, fontSize, fontWeight, fontFamily) {
const span = document.createElement('span');
span.style.font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
span.textContent = text;

var block = document.createElement('div');
const block = document.createElement('div');
block.style.width = '1px';
block.style.height = 0;

var div = document.createElement('div');
const div = document.createElement('div');
div.setAttribute('style', 'position: fixed; top: 0; left: 0; display: flex;');
div.appendChild(span);
div.appendChild(block);
Expand All @@ -389,3 +386,25 @@ Blockly.utils.dom.measureFontMetrics = function(text, fontSize, fontWeight,
}
return result;
};

exports = {
SVG_NS,
HTML_NS,
XLINK_NS,
NodeType,
createSvgElement,
addClass,
removeClasses,
removeClass,
hasClass,
removeNode,
insertAfter,
containsNode,
setCssTransform,
startTextWidthCache,
stopTextWidthCache,
getTextWidth,
getFastTextWidth,
getFastTextWidthWithSizeString,
measureFontMetrics,
};
2 changes: 1 addition & 1 deletion tests/deps.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c9a0333

Please sign in to comment.