tupali/librerias/gantt/code/es-modules/parts/Html.js

425 lines
17 KiB
JavaScript
Raw Normal View History

2020-05-23 20:45:54 +00:00
/* *
*
* (c) 2010-2020 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
import H from './Globals.js';
import U from './Utilities.js';
var attr = U.attr, createElement = U.createElement, css = U.css, defined = U.defined, extend = U.extend, pick = U.pick, pInt = U.pInt;
import './SvgRenderer.js';
var isFirefox = H.isFirefox, isMS = H.isMS, isWebKit = H.isWebKit, SVGElement = H.SVGElement, SVGRenderer = H.SVGRenderer, win = H.win;
/* eslint-disable valid-jsdoc */
// Extend SvgElement for useHTML option.
extend(SVGElement.prototype, /** @lends SVGElement.prototype */ {
/**
* Apply CSS to HTML elements. This is used in text within SVG rendering and
* by the VML renderer
*
* @private
* @function Highcharts.SVGElement#htmlCss
*
* @param {Highcharts.CSSObject} styles
*
* @return {Highcharts.SVGElement}
*/
htmlCss: function (styles) {
var wrapper = this, element = wrapper.element,
// When setting or unsetting the width style, we need to update
// transform (#8809)
isSettingWidth = (element.tagName === 'SPAN' &&
styles &&
'width' in styles), textWidth = pick(isSettingWidth && styles.width, void 0), doTransform;
if (isSettingWidth) {
delete styles.width;
wrapper.textWidth = textWidth;
doTransform = true;
}
if (styles && styles.textOverflow === 'ellipsis') {
styles.whiteSpace = 'nowrap';
styles.overflow = 'hidden';
}
wrapper.styles = extend(wrapper.styles, styles);
css(wrapper.element, styles);
// Now that all styles are applied, to the transform
if (doTransform) {
wrapper.htmlUpdateTransform();
}
return wrapper;
},
/**
* VML and useHTML method for calculating the bounding box based on offsets.
*
* @private
* @function Highcharts.SVGElement#htmlGetBBox
*
* @param {boolean} refresh
* Whether to force a fresh value from the DOM or to use the cached
* value.
*
* @return {Highcharts.BBoxObject}
* A hash containing values for x, y, width and height.
*/
htmlGetBBox: function () {
var wrapper = this, element = wrapper.element;
return {
x: element.offsetLeft,
y: element.offsetTop,
width: element.offsetWidth,
height: element.offsetHeight
};
},
/**
* VML override private method to update elements based on internal
* properties based on SVG transform.
*
* @private
* @function Highcharts.SVGElement#htmlUpdateTransform
* @return {void}
*/
htmlUpdateTransform: function () {
// aligning non added elements is expensive
if (!this.added) {
this.alignOnAdd = true;
return;
}
var wrapper = this, renderer = wrapper.renderer, elem = wrapper.element, translateX = wrapper.translateX || 0, translateY = wrapper.translateY || 0, x = wrapper.x || 0, y = wrapper.y || 0, align = wrapper.textAlign || 'left', alignCorrection = {
left: 0, center: 0.5, right: 1
}[align], styles = wrapper.styles, whiteSpace = styles && styles.whiteSpace;
/**
* @private
* @return {number}
*/
function getTextPxLength() {
// Reset multiline/ellipsis in order to read width (#4928,
// #5417)
css(elem, {
width: '',
whiteSpace: whiteSpace || 'nowrap'
});
return elem.offsetWidth;
}
// apply translate
css(elem, {
marginLeft: translateX,
marginTop: translateY
});
if (!renderer.styledMode && wrapper.shadows) { // used in labels/tooltip
wrapper.shadows.forEach(function (shadow) {
css(shadow, {
marginLeft: translateX + 1,
marginTop: translateY + 1
});
});
}
// apply inversion
if (wrapper.inverted) { // wrapper is a group
[].forEach.call(elem.childNodes, function (child) {
renderer.invertChild(child, elem);
});
}
if (elem.tagName === 'SPAN') {
var rotation = wrapper.rotation, baseline, textWidth = wrapper.textWidth && pInt(wrapper.textWidth), currentTextTransform = [
rotation,
align,
elem.innerHTML,
wrapper.textWidth,
wrapper.textAlign
].join(',');
// Update textWidth. Use the memoized textPxLength if possible, to
// avoid the getTextPxLength function using elem.offsetWidth.
// Calling offsetWidth affects rendering time as it forces layout
// (#7656).
if (textWidth !== wrapper.oldTextWidth &&
((textWidth > wrapper.oldTextWidth) ||
(wrapper.textPxLength || getTextPxLength()) > textWidth) && (
// Only set the width if the text is able to word-wrap, or
// text-overflow is ellipsis (#9537)
/[ \-]/.test(elem.textContent || elem.innerText) ||
elem.style.textOverflow === 'ellipsis')) { // #983, #1254
css(elem, {
width: textWidth + 'px',
display: 'block',
whiteSpace: whiteSpace || 'normal' // #3331
});
wrapper.oldTextWidth = textWidth;
wrapper.hasBoxWidthChanged = true; // #8159
}
else {
wrapper.hasBoxWidthChanged = false; // #8159
}
// Do the calculations and DOM access only if properties changed
if (currentTextTransform !== wrapper.cTT) {
baseline = renderer.fontMetrics(elem.style.fontSize, elem).b;
// Renderer specific handling of span rotation, but only if we
// have something to update.
if (defined(rotation) &&
((rotation !== (wrapper.oldRotation || 0)) ||
(align !== wrapper.oldAlign))) {
wrapper.setSpanRotation(rotation, alignCorrection, baseline);
}
wrapper.getSpanCorrection(
// Avoid elem.offsetWidth if we can, it affects rendering
// time heavily (#7656)
((!defined(rotation) && wrapper.textPxLength) || // #7920
elem.offsetWidth), baseline, alignCorrection, rotation, align);
}
// apply position with correction
css(elem, {
left: (x + (wrapper.xCorr || 0)) + 'px',
top: (y + (wrapper.yCorr || 0)) + 'px'
});
// record current text transform
wrapper.cTT = currentTextTransform;
wrapper.oldRotation = rotation;
wrapper.oldAlign = align;
}
},
/**
* Set the rotation of an individual HTML span.
*
* @private
* @function Highcharts.SVGElement#setSpanRotation
* @param {number} rotation
* @param {number} alignCorrection
* @param {number} baseline
* @return {void}
*/
setSpanRotation: function (rotation, alignCorrection, baseline) {
var rotationStyle = {}, cssTransformKey = this.renderer.getTransformKey();
rotationStyle[cssTransformKey] = rotationStyle.transform =
'rotate(' + rotation + 'deg)';
rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] =
rotationStyle.transformOrigin =
(alignCorrection * 100) + '% ' + baseline + 'px';
css(this.element, rotationStyle);
},
/**
* Get the correction in X and Y positioning as the element is rotated.
*
* @private
* @function Highcharts.SVGElement#getSpanCorrection
* @param {number} width
* @param {number} baseline
* @param {number} alignCorrection
* @return {void}
*/
getSpanCorrection: function (width, baseline, alignCorrection) {
this.xCorr = -width * alignCorrection;
this.yCorr = -baseline;
}
});
// Extend SvgRenderer for useHTML option.
extend(SVGRenderer.prototype, /** @lends SVGRenderer.prototype */ {
/**
* @private
* @function Highcharts.SVGRenderer#getTransformKey
*
* @return {string}
*/
getTransformKey: function () {
return isMS && !/Edge/.test(win.navigator.userAgent) ?
'-ms-transform' :
isWebKit ?
'-webkit-transform' :
isFirefox ?
'MozTransform' :
win.opera ?
'-o-transform' :
'';
},
/**
* Create HTML text node. This is used by the VML renderer as well as the
* SVG renderer through the useHTML option.
*
* @private
* @function Highcharts.SVGRenderer#html
*
* @param {string} str
* The text of (subset) HTML to draw.
*
* @param {number} x
* The x position of the text's lower left corner.
*
* @param {number} y
* The y position of the text's lower left corner.
*
* @return {Highcharts.HTMLDOMElement}
*/
html: function (str, x, y) {
var wrapper = this.createElement('span'), element = wrapper.element, renderer = wrapper.renderer, isSVG = renderer.isSVG, addSetters = function (gWrapper, style) {
// These properties are set as attributes on the SVG group, and
// as identical CSS properties on the div. (#3542)
['opacity', 'visibility'].forEach(function (prop) {
gWrapper[prop + 'Setter'] = function (value, key, elem) {
var styleObject = gWrapper.div ?
gWrapper.div.style :
style;
SVGElement.prototype[prop + 'Setter']
.call(this, value, key, elem);
if (styleObject) {
styleObject[key] = value;
}
};
});
gWrapper.addedSetters = true;
};
// Text setter
wrapper.textSetter = function (value) {
if (value !== element.innerHTML) {
delete this.bBox;
delete this.oldTextWidth;
}
this.textStr = value;
element.innerHTML = pick(value, '');
wrapper.doTransform = true;
};
// Add setters for the element itself (#4938)
if (isSVG) { // #4938, only for HTML within SVG
addSetters(wrapper, wrapper.element.style);
}
// Various setters which rely on update transform
wrapper.xSetter =
wrapper.ySetter =
wrapper.alignSetter =
wrapper.rotationSetter =
function (value, key) {
if (key === 'align') {
// Do not overwrite the SVGElement.align method. Same as VML.
key = 'textAlign';
}
wrapper[key] = value;
wrapper.doTransform = true;
};
// Runs at the end of .attr()
wrapper.afterSetters = function () {
// Update transform. Do this outside the loop to prevent redundant
// updating for batch setting of attributes.
if (this.doTransform) {
this.htmlUpdateTransform();
this.doTransform = false;
}
};
// Set the default attributes
wrapper
.attr({
text: str,
x: Math.round(x),
y: Math.round(y)
})
.css({
position: 'absolute'
});
if (!renderer.styledMode) {
wrapper.css({
fontFamily: this.style.fontFamily,
fontSize: this.style.fontSize
});
}
// Keep the whiteSpace style outside the wrapper.styles collection
element.style.whiteSpace = 'nowrap';
// Use the HTML specific .css method
wrapper.css = wrapper.htmlCss;
// This is specific for HTML within SVG
if (isSVG) {
wrapper.add = function (svgGroupWrapper) {
var htmlGroup, container = renderer.box.parentNode, parentGroup, parents = [];
this.parentGroup = svgGroupWrapper;
// Create a mock group to hold the HTML elements
if (svgGroupWrapper) {
htmlGroup = svgGroupWrapper.div;
if (!htmlGroup) {
// Read the parent chain into an array and read from top
// down
parentGroup = svgGroupWrapper;
while (parentGroup) {
parents.push(parentGroup);
// Move up to the next parent group
parentGroup = parentGroup.parentGroup;
}
// Ensure dynamically updating position when any parent
// is translated
parents.reverse().forEach(function (parentGroup) {
var htmlGroupStyle, cls = attr(parentGroup.element, 'class');
/**
* Common translate setter for X and Y on the HTML
* group. Reverted the fix for #6957 du to
* positioning problems and offline export (#7254,
* #7280, #7529)
* @private
* @param {*} value
* @param {string} key
* @return {void}
*/
function translateSetter(value, key) {
parentGroup[key] = value;
if (key === 'translateX') {
htmlGroupStyle.left = value + 'px';
}
else {
htmlGroupStyle.top = value + 'px';
}
parentGroup.doTransform = true;
}
// Create a HTML div and append it to the parent div
// to emulate the SVG group structure
htmlGroup =
parentGroup.div =
parentGroup.div || createElement('div', cls ? { className: cls } : void 0, {
position: 'absolute',
left: (parentGroup.translateX || 0) + 'px',
top: (parentGroup.translateY || 0) + 'px',
display: parentGroup.display,
opacity: parentGroup.opacity,
pointerEvents: (parentGroup.styles &&
parentGroup.styles.pointerEvents) // #5595
// the top group is appended to container
}, htmlGroup || container);
// Shortcut
htmlGroupStyle = htmlGroup.style;
// Set listeners to update the HTML div's position
// whenever the SVG group position is changed.
extend(parentGroup, {
// (#7287) Pass htmlGroup to use
// the related group
classSetter: (function (htmlGroup) {
return function (value) {
this.element.setAttribute('class', value);
htmlGroup.className = value;
};
}(htmlGroup)),
on: function () {
if (parents[0].div) { // #6418
wrapper.on.apply({ element: parents[0].div }, arguments);
}
return parentGroup;
},
translateXSetter: translateSetter,
translateYSetter: translateSetter
});
if (!parentGroup.addedSetters) {
addSetters(parentGroup);
}
});
}
}
else {
htmlGroup = container;
}
htmlGroup.appendChild(element);
// Shared with VML:
wrapper.added = true;
if (wrapper.alignOnAdd) {
wrapper.htmlUpdateTransform();
}
return wrapper;
};
}
return wrapper;
}
});