From 7bb451cfcb2c08de9a8ee05caaa164199ea431a3 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 23 Sep 2021 21:26:27 +0800 Subject: [PATCH 001/148] WIP: try svg-ssr --- index.js | 8 +- index.ts | 3 +- src/PainterBase.ts | 8 +- src/svg-ssr/Painter.ts | 182 ++++++++++++++++++++++++++ src/svg-ssr/graphic.ts | 170 +++++++++++++++++++++++++ src/svg-ssr/helper.ts | 20 +++ src/svg-ssr/svg-ssr.ts | 4 + src/svg/Painter.ts | 8 +- src/svg/SVGPathRebuilder.ts | 138 ++++++++++++++++++++ src/svg/core.ts | 34 ++++- src/svg/graphic.ts | 246 +----------------------------------- src/svg/mapStyleToAttrs.ts | 94 ++++++++++++++ src/zrender.ts | 73 ++++------- test/svg-save.html | 2 +- test/svg-ssr.html | 33 +++++ 15 files changed, 723 insertions(+), 300 deletions(-) create mode 100644 src/svg-ssr/Painter.ts create mode 100644 src/svg-ssr/graphic.ts create mode 100644 src/svg-ssr/helper.ts create mode 100644 src/svg-ssr/svg-ssr.ts create mode 100644 src/svg/SVGPathRebuilder.ts create mode 100644 src/svg/mapStyleToAttrs.ts create mode 100644 test/svg-ssr.html diff --git a/index.js b/index.js index a95f37b0a..0b37da558 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,10 @@ export * from './lib/zrender'; export * from './lib/export'; -import {registerPainter} from './lib/zrender'; -import CanvasPainter from './lib/canvas/Painter'; -import SVGPainter from './lib/svg/Painter'; +import {registerPainter} from './src/zrender'; +import CanvasPainter from './src/canvas/Painter'; +import SVGPainter from './src/svg/Painter'; +import SVGSSRPainter from './src/svg-ssr/Painter'; registerPainter('canvas', CanvasPainter); registerPainter('svg', SVGPainter); +registerPainter('svg-ssr', SVGSSRPainter); diff --git a/index.ts b/index.ts index 717166057..ddeba4eee 100644 --- a/index.ts +++ b/index.ts @@ -4,6 +4,7 @@ export * from './src/export'; import {registerPainter} from './src/zrender'; import CanvasPainter from './src/canvas/Painter'; import SVGPainter from './src/svg/Painter'; +import SVGSSRPainter from './src/svg-ssr/Painter'; registerPainter('canvas', CanvasPainter); registerPainter('svg', SVGPainter); -// import './src/vml/vml'; +registerPainter('svg-ssr', SVGSSRPainter); diff --git a/src/PainterBase.ts b/src/PainterBase.ts index c4eedeb49..8da11401f 100644 --- a/src/PainterBase.ts +++ b/src/PainterBase.ts @@ -13,7 +13,10 @@ export interface PainterBase { type: string - root: HTMLElement + // root will be undefined if ssr is true + root?: HTMLElement + + ssr?: boolean // constructor(dom: HTMLElement, storage: Storage, opts: PainterOption, id: number): void @@ -21,6 +24,9 @@ export interface PainterBase { refresh(): void clear(): void + // must be given if ssr is true. + renderToString?(): void; + getType: () => string getWidth(): number diff --git a/src/svg-ssr/Painter.ts b/src/svg-ssr/Painter.ts new file mode 100644 index 000000000..2f42a4df5 --- /dev/null +++ b/src/svg-ssr/Painter.ts @@ -0,0 +1,182 @@ +/** + * SVG Painter + */ + +import * as util from '../core/util'; +import Path from '../graphic/Path'; +import ZRImage from '../graphic/Image'; +import TSpan from '../graphic/TSpan'; +import { + path as svgPath, + image as svgImage, + text as svgText, + SVGProxy +} from './graphic'; +import Displayable from '../graphic/Displayable'; +import Storage from '../Storage'; +import { PainterBase } from '../PainterBase'; +import { createElement, createElementClose, createElementOpen } from './helper'; +import { normalizeColor, SVGNS, XLINKNS } from '../svg/core'; + + function parseInt10(val: string) { + return parseInt(val, 10); + } + +function getSvgProxy(el: Displayable) { + if (el instanceof Path) { + return svgPath; + } + else if (el instanceof ZRImage) { + return svgImage; + } + else if (el instanceof TSpan) { + return svgText; + } + else { + return svgPath; + } +} + +interface SVGPainterOption { + width?: number + height?: number +} + +class SVGPainter implements PainterBase { + + type = 'svg-ssr' + + ssr = true + + storage: Storage + + private _opts: SVGPainterOption + + private _width: number + private _height: number + + private _backgroundColor: string + + constructor(root: HTMLElement, storage: Storage, opts: SVGPainterOption, zrId: number) { + this.storage = storage; + this._opts = opts = util.extend({}, opts); + + this.resize(opts.width, opts.height); + } + + getType() { + return this.type; + } + + refresh() { + throw 'refresh is not supported in SSR mode'; + } + + renderToString() { + const list = this.storage.getDisplayList(true); + const bgColor = this._backgroundColor; + const width = this._width + ''; + const height = this._height + ''; + + let backgroundRect; + if (bgColor && bgColor !== 'none') { + const { color, opacity } = normalizeColor(bgColor); + backgroundRect = createElement( + 'rect', + [ + ['width', width], + ['height', height], + ['x', '0'], + ['y', '0'], + ['id', '0'], + ['fill', color], + ['fillOpacity', opacity + ''] + ] + ); + } + + const svgElsArr = [ + createElementOpen('svg', + [ + ['width', width], + ['height', height], + ['xmlns', SVGNS], + ['xmlns:xlink', XLINKNS], + ['version', '1.1'], + ['baseProfile', 'full'] + ] + ), + // Background + backgroundRect, + + // Elements + this._paintList(list), + + createElementClose('svg') + ]; + + return svgElsArr.join('\n'); + } + + setBackgroundColor(backgroundColor: string) { + this._backgroundColor = backgroundColor; + } + + _paintList(list: Displayable[]) { + const listLen = list.length; + + const elStrs: string[] = []; + + for (let i = 0; i < listLen; i++) { + const displayable = list[i]; + const svgProxy = getSvgProxy(displayable); + if (!displayable.invisible && svgProxy) { + const str = (svgProxy as SVGProxy).brush(displayable); + elStrs.push(str); + } + } + + return elStrs.join('\n'); + } + + resize(width: number, height: number) { + this._width = width; + this._height = height; + } + + /** + * 获取绘图区域宽度 + */ + getWidth() { + return this._width; + } + + /** + * 获取绘图区域高度 + */ + getHeight() { + return this._height; + } + + dispose() {} + clear() {} + toDataURL() {} + + getViewportRoot = createMethodNotSupport('getViewportRoot') as PainterBase['getViewportRoot'] + getViewportRootOffset = createMethodNotSupport('getViewportRootOffset') as PainterBase['getViewportRootOffset'] + + refreshHover = createMethodNotSupport('refreshHover') as PainterBase['refreshHover']; + pathToImage = createMethodNotSupport('pathToImage') as PainterBase['pathToImage']; + configLayer = createMethodNotSupport('configLayer') as PainterBase['configLayer']; +} + + +// Not supported methods +function createMethodNotSupport(method: string): any { + return function () { + util.logError('In SVG mode painter not support method "' + method + '"'); + }; +} + + +export default SVGPainter; \ No newline at end of file diff --git a/src/svg-ssr/graphic.ts b/src/svg-ssr/graphic.ts new file mode 100644 index 000000000..a4f2c9a44 --- /dev/null +++ b/src/svg-ssr/graphic.ts @@ -0,0 +1,170 @@ +// TODO +// 1. shadow +// 2. Image: sx, sy, sw, sh + +import {getMatrixStr} from '../svg/core'; +import Path, { PathStyleProps } from '../graphic/Path'; +import ZRImage, { ImageStyleProps } from '../graphic/Image'; +import { DEFAULT_FONT, getLineHeight } from '../contain/text'; +import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; +import SVGPathRebuilder from '../svg/SVGPathRebuilder'; +import mapStyleToAttrs from '../svg/mapStyleToAttrs'; +import { createElementClose, createElementOpen } from './helper'; +import { MatrixArray } from '../core/matrix'; + +type Attrs = [string, string][] + +export interface SVGProxy { + brush(el: T): string +} + + +type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; + +function setStyleAttrs(attrs: Attrs, style: AllStyleOption, el: Path | TSpan | ZRImage) { + mapStyleToAttrs((key, val) => { + // TODO gradient + if (!val || (val as any).type !== 'linear' && (val as any).type !== 'radial') { + attrs.push([key, val]); + } + }, style, el); +} + +function setTransform(attrs: Attrs, m: MatrixArray) { + if (m) { + attrs.push(['transform', getMatrixStr(m)]); + } +} + + +const svgPath: SVGProxy = { + brush(el: Path) { + const style = el.style; + if (!el.path) { + el.createPathProxy(); + } + const path = el.path; + + if (el.shapeChanged()) { + path.beginPath(); + el.buildPath(path, el.shape); + el.pathUpdated(); + } + // Because SSR renderer only render once. So always create new to simplify the case. + const svgPathBuilder = new SVGPathRebuilder(); + svgPathBuilder.reset(); + path.rebuildPath(svgPathBuilder, 1); + svgPathBuilder.generateStr(); + + const attrs: Attrs = [['d', svgPathBuilder.getStr()]]; + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el); + + return [ + createElementOpen('path', attrs), + createElementClose('path') + ].join('\n'); + } +}; + +export {svgPath as path}; + +/*************************************************** + * IMAGE + **************************************************/ +const svgImage: SVGProxy = { + brush(el: ZRImage) { + const style = el.style; + let image = style.image; + + if (!image) { + return ''; + } + // Only support string image in ssr renderer. + + const x = style.x || 0; + const y = style.y || 0; + + const dw = style.width; + const dh = style.height; + + const attrs: Attrs = [ + ['href', image as string], + ['width', dw + ''], + ['height', dh + ''], + ['x', x + ''], + ['y', y + ''] + ]; + + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el); + + return [ + createElementOpen('image', attrs), + createElementClose('image') + ].join('\n'); + } +}; +export {svgImage as image}; + +/*************************************************** + * TEXT + **************************************************/ +const TEXT_ALIGN_TO_ANCHOR = { + left: 'start', + right: 'end', + center: 'middle', + middle: 'middle' +}; + +function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number { + // TODO Other values. + if (textBaseline === 'top') { + y += lineHeight / 2; + } + else if (textBaseline === 'bottom') { + y -= lineHeight / 2; + } + return y; +} + +const svgText: SVGProxy = { + brush(el: TSpan) { + const style = el.style; + + let text = style.text; + // Convert to string + text != null && (text += ''); + if (!text || isNaN(style.x) || isNaN(style.y)) { + return ''; + } + + // style.font has been normalized by `normalizeTextStyle`. + const font = style.font || DEFAULT_FONT; + + // Consider different font display differently in vertial align, we always + // set vertialAlign as 'middle', and use 'y' to locate text vertically. + const x = style.x || 0; + const y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline); + const textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign as keyof typeof TEXT_ALIGN_TO_ANCHOR] + || style.textAlign; + + const attrs: Attrs = [ + ['xml:space', 'preserve'], + ['style', `font: ${font}`], + ['dominant-baseline', 'central'], + ['text-anchor', textAlign], + ['x', x + ''], + ['y', y + ''] + ]; + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el); + + return [ + createElementOpen('image', attrs), + text, + createElementClose('image') + ].join('\n'); + } +}; +export {svgText as text}; diff --git a/src/svg-ssr/helper.ts b/src/svg-ssr/helper.ts new file mode 100644 index 000000000..d17dbf731 --- /dev/null +++ b/src/svg-ssr/helper.ts @@ -0,0 +1,20 @@ +export function createElement(name: string, attrs?: ([string, string] | [string])[]) { + return createElementOpen(name, attrs) + createElementClose(name); +} +export function createElementOpen(name: string, attrs?: ([string, string] | [string])[], selfClose?: boolean) { + const attrsStr: string[] = []; + if (attrs) { + for (let i = 0; i < attrs.length; i++) { + let part = attrs[i][0]; + if (attrs[i][1]) { + part += `="${attrs[i][1]}"`; + } + attrsStr.push(part); + } + } + return `<${name} ${attrsStr.join(' ')} ${selfClose ? '/>' : '>'}`; +} + +export function createElementClose(name: string) { + return ``; +} \ No newline at end of file diff --git a/src/svg-ssr/svg-ssr.ts b/src/svg-ssr/svg-ssr.ts new file mode 100644 index 000000000..c57f7b0ce --- /dev/null +++ b/src/svg-ssr/svg-ssr.ts @@ -0,0 +1,4 @@ +import {registerPainter} from '../zrender'; +import Painter from './Painter'; + +registerPainter('svg-ssr', Painter); \ No newline at end of file diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 811eb64a3..de70b9600 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -1,9 +1,8 @@ /** * SVG Painter - * @module zrender/svg/Painter */ -import {createElement, normalizeColor} from './core'; +import {createElement, normalizeColor, SVGNS, XLINKNS, XMLNS} from './core'; import * as util from '../core/util'; import Path from '../graphic/Path'; import ZRImage from '../graphic/Image'; @@ -114,8 +113,8 @@ class SVGPainter implements PainterBase { this._opts = opts = util.extend({}, opts || {}); const svgDom = createElement('svg'); - svgDom.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns', 'http://www.w3.org/2000/svg'); - svgDom.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', 'http://www.w3.org/1999/xlink'); + svgDom.setAttributeNS(XMLNS, 'xmlns', SVGNS); + svgDom.setAttributeNS(XMLNS, 'xmlns:xlink', XLINKNS); svgDom.setAttribute('version', '1.1'); svgDom.setAttribute('baseProfile', 'full'); @@ -253,7 +252,6 @@ class SVGPainter implements PainterBase { if (svgElement) { newVisibleList.push(displayable); } - } } diff --git a/src/svg/SVGPathRebuilder.ts b/src/svg/SVGPathRebuilder.ts new file mode 100644 index 000000000..b8df47014 --- /dev/null +++ b/src/svg/SVGPathRebuilder.ts @@ -0,0 +1,138 @@ +import { PathRebuilder } from '../core/PathProxy'; +import { isAroundZero, round4 } from './core'; + +const mathSin = Math.sin; +const mathCos = Math.cos; +const PI = Math.PI; +const PI2 = Math.PI * 2; +const degree = 180 / PI; + + +export default class SVGPathRebuilder implements PathRebuilder { + _d: (string | number)[] + _str: string + _invalid: boolean + + reset() { + this._d = []; + this._str = ''; + } + moveTo(x: number, y: number) { + this._add('M', x, y); + } + lineTo(x: number, y: number) { + this._add('L', x, y); + } + bezierCurveTo(x: number, y: number, x2: number, y2: number, x3: number, y3: number) { + this._add('C', x, y, x2, y2, x3, y3); + } + quadraticCurveTo(x: number, y: number, x2: number, y2: number) { + this._add('Q', x, y, x2, y2); + } + arc(cx: number, cy: number, r: number, startAngle: number, endAngle: number, anticlockwise: boolean) { + this.ellipse(cx, cy, r, r, 0, startAngle, endAngle, anticlockwise); + } + ellipse( + cx: number, cy: number, + rx: number, ry: number, + psi: number, + startAngle: number, + endAngle: number, + anticlockwise: boolean + ) { + + const firstCmd = this._d.length === 0; + + let dTheta = endAngle - startAngle; + const clockwise = !anticlockwise; + + const dThetaPositive = Math.abs(dTheta); + const isCircle = isAroundZero(dThetaPositive - PI2) + || (clockwise ? dTheta >= PI2 : -dTheta >= PI2); + + // Mapping to 0~2PI + const unifiedTheta = dTheta > 0 ? dTheta % PI2 : (dTheta % PI2 + PI2); + + let large = false; + if (isCircle) { + large = true; + } + else if (isAroundZero(dThetaPositive)) { + large = false; + } + else { + large = (unifiedTheta >= PI) === !!clockwise; + } + + const x0 = round4(cx + rx * mathCos(startAngle)); + const y0 = round4(cy + ry * mathSin(startAngle)); + + // It will not draw if start point and end point are exactly the same + // We need to shift the end point with a small value + // FIXME A better way to draw circle ? + if (isCircle) { + if (clockwise) { + dTheta = PI2 - 1e-4; + } + else { + dTheta = -PI2 + 1e-4; + } + + large = true; + + if (firstCmd) { + // Move to (x0, y0) only when CMD.A comes at the + // first position of a shape. + // For instance, when drawing a ring, CMD.A comes + // after CMD.M, so it's unnecessary to move to + // (x0, y0). + this._d.push('M', x0, y0); + } + } + + const x = round4(cx + rx * mathCos(startAngle + dTheta)); + const y = round4(cy + ry * mathSin(startAngle + dTheta)); + + if (isNaN(x0) || isNaN(y0) || isNaN(rx) || isNaN(ry) || isNaN(psi) || isNaN(degree) || isNaN(x) || isNaN(y)) { + return ''; + } + + // FIXME Ellipse + this._d.push('A', round4(rx), round4(ry), + Math.round(psi * degree), +large, +clockwise, x, y); + } + rect(x: number, y: number, w: number, h: number) { + this._add('M', x, y); + this._add('L', x + w, y); + this._add('L', x + w, y + h); + this._add('L', x, y + h); + this._add('L', x, y); + this._add('Z'); + } + closePath() { + // Not use Z as first command + if (this._d.length > 0) { + this._add('Z'); + } + } + + _add(cmd: string, a?: number, b?: number, c?: number, d?: number, e?: number, f?: number, g?: number, h?: number) { + this._d.push(cmd); + for (let i = 1; i < arguments.length; i++) { + const val = arguments[i]; + if (isNaN(val)) { + this._invalid = true; + return; + } + this._d.push(round4(val)); + } + } + + generateStr() { + this._str = this._invalid ? '' : this._d.join(' '); + this._d = []; + } + getStr() { + return this._str; + } +} \ No newline at end of file diff --git a/src/svg/core.ts b/src/svg/core.ts index 7ddc66b0f..967c2ffaf 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -1,7 +1,14 @@ +import { MatrixArray } from '../core/matrix'; import { parse } from '../tool/color'; +const mathRound = Math.round; + +export const SVGNS = 'http://www.w3.org/2000/svg'; +export const XLINKNS = 'http://www.w3.org/1999/xlink'; +export const XMLNS = 'http://www.w3.org/2000/xmlns/'; + export function createElement(name: string) { - return document.createElementNS('http://www.w3.org/2000/svg', name); + return document.createElementNS(SVGNS, name); } export function normalizeColor(color: string): { color: string, opacity: number } { @@ -21,3 +28,28 @@ export function normalizeColor(color: string): { color: string, opacity: number opacity: opacity == null ? 1 : opacity }; } + +const EPSILON = 1e-4; +export function isAroundZero(val: number) { + return val < EPSILON && val > -EPSILON; +} + +export function round3(val: number) { + return mathRound(val * 1e3) / 1e3; +} +export function round4(val: number) { + return mathRound(val * 1e4) / 1e4; +} + +export function getMatrixStr(m: MatrixArray) { + return 'matrix(' + // Avoid large string of matrix + // PENDING If have precision issue when scaled + + round3(m[0]) + ',' + + round3(m[1]) + ',' + + round3(m[2]) + ',' + + round3(m[3]) + ',' + + round4(m[4]) + ',' + + round4(m[5]) + + ')'; +} \ No newline at end of file diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index f7bbdfb22..5f6c7dbea 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -2,65 +2,24 @@ // 1. shadow // 2. Image: sx, sy, sw, sh -import {createElement, normalizeColor} from './core'; -import { PathRebuilder } from '../core/PathProxy'; +import {createElement, getMatrixStr, XLINKNS, XMLNS} from './core'; import * as matrix from '../core/matrix'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; import { DEFAULT_FONT, getLineHeight } from '../contain/text'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; -import { map } from '../core/util'; -import { normalizeLineDash } from '../graphic/helper/dashStyle'; +import SVGPathRebuilder from './SVGPathRebuilder'; +import mapStyleToAttrs from './mapStyleToAttrs'; export interface SVGProxy { brush(el: T): void } -const NONE = 'none'; -const mathRound = Math.round; -const mathSin = Math.sin; -const mathCos = Math.cos; -const PI = Math.PI; -const PI2 = Math.PI * 2; -const degree = 180 / PI; - -const EPSILON = 1e-4; - type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; -function round3(val: number) { - return mathRound(val * 1e3) / 1e3; -} -function round4(val: number) { - return mathRound(val * 1e4) / 1e4; -} - -function isAroundZero(val: number) { - return val < EPSILON && val > -EPSILON; -} - -function pathHasFill(style: AllStyleOption): style is PathStyleProps { - const fill = (style as PathStyleProps).fill; - return fill != null && fill !== NONE; -} - -function pathHasStroke(style: AllStyleOption): style is PathStyleProps { - const stroke = (style as PathStyleProps).stroke; - return stroke != null && stroke !== NONE; -} - function setTransform(svgEl: SVGElement, m: matrix.MatrixArray) { if (m) { - attr(svgEl, 'transform', 'matrix(' - // Avoid large string of matrix - // PENDING If have precision issue when scaled - + round3(m[0]) + ',' - + round3(m[1]) + ',' - + round3(m[2]) + ',' - + round3(m[3]) + ',' - + round4(m[4]) + ',' - + round4(m[5]) - + ')'); + attr(svgEl, 'transform', getMatrixStr(m)); } } @@ -72,7 +31,7 @@ function attr(el: SVGElement, key: string, val: string) { } function attrXLink(el: SVGElement, key: string, val: string) { - el.setAttributeNS('http://www.w3.org/1999/xlink', key, val); + el.setAttributeNS(XLINKNS, key, val); } function attrXML(el: SVGElement, key: string, val: string) { @@ -83,200 +42,7 @@ function bindStyle(svgEl: SVGElement, style: PathStyleProps, el?: Path): void function bindStyle(svgEl: SVGElement, style: TSpanStyleProps, el?: TSpan): void function bindStyle(svgEl: SVGElement, style: ImageStyleProps, el?: ZRImage): void function bindStyle(svgEl: SVGElement, style: AllStyleOption, el?: Path | TSpan | ZRImage) { - const opacity = style.opacity == null ? 1 : style.opacity; - - // only set opacity. stroke and fill cannot be applied to svg image - if (el instanceof ZRImage) { - attr(svgEl, 'opacity', opacity + ''); - return; - } - - if (pathHasFill(style)) { - const fill = normalizeColor(style.fill as string); - attr(svgEl, 'fill', fill.color); - attr(svgEl, - 'fill-opacity', - (style.fillOpacity != null - ? style.fillOpacity * fill.opacity * opacity - : fill.opacity * opacity - ) + '' - ); - } - else { - attr(svgEl, 'fill', NONE); - } - - if (pathHasStroke(style)) { - const stroke = normalizeColor(style.stroke as string); - attr(svgEl, 'stroke', stroke.color); - const strokeWidth = style.lineWidth; - const strokeScale = style.strokeNoScale - ? (el as Path).getLineScale() - : 1; - attr(svgEl, 'stroke-width', (strokeScale ? strokeWidth / strokeScale : 0) + ''); - // stroke then fill for text; fill then stroke for others - attr(svgEl, 'paint-order', style.strokeFirst ? 'stroke' : 'fill'); - attr(svgEl, 'stroke-opacity', ( - style.strokeOpacity != null - ? style.strokeOpacity * stroke.opacity * opacity - : stroke.opacity * opacity - ) + ''); - let lineDash = style.lineDash && strokeWidth > 0 && normalizeLineDash(style.lineDash, strokeWidth); - if (lineDash) { - let lineDashOffset = style.lineDashOffset; - if (strokeScale && strokeScale !== 1) { - lineDash = map(lineDash, function (rawVal) { - return rawVal / strokeScale; - }); - if (lineDashOffset) { - lineDashOffset /= strokeScale; - lineDashOffset = mathRound(lineDashOffset); - } - } - attr(svgEl, 'stroke-dasharray', lineDash.join(',')); - attr(svgEl, 'stroke-dashoffset', (lineDashOffset || 0) + ''); - } - else { - attr(svgEl, 'stroke-dasharray', NONE); - } - - // PENDING - style.lineCap && attr(svgEl, 'stroke-linecap', style.lineCap); - style.lineJoin && attr(svgEl, 'stroke-linejoin', style.lineJoin); - style.miterLimit && attr(svgEl, 'stroke-miterlimit', style.miterLimit + ''); - } - else { - attr(svgEl, 'stroke', NONE); - } -} - -class SVGPathRebuilder implements PathRebuilder { - _d: (string | number)[] - _str: string - _invalid: boolean - - reset() { - this._d = []; - this._str = ''; - } - moveTo(x: number, y: number) { - this._add('M', x, y); - } - lineTo(x: number, y: number) { - this._add('L', x, y); - } - bezierCurveTo(x: number, y: number, x2: number, y2: number, x3: number, y3: number) { - this._add('C', x, y, x2, y2, x3, y3); - } - quadraticCurveTo(x: number, y: number, x2: number, y2: number) { - this._add('Q', x, y, x2, y2); - } - arc(cx: number, cy: number, r: number, startAngle: number, endAngle: number, anticlockwise: boolean) { - this.ellipse(cx, cy, r, r, 0, startAngle, endAngle, anticlockwise); - } - ellipse( - cx: number, cy: number, - rx: number, ry: number, - psi: number, - startAngle: number, - endAngle: number, - anticlockwise: boolean - ) { - - const firstCmd = this._d.length === 0; - - let dTheta = endAngle - startAngle; - const clockwise = !anticlockwise; - - const dThetaPositive = Math.abs(dTheta); - const isCircle = isAroundZero(dThetaPositive - PI2) - || (clockwise ? dTheta >= PI2 : -dTheta >= PI2); - - // Mapping to 0~2PI - const unifiedTheta = dTheta > 0 ? dTheta % PI2 : (dTheta % PI2 + PI2); - - let large = false; - if (isCircle) { - large = true; - } - else if (isAroundZero(dThetaPositive)) { - large = false; - } - else { - large = (unifiedTheta >= PI) === !!clockwise; - } - - const x0 = round4(cx + rx * mathCos(startAngle)); - const y0 = round4(cy + ry * mathSin(startAngle)); - - // It will not draw if start point and end point are exactly the same - // We need to shift the end point with a small value - // FIXME A better way to draw circle ? - if (isCircle) { - if (clockwise) { - dTheta = PI2 - 1e-4; - } - else { - dTheta = -PI2 + 1e-4; - } - - large = true; - - if (firstCmd) { - // Move to (x0, y0) only when CMD.A comes at the - // first position of a shape. - // For instance, when drawing a ring, CMD.A comes - // after CMD.M, so it's unnecessary to move to - // (x0, y0). - this._d.push('M', x0, y0); - } - } - - const x = round4(cx + rx * mathCos(startAngle + dTheta)); - const y = round4(cy + ry * mathSin(startAngle + dTheta)); - - if (isNaN(x0) || isNaN(y0) || isNaN(rx) || isNaN(ry) || isNaN(psi) || isNaN(degree) || isNaN(x) || isNaN(y)) { - return ''; - } - - // FIXME Ellipse - this._d.push('A', round4(rx), round4(ry), - mathRound(psi * degree), +large, +clockwise, x, y); - } - rect(x: number, y: number, w: number, h: number) { - this._add('M', x, y); - this._add('L', x + w, y); - this._add('L', x + w, y + h); - this._add('L', x, y + h); - this._add('L', x, y); - this._add('Z'); - } - closePath() { - // Not use Z as first command - if (this._d.length > 0) { - this._add('Z'); - } - } - - _add(cmd: string, a?: number, b?: number, c?: number, d?: number, e?: number, f?: number, g?: number, h?: number) { - this._d.push(cmd); - for (let i = 1; i < arguments.length; i++) { - const val = arguments[i]; - if (isNaN(val)) { - this._invalid = true; - return; - } - this._d.push(round4(val)); - } - } - - generateStr() { - this._str = this._invalid ? '' : this._d.join(' '); - this._d = []; - } - getStr() { - return this._str; - } + mapStyleToAttrs((key, val) => attr(svgEl, key, val), style, el); } interface PathWithSVGBuildPath extends Path { diff --git a/src/svg/mapStyleToAttrs.ts b/src/svg/mapStyleToAttrs.ts new file mode 100644 index 000000000..c293b5305 --- /dev/null +++ b/src/svg/mapStyleToAttrs.ts @@ -0,0 +1,94 @@ + +import Path, { PathStyleProps } from '../graphic/Path'; +import ZRImage, { ImageStyleProps } from '../graphic/Image'; +import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; +import { normalizeLineDash } from '../graphic/helper/dashStyle'; +import { map } from '../core/util'; +import { normalizeColor } from './core'; + +type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; + +const NONE = 'none'; +const mathRound = Math.round; + +function pathHasFill(style: AllStyleOption): style is PathStyleProps { + const fill = (style as PathStyleProps).fill; + return fill != null && fill !== NONE; +} + +function pathHasStroke(style: AllStyleOption): style is PathStyleProps { + const stroke = (style as PathStyleProps).stroke; + return stroke != null && stroke !== NONE; +} + +export default function mapStyleToAttrs( + updateAttr: (key: string, val: string) => void, + style: AllStyleOption, + el?: Path | TSpan | ZRImage +): void { + const opacity = style.opacity == null ? 1 : style.opacity; + + // only set opacity. stroke and fill cannot be applied to svg image + if (el instanceof ZRImage) { + updateAttr('opacity', opacity + ''); + return; + } + + if (pathHasFill(style)) { + const fill = normalizeColor(style.fill as string); + updateAttr('fill', fill.color); + updateAttr( + 'fill-opacity', + (style.fillOpacity != null + ? style.fillOpacity * fill.opacity * opacity + : fill.opacity * opacity + ) + '' + ); + } + else { + updateAttr('fill', NONE); + } + + if (pathHasStroke(style)) { + const stroke = normalizeColor(style.stroke as string); + updateAttr('stroke', stroke.color); + const strokeWidth = style.lineWidth; + const strokeScale = style.strokeNoScale + ? (el as Path).getLineScale() + : 1; + updateAttr('stroke-width', (strokeScale ? strokeWidth / strokeScale : 0) + ''); + // stroke then fill for text; fill then stroke for others + updateAttr('paint-order', style.strokeFirst ? 'stroke' : 'fill'); + updateAttr('stroke-opacity', ( + style.strokeOpacity != null + ? style.strokeOpacity * stroke.opacity * opacity + : stroke.opacity * opacity + ) + ''); + let lineDash = style.lineDash && strokeWidth > 0 && normalizeLineDash(style.lineDash, strokeWidth); + if (lineDash) { + let lineDashOffset = style.lineDashOffset; + if (strokeScale && strokeScale !== 1) { + lineDash = map(lineDash, function (rawVal) { + return rawVal / strokeScale; + }); + if (lineDashOffset) { + lineDashOffset /= strokeScale; + lineDashOffset = mathRound(lineDashOffset); + } + } + updateAttr('stroke-dasharray', lineDash.join(',')); + updateAttr('stroke-dashoffset', (lineDashOffset || 0) + ''); + } + else { + updateAttr('stroke-dasharray', NONE); + } + + // PENDING + style.lineCap && updateAttr('stroke-linecap', style.lineCap); + style.lineJoin && updateAttr('stroke-linejoin', style.lineJoin); + style.miterLimit && updateAttr('stroke-miterlimit', style.miterLimit + ''); + } + else { + updateAttr('stroke', NONE); + } +} diff --git a/src/zrender.ts b/src/zrender.ts index af82a7823..9fda1c237 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -21,8 +21,6 @@ import { LayerConfig } from './canvas/Layer'; import { GradientObject } from './graphic/Gradient'; import { PatternObject } from './graphic/Pattern'; import { EventCallback } from './core/Eventful'; -import TSpan from './graphic/TSpan'; -import ZRImage from './graphic/Image'; import Displayable from './graphic/Displayable'; import { lum } from './tool/color'; import { DARK_MODE_THRESHOLD } from './config'; @@ -68,8 +66,10 @@ function isDarkMode(backgroundColor: string | GradientObject | PatternObject): b } class ZRender { - - dom: HTMLElement + /** + * Not necessary if using SSR painter like svg-ssr + */ + dom?: HTMLElement id: number @@ -92,7 +92,7 @@ class ZRender { private _backgroundColor: string | GradientObject | PatternObject; - constructor(id: number, dom: HTMLElement, opts?: ZRenderInitOpt) { + constructor(id: number, dom?: HTMLElement, opts?: ZRenderInitOpt) { opts = opts || {}; /** @@ -124,21 +124,25 @@ class ZRender { : opts.useDirtyRect; const painter = new painterCtors[rendererType](dom, storage, opts, id); + const SSRMode = painter.ssr; this.storage = storage; this.painter = painter; - const handerProxy = (!env.node && !env.worker) + const handerProxy = (!env.node && !env.worker && !SSRMode) ? new HandlerProxy(painter.getViewportRoot(), painter.root) : null; this.handler = new Handler(storage, painter, handerProxy, painter.root); this.animation = new Animation({ stage: { - update: () => this._flush(true) + update: SSRMode ? null : () => this._flush(true) } }); - this.animation.start(); + + if (!SSRMode) { + this.animation.start(); + } } /** @@ -220,12 +224,6 @@ class ZRender { this.painter.refresh(); // Avoid trigger zr.refresh in Element#beforeUpdate hook this._needsRefresh = false; - - // const end = new Date(); - // const log = document.getElementById('log'); - // if (log) { - // log.innerHTML = log.innerHTML + '
' + (end - start); - // } } /** @@ -244,6 +242,16 @@ class ZRender { this._flush(false); } + renderToString() { + const painter = this.painter; + if (!painter.renderToString) { + throw 'Can only use renderToString in svg-ssr'; + } + // pending + this.animation.update(true); + return painter.renderToString(); + } + private _flush(fromInside?: boolean) { let triggerRendered; @@ -291,27 +299,6 @@ class ZRender { this._stillFrameAccum = 0; } - /** - * Add element to hover layer - */ - addHover(el: Displayable) { - // deprecated. - } - - /** - * Add element from hover layer - */ - removeHover(el: Path | TSpan | ZRImage) { - // deprecated. - } - - /** - * Clear all hover elements in hover layer - */ - clearHover() { - // deprecated. - } - /** * Refresh hover in next frame */ @@ -363,18 +350,6 @@ class ZRender { return this.painter.getHeight(); } - /** - * Export the canvas as Base64 URL - * @param {string} type - * @param {string} [backgroundColor='#fff'] - * @return {string} Base64 URL - */ - // toDataURL: function(type, backgroundColor) { - // return this.painter.getRenderedCanvas({ - // backgroundColor: backgroundColor - // }).toDataURL(type); - // }, - /** * Converting a path to image. * It has much better performance of drawing image rather than drawing a vector path. @@ -481,8 +456,10 @@ export interface ZRenderInitOpt { /** * Initializing a zrender instance + * + * @param dom Not necessary if using SSR painter like svg-ssr */ -export function init(dom: HTMLElement, opts?: ZRenderInitOpt) { +export function init(dom?: HTMLElement | null, opts?: ZRenderInitOpt) { const zr = new ZRender(zrUtil.guid(), dom, opts); instances[zr.id] = zr; return zr; diff --git a/test/svg-save.html b/test/svg-save.html index 921053d26..326337b09 100644 --- a/test/svg-save.html +++ b/test/svg-save.html @@ -40,7 +40,7 @@ }); zr.add(rect); - var svgPath = zr.painter.pathToDataUrl(); + var svgPath = zr.painter.toDataURL(); console.log(svgPath); var img = document.getElementById('img'); diff --git a/test/svg-ssr.html b/test/svg-ssr.html new file mode 100644 index 000000000..38d8fa436 --- /dev/null +++ b/test/svg-ssr.html @@ -0,0 +1,33 @@ + + + + + + + SVG SSR + + + +
+ + + + \ No newline at end of file From cd93186dd23a81e142797f73c95988d2c5841622 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 24 Sep 2021 11:08:56 +0800 Subject: [PATCH 002/148] wip(svg-ssr): compress generated strings --- src/svg-ssr/Painter.ts | 33 +--- src/svg-ssr/graphic.ts | 285 ++++++++++++++++++------------ src/svg/Painter.ts | 3 +- src/svg/SVGPathRebuilder.ts | 14 +- src/svg/core.ts | 46 +---- src/svg/graphic.ts | 21 +-- src/svg/helper/ClippathManager.ts | 14 +- src/svg/helper/PatternManager.ts | 5 +- src/svg/helper/ShadowManager.ts | 23 +-- src/svg/mapStyleToAttrs.ts | 57 +++--- src/svg/shared.ts | 103 +++++++++++ 11 files changed, 331 insertions(+), 273 deletions(-) create mode 100644 src/svg/shared.ts diff --git a/src/svg-ssr/Painter.ts b/src/svg-ssr/Painter.ts index 2f42a4df5..94bdc506f 100644 --- a/src/svg-ssr/Painter.ts +++ b/src/svg-ssr/Painter.ts @@ -3,40 +3,20 @@ */ import * as util from '../core/util'; -import Path from '../graphic/Path'; -import ZRImage from '../graphic/Image'; -import TSpan from '../graphic/TSpan'; import { - path as svgPath, - image as svgImage, - text as svgText, - SVGProxy + brush } from './graphic'; import Displayable from '../graphic/Displayable'; import Storage from '../Storage'; import { PainterBase } from '../PainterBase'; import { createElement, createElementClose, createElementOpen } from './helper'; -import { normalizeColor, SVGNS, XLINKNS } from '../svg/core'; +import { SVGNS, XLINKNS } from '../svg/core'; +import { normalizeColor } from '../svg/shared'; function parseInt10(val: string) { return parseInt(val, 10); } -function getSvgProxy(el: Displayable) { - if (el instanceof Path) { - return svgPath; - } - else if (el instanceof ZRImage) { - return svgImage; - } - else if (el instanceof TSpan) { - return svgText; - } - else { - return svgPath; - } -} - interface SVGPainterOption { width?: number height?: number @@ -129,10 +109,9 @@ class SVGPainter implements PainterBase { for (let i = 0; i < listLen; i++) { const displayable = list[i]; - const svgProxy = getSvgProxy(displayable); - if (!displayable.invisible && svgProxy) { - const str = (svgProxy as SVGProxy).brush(displayable); - elStrs.push(str); + if (!displayable.invisible) { + const str = brush(displayable); + str && elStrs.push(str); } } diff --git a/src/svg-ssr/graphic.ts b/src/svg-ssr/graphic.ts index a4f2c9a44..afcaa0060 100644 --- a/src/svg-ssr/graphic.ts +++ b/src/svg-ssr/graphic.ts @@ -2,7 +2,7 @@ // 1. shadow // 2. Image: sx, sy, sw, sh -import {getMatrixStr} from '../svg/core'; +import { adjustTextY, getMatrixStr, getShadowKey, hasShadow, isAroundZero, round4, TEXT_ALIGN_TO_ANCHOR } from '../svg/shared'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; import { DEFAULT_FONT, getLineHeight } from '../contain/text'; @@ -11,6 +11,10 @@ import SVGPathRebuilder from '../svg/SVGPathRebuilder'; import mapStyleToAttrs from '../svg/mapStyleToAttrs'; import { createElementClose, createElementOpen } from './helper'; import { MatrixArray } from '../core/matrix'; +import Displayable from '../graphic/Displayable'; +import { LinearGradientObject } from '../graphic/LinearGradient'; +import { RadialGradientObject } from '../graphic/RadialGradient'; +import { PatternObject } from '../graphic/Pattern'; type Attrs = [string, string][] @@ -27,144 +31,193 @@ function setStyleAttrs(attrs: Attrs, style: AllStyleOption, el: Path | TSpan | Z if (!val || (val as any).type !== 'linear' && (val as any).type !== 'radial') { attrs.push([key, val]); } - }, style, el); + }, style, el, false); +} + +function noRotateScale(m: MatrixArray) { + return isAroundZero(m[0] - 1) + && isAroundZero(m[1]) + && isAroundZero(m[2]) + && isAroundZero(m[3] - 1); +} + +function noTranslate(m: MatrixArray) { + return isAroundZero(m[4]) && isAroundZero(m[5]); } function setTransform(attrs: Attrs, m: MatrixArray) { - if (m) { - attrs.push(['transform', getMatrixStr(m)]); + if (m && !(noTranslate(m) && noRotateScale(m))) { + attrs.push([ + 'transform', + // Use translate possible to reduce the size a bit. + noRotateScale(m) ? `translate(${round4(m[4])} ${round4(m[5])})` : getMatrixStr(m) + ]); } } -const svgPath: SVGProxy = { - brush(el: Path) { - const style = el.style; - if (!el.path) { - el.createPathProxy(); - } - const path = el.path; +export function brushSVGPath(el: Path) { + const style = el.style; + if (!el.path) { + el.createPathProxy(); + } + const path = el.path; - if (el.shapeChanged()) { - path.beginPath(); - el.buildPath(path, el.shape); - el.pathUpdated(); - } - // Because SSR renderer only render once. So always create new to simplify the case. - const svgPathBuilder = new SVGPathRebuilder(); - svgPathBuilder.reset(); - path.rebuildPath(svgPathBuilder, 1); - svgPathBuilder.generateStr(); - - const attrs: Attrs = [['d', svgPathBuilder.getStr()]]; - setTransform(attrs, el.transform); - setStyleAttrs(attrs, style, el); - - return [ - createElementOpen('path', attrs), - createElementClose('path') - ].join('\n'); + if (el.shapeChanged()) { + path.beginPath(); + el.buildPath(path, el.shape); + el.pathUpdated(); } -}; + // Because SSR renderer only render once. So always create new to simplify the case. + const svgPathBuilder = new SVGPathRebuilder(); + svgPathBuilder.reset(); + path.rebuildPath(svgPathBuilder, 1); + svgPathBuilder.generateStr(); + + const attrs: Attrs = [['d', svgPathBuilder.getStr()]]; + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el); + + return [ + createElementOpen('path', attrs), + createElementClose('path') + ].join('\n'); +} -export {svgPath as path}; +export function brushSVGImage(el: ZRImage) { + const style = el.style; + let image = style.image; -/*************************************************** - * IMAGE - **************************************************/ -const svgImage: SVGProxy = { - brush(el: ZRImage) { - const style = el.style; - let image = style.image; + if (!image) { + return ''; + } + // Only support string image in ssr renderer. - if (!image) { - return ''; - } - // Only support string image in ssr renderer. + const x = style.x || 0; + const y = style.y || 0; - const x = style.x || 0; - const y = style.y || 0; + const dw = style.width; + const dh = style.height; - const dw = style.width; - const dh = style.height; + const attrs: Attrs = [ + ['href', image as string], + ['width', dw + ''], + ['height', dh + ''] + ]; + if (x) { + attrs.push(['x', x + '']); + } + if (y) { + attrs.push(['y', y + '']); + } - const attrs: Attrs = [ - ['href', image as string], - ['width', dw + ''], - ['height', dh + ''], - ['x', x + ''], - ['y', y + ''] - ]; + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el); - setTransform(attrs, el.transform); - setStyleAttrs(attrs, style, el); + return [ + createElementOpen('image', attrs), + createElementClose('image') + ].join('\n'); +}; - return [ - createElementOpen('image', attrs), - createElementClose('image') - ].join('\n'); +export function brushSVGTSpan(el: TSpan) { + const style = el.style; + + let text = style.text; + // Convert to string + text != null && (text += ''); + if (!text || isNaN(style.x) || isNaN(style.y)) { + return ''; } -}; -export {svgImage as image}; - -/*************************************************** - * TEXT - **************************************************/ -const TEXT_ALIGN_TO_ANCHOR = { - left: 'start', - right: 'end', - center: 'middle', - middle: 'middle' -}; -function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number { - // TODO Other values. - if (textBaseline === 'top') { - y += lineHeight / 2; + // style.font has been normalized by `normalizeTextStyle`. + const font = style.font || DEFAULT_FONT; + + // Consider different font display differently in vertial align, we always + // set vertialAlign as 'middle', and use 'y' to locate text vertically. + const x = style.x || 0; + const y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline); + const textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign as keyof typeof TEXT_ALIGN_TO_ANCHOR] + || style.textAlign; + + const attrs: Attrs = [ + ['style', `font:${font}`], + ['dominant-baseline', 'central'], + ['text-anchor', textAlign] + ]; + if (text.match(/\s\s/)) { + attrs.push(['xml:space', 'preserve']); + } + if (x) { + attrs.push(['x', x + '']); } - else if (textBaseline === 'bottom') { - y -= lineHeight / 2; + if (y) { + attrs.push(['y', y + '']); } - return y; + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el); + + return [ + createElementOpen('image', attrs), + text, + createElementClose('image') + ].join('\n'); } -const svgText: SVGProxy = { - brush(el: TSpan) { - const style = el.style; +export function brush(el: Displayable) { + if (el instanceof Path) { + return brushSVGPath(el); + } + else if (el instanceof ZRImage) { + return brushSVGImage(el); + } + else if (el instanceof TSpan) { + return brushSVGTSpan(el); + } +} - let text = style.text; - // Convert to string - text != null && (text += ''); - if (!text || isNaN(style.x) || isNaN(style.y)) { - return ''; +let shadowId = 0; +let gradientId = 0; +let patternId = 0; +let clipPathId = 0; +export function createShadow( + el: Displayable, + defs: Record, + shadowCache: Record +) { + const style = el.style; + if (hasShadow(style)) { + const shadowKey = getShadowKey(el); + if (shadowCache[shadowKey]) { + return shadowCache[shadowKey]; } - - // style.font has been normalized by `normalizeTextStyle`. - const font = style.font || DEFAULT_FONT; - - // Consider different font display differently in vertial align, we always - // set vertialAlign as 'middle', and use 'y' to locate text vertically. - const x = style.x || 0; - const y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline); - const textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign as keyof typeof TEXT_ALIGN_TO_ANCHOR] - || style.textAlign; - - const attrs: Attrs = [ - ['xml:space', 'preserve'], - ['style', `font: ${font}`], - ['dominant-baseline', 'central'], - ['text-anchor', textAlign], - ['x', x + ''], - ['y', y + ''] - ]; - setTransform(attrs, el.transform); - setStyleAttrs(attrs, style, el); - - return [ - createElementOpen('image', attrs), - text, - createElementClose('image') - ].join('\n'); + // const shadowStr = } -}; -export {svgText as text}; +} + +export function createGradient( + gradient: LinearGradientObject | RadialGradientObject, + defs: Record, + gradientCache: Record +) { + +} + +export function createPattern( + pattern: PatternObject, + defs: Record, + patternCache: Record +) { + +} + +export function createAnimation() { + +} + +export function createClipPath( + el: Displayable, + defs: Record +) { + +} \ No newline at end of file diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index de70b9600..7a2166832 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -2,7 +2,8 @@ * SVG Painter */ -import {createElement, normalizeColor, SVGNS, XLINKNS, XMLNS} from './core'; +import {createElement, SVGNS, XLINKNS, XMLNS} from './core'; +import { normalizeColor } from './shared'; import * as util from '../core/util'; import Path from '../graphic/Path'; import ZRImage from '../graphic/Image'; diff --git a/src/svg/SVGPathRebuilder.ts b/src/svg/SVGPathRebuilder.ts index b8df47014..0ef7f04e4 100644 --- a/src/svg/SVGPathRebuilder.ts +++ b/src/svg/SVGPathRebuilder.ts @@ -1,5 +1,5 @@ import { PathRebuilder } from '../core/PathProxy'; -import { isAroundZero, round4 } from './core'; +import { isAroundZero, round4 } from './shared'; const mathSin = Math.sin; const mathCos = Math.cos; @@ -86,7 +86,7 @@ export default class SVGPathRebuilder implements PathRebuilder { // For instance, when drawing a ring, CMD.A comes // after CMD.M, so it's unnecessary to move to // (x0, y0). - this._d.push('M', x0, y0); + this._add('M', x0, y0); } } @@ -98,8 +98,7 @@ export default class SVGPathRebuilder implements PathRebuilder { } // FIXME Ellipse - this._d.push('A', round4(rx), round4(ry), - Math.round(psi * degree), +large, +clockwise, x, y); + this._add('A', round4(rx), round4(ry), Math.round(psi * degree), +large, +clockwise, x, y); } rect(x: number, y: number, w: number, h: number) { this._add('M', x, y); @@ -117,19 +116,20 @@ export default class SVGPathRebuilder implements PathRebuilder { } _add(cmd: string, a?: number, b?: number, c?: number, d?: number, e?: number, f?: number, g?: number, h?: number) { - this._d.push(cmd); + const vals = []; for (let i = 1; i < arguments.length; i++) { const val = arguments[i]; if (isNaN(val)) { this._invalid = true; return; } - this._d.push(round4(val)); + vals.push(round4(val)); } + this._d.push(cmd + vals.join(' ')); } generateStr() { - this._str = this._invalid ? '' : this._d.join(' '); + this._str = this._invalid ? '' : this._d.join(''); this._d = []; } getStr() { diff --git a/src/svg/core.ts b/src/svg/core.ts index 967c2ffaf..a9263a864 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -1,7 +1,5 @@ -import { MatrixArray } from '../core/matrix'; -import { parse } from '../tool/color'; -const mathRound = Math.round; +export const mathRound = Math.round; export const SVGNS = 'http://www.w3.org/2000/svg'; export const XLINKNS = 'http://www.w3.org/1999/xlink'; @@ -11,45 +9,3 @@ export function createElement(name: string) { return document.createElementNS(SVGNS, name); } -export function normalizeColor(color: string): { color: string, opacity: number } { - let opacity; - if (!color || color === 'transparent') { - color = 'none'; - } - else if (typeof color === 'string' && color.indexOf('rgba') > -1) { - const arr = parse(color); - if (arr) { - color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')'; - opacity = arr[3]; - } - } - return { - color, - opacity: opacity == null ? 1 : opacity - }; -} - -const EPSILON = 1e-4; -export function isAroundZero(val: number) { - return val < EPSILON && val > -EPSILON; -} - -export function round3(val: number) { - return mathRound(val * 1e3) / 1e3; -} -export function round4(val: number) { - return mathRound(val * 1e4) / 1e4; -} - -export function getMatrixStr(m: MatrixArray) { - return 'matrix(' - // Avoid large string of matrix - // PENDING If have precision issue when scaled - + round3(m[0]) + ',' - + round3(m[1]) + ',' - + round3(m[2]) + ',' - + round3(m[3]) + ',' - + round4(m[4]) + ',' - + round4(m[5]) - + ')'; -} \ No newline at end of file diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 5f6c7dbea..02bfa2a88 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -2,7 +2,8 @@ // 1. shadow // 2. Image: sx, sy, sw, sh -import {createElement, getMatrixStr, XLINKNS, XMLNS} from './core'; +import {createElement, XLINKNS } from './core'; +import { getMatrixStr, TEXT_ALIGN_TO_ANCHOR, adjustTextY } from './shared'; import * as matrix from '../core/matrix'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; @@ -42,7 +43,7 @@ function bindStyle(svgEl: SVGElement, style: PathStyleProps, el?: Path): void function bindStyle(svgEl: SVGElement, style: TSpanStyleProps, el?: TSpan): void function bindStyle(svgEl: SVGElement, style: ImageStyleProps, el?: ZRImage): void function bindStyle(svgEl: SVGElement, style: AllStyleOption, el?: Path | TSpan | ZRImage) { - mapStyleToAttrs((key, val) => attr(svgEl, key, val), style, el); + mapStyleToAttrs((key, val) => attr(svgEl, key, val), style, el, true); } interface PathWithSVGBuildPath extends Path { @@ -145,23 +146,7 @@ export {svgImage as image}; /*************************************************** * TEXT **************************************************/ -const TEXT_ALIGN_TO_ANCHOR = { - left: 'start', - right: 'end', - center: 'middle', - middle: 'middle' -}; -function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number { - // TODO Other values. - if (textBaseline === 'top') { - y += lineHeight / 2; - } - else if (textBaseline === 'bottom') { - y -= lineHeight / 2; - } - return y; -} const svgText: SVGProxy = { brush(el: TSpan) { diff --git a/src/svg/helper/ClippathManager.ts b/src/svg/helper/ClippathManager.ts index 2e2fe3345..e997a8e6a 100644 --- a/src/svg/helper/ClippathManager.ts +++ b/src/svg/helper/ClippathManager.ts @@ -10,22 +10,12 @@ import Path from '../../graphic/Path'; import {SVGProxy} from '../graphic'; import { Dictionary } from '../../core/types'; import { isClipPathChanged } from '../../canvas/helper'; +import { getClipPathsKey } from '../shared'; type PathExtended = Path & { _dom: SVGElement } -function generateClipPathsKey(clipPaths: Path[]) { - let key: number[] = []; - if (clipPaths) { - for (let i = 0; i < clipPaths.length; i++) { - const clipPath = clipPaths[i]; - key.push(clipPath.id); - } - } - return key.join(','); -} - export function hasClipPath(displayable: Displayable) { const clipPaths = displayable.__clipPaths; return clipPaths && clipPaths.length > 0; @@ -61,7 +51,7 @@ export default class ClippathManager extends Definable { const clipPaths = displayable.__clipPaths; const keyDuplicateCount = this._keyDuplicateCount; - let clipPathKey = generateClipPathsKey(clipPaths); + let clipPathKey = getClipPathsKey(clipPaths); if (isClipPathChanged(clipPaths, prevDisplayable && prevDisplayable.__clipPaths)) { keyDuplicateCount[clipPathKey] = keyDuplicateCount[clipPathKey] || 0; keyDuplicateCount[clipPathKey] && (clipPathKey += '-' + keyDuplicateCount[clipPathKey]); diff --git a/src/svg/helper/PatternManager.ts b/src/svg/helper/PatternManager.ts index 1f1f74e3b..279d1f163 100644 --- a/src/svg/helper/PatternManager.ts +++ b/src/svg/helper/PatternManager.ts @@ -9,10 +9,7 @@ import Displayable from '../../graphic/Displayable'; import {PatternObject, ImagePatternObject, SVGPatternObject} from '../../graphic/Pattern'; import {createOrUpdateImage} from '../../graphic/helper/image'; import WeakMap from '../../core/WeakMap'; - -function isPattern(value: PatternObject | string): value is PatternObject { - return value && (!!(value as ImagePatternObject).image || !!(value as SVGPatternObject).svgElement); -} +import { isPattern } from '../shared'; const patternDomMap = new WeakMap(); diff --git a/src/svg/helper/ShadowManager.ts b/src/svg/helper/ShadowManager.ts index f836444fa..b846f3203 100644 --- a/src/svg/helper/ShadowManager.ts +++ b/src/svg/helper/ShadowManager.ts @@ -5,9 +5,8 @@ import Definable from './Definable'; import Displayable from '../../graphic/Displayable'; -import { PathStyleProps } from '../../graphic/Path'; import { Dictionary } from '../../core/types'; -import { normalizeColor } from '../core'; +import { getShadowKey, hasShadow, normalizeColor } from '../shared'; type DisplayableExtended = Displayable & { _shadowDom: SVGElement @@ -148,23 +147,3 @@ export default class ShadowManager extends Definable { this._shadowDomMap = {}; } } - - -function hasShadow(style: PathStyleProps) { - // TODO: textBoxShadowBlur is not supported yet - return style - && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY); -} - -function getShadowKey(displayable: Displayable) { - const style = displayable.style; - const globalScale = displayable.getGlobalScale(); - return [ - style.shadowColor, - (style.shadowBlur || 0).toFixed(2), // Reduce the precision - (style.shadowOffsetX || 0).toFixed(2), - (style.shadowOffsetY || 0).toFixed(2), - globalScale[0], - globalScale[1] - ].join(','); -} \ No newline at end of file diff --git a/src/svg/mapStyleToAttrs.ts b/src/svg/mapStyleToAttrs.ts index c293b5305..b1bd10499 100644 --- a/src/svg/mapStyleToAttrs.ts +++ b/src/svg/mapStyleToAttrs.ts @@ -4,7 +4,7 @@ import ZRImage, { ImageStyleProps } from '../graphic/Image'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import { normalizeLineDash } from '../graphic/helper/dashStyle'; import { map } from '../core/util'; -import { normalizeColor } from './core'; +import { normalizeColor } from './shared'; type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; @@ -24,7 +24,12 @@ function pathHasStroke(style: AllStyleOption): style is PathStyleProps { export default function mapStyleToAttrs( updateAttr: (key: string, val: string) => void, style: AllStyleOption, - el?: Path | TSpan | ZRImage + el: Path | TSpan | ZRImage, + /** + * Will try not to set the attribute if it's using default value if not using forceUpdate. + * Mainly for reduce the generated size in svg-ssr mode. + */ + forceUpdate: boolean ): void { const opacity = style.opacity == null ? 1 : style.opacity; @@ -37,13 +42,12 @@ export default function mapStyleToAttrs( if (pathHasFill(style)) { const fill = normalizeColor(style.fill as string); updateAttr('fill', fill.color); - updateAttr( - 'fill-opacity', - (style.fillOpacity != null - ? style.fillOpacity * fill.opacity * opacity - : fill.opacity * opacity - ) + '' - ); + const fillOpacity = style.fillOpacity != null + ? style.fillOpacity * fill.opacity * opacity + : fill.opacity * opacity; + if (forceUpdate || fillOpacity < 1) { + updateAttr('fill-opacity', fillOpacity + ''); + } } else { updateAttr('fill', NONE); @@ -52,18 +56,26 @@ export default function mapStyleToAttrs( if (pathHasStroke(style)) { const stroke = normalizeColor(style.stroke as string); updateAttr('stroke', stroke.color); - const strokeWidth = style.lineWidth; const strokeScale = style.strokeNoScale ? (el as Path).getLineScale() : 1; - updateAttr('stroke-width', (strokeScale ? strokeWidth / strokeScale : 0) + ''); + const strokeWidth = (strokeScale ? (style.lineWidth || 0) / strokeScale : 0); + const strokeOpacity = style.strokeOpacity != null + ? style.strokeOpacity * stroke.opacity * opacity + : stroke.opacity * opacity; + const strokeFirst = style.strokeFirst; + + if (forceUpdate || strokeWidth !== 1) { + updateAttr('stroke-width', strokeWidth + ''); + } // stroke then fill for text; fill then stroke for others - updateAttr('paint-order', style.strokeFirst ? 'stroke' : 'fill'); - updateAttr('stroke-opacity', ( - style.strokeOpacity != null - ? style.strokeOpacity * stroke.opacity * opacity - : stroke.opacity * opacity - ) + ''); + if (forceUpdate || strokeFirst) { + updateAttr('paint-order', strokeFirst ? 'stroke' : 'fill'); + } + if (forceUpdate || strokeOpacity < 1) { + updateAttr('stroke-opacity', strokeOpacity + ''); + } + let lineDash = style.lineDash && strokeWidth > 0 && normalizeLineDash(style.lineDash, strokeWidth); if (lineDash) { let lineDashOffset = style.lineDashOffset; @@ -77,18 +89,21 @@ export default function mapStyleToAttrs( } } updateAttr('stroke-dasharray', lineDash.join(',')); - updateAttr('stroke-dashoffset', (lineDashOffset || 0) + ''); + if (lineDashOffset || forceUpdate) { + updateAttr('stroke-dashoffset', (lineDashOffset || 0) + ''); + } } - else { + else if (forceUpdate) { + // Reset if force update. updateAttr('stroke-dasharray', NONE); } - // PENDING + // PENDING reset style.lineCap && updateAttr('stroke-linecap', style.lineCap); style.lineJoin && updateAttr('stroke-linejoin', style.lineJoin); style.miterLimit && updateAttr('stroke-miterlimit', style.miterLimit + ''); } - else { + else if (forceUpdate) { updateAttr('stroke', NONE); } } diff --git a/src/svg/shared.ts b/src/svg/shared.ts new file mode 100644 index 000000000..409df1e0e --- /dev/null +++ b/src/svg/shared.ts @@ -0,0 +1,103 @@ +// Shared methods of svg and svg-ssr + +import { MatrixArray } from '../core/matrix'; +import Displayable from '../graphic/Displayable'; +import Path from '../graphic/Path'; +import { ImagePatternObject, PatternObject, SVGPatternObject } from '../graphic/Pattern'; +import { parse } from '../tool/color'; +import { mathRound } from './core'; + +export function normalizeColor(color: string): { color: string; opacity: number; } { + let opacity; + if (!color || color === 'transparent') { + color = 'none'; + } + else if (typeof color === 'string' && color.indexOf('rgba') > -1) { + const arr = parse(color); + if (arr) { + color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')'; + opacity = arr[3]; + } + } + return { + color, + opacity: opacity == null ? 1 : opacity + }; +} +const EPSILON = 1e-4; +export function isAroundZero(val: number) { + return val < EPSILON && val > -EPSILON; +} + +export function round3(val: number) { + return mathRound(val * 1e3) / 1e3; +} +export function round4(val: number) { + return mathRound(val * 1e4) / 1e4; +} + +export function getMatrixStr(m: MatrixArray) { + return 'matrix(' + // Avoid large string of matrix + // PENDING If have precision issue when scaled + + round3(m[0]) + ',' + + round3(m[1]) + ',' + + round3(m[2]) + ',' + + round3(m[3]) + ',' + + round4(m[4]) + ',' + + round4(m[5]) + + ')'; +} + +export const TEXT_ALIGN_TO_ANCHOR = { + left: 'start', + right: 'end', + center: 'middle', + middle: 'middle' +}; + +export function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number { + // TODO Other values. + if (textBaseline === 'top') { + y += lineHeight / 2; + } + else if (textBaseline === 'bottom') { + y -= lineHeight / 2; + } + return y; +} + + +export function hasShadow(style: Displayable['style']) { + // TODO: textBoxShadowBlur is not supported yet + return style + && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY); +} + +export function getShadowKey(displayable: Displayable) { + const style = displayable.style; + const globalScale = displayable.getGlobalScale(); + return [ + style.shadowColor, + (style.shadowBlur || 0).toFixed(2), // Reduce the precision + (style.shadowOffsetX || 0).toFixed(2), + (style.shadowOffsetY || 0).toFixed(2), + globalScale[0], + globalScale[1] + ].join(','); +} + +export function getClipPathsKey(clipPaths: Path[]) { + let key: number[] = []; + if (clipPaths) { + for (let i = 0; i < clipPaths.length; i++) { + const clipPath = clipPaths[i]; + key.push(clipPath.id); + } + } + return key.join(','); +} + +export function isPattern(value: PatternObject | string): value is PatternObject { + return value && (!!(value as ImagePatternObject).image || !!(value as SVGPatternObject).svgElement); +} From 7381160b8df35d6d38a1722f1ee8bb3f8f68e3d4 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 24 Sep 2021 14:42:28 +0800 Subject: [PATCH 003/148] wip(svg-ssr): use builtin shape to optimize the size --- src/svg-ssr/graphic.ts | 109 ++++++++++++++++++++++++++++++++++------- src/svg-ssr/helper.ts | 4 +- test/svg-ssr.html | 41 +++++++++++----- 3 files changed, 122 insertions(+), 32 deletions(-) diff --git a/src/svg-ssr/graphic.ts b/src/svg-ssr/graphic.ts index afcaa0060..7e2c2bb84 100644 --- a/src/svg-ssr/graphic.ts +++ b/src/svg-ssr/graphic.ts @@ -2,7 +2,15 @@ // 1. shadow // 2. Image: sx, sy, sw, sh -import { adjustTextY, getMatrixStr, getShadowKey, hasShadow, isAroundZero, round4, TEXT_ALIGN_TO_ANCHOR } from '../svg/shared'; +import { + adjustTextY, + getMatrixStr, + getShadowKey, + hasShadow, + isAroundZero, + round4, + TEXT_ALIGN_TO_ANCHOR +} from '../svg/shared'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; import { DEFAULT_FONT, getLineHeight } from '../contain/text'; @@ -15,6 +23,10 @@ import Displayable from '../graphic/Displayable'; import { LinearGradientObject } from '../graphic/LinearGradient'; import { RadialGradientObject } from '../graphic/RadialGradient'; import { PatternObject } from '../graphic/Pattern'; +import Rect from '../graphic/shape/Rect'; +import { isArray, map } from '../core/util'; +import Polyline from '../graphic/shape/Polyline'; +import Polygon from '../graphic/shape/Polygon'; type Attrs = [string, string][] @@ -55,32 +67,95 @@ function setTransform(attrs: Attrs, m: MatrixArray) { } } +type ShapeMapDesc = (string | [string, string] | [string, string[]])[]; +type ConvertShapeToAttr = (shape: any, attrs: Attrs) => void; +type ShapeValidator = (shape: any) => boolean; + +function converPolyShape(shape: Polygon['shape'], attrs: Attrs) { + const points = shape.points; + const strArr = []; + for (let i = 0; i < points.length; i++) { + strArr.push(round4(points[i][0])); + strArr.push(round4(points[i][1])); + } + attrs.push(['points', strArr.join(' ')]); +} + +function validatePolyShape(shape: Polyline['shape']) { + return !shape.smooth; +} + +function createAttrsConvert(desc: ShapeMapDesc): ConvertShapeToAttr { + const normalizedDesc: [string, string[]][] = map(desc, (item) => + (typeof item === 'string' + ? [item, [item]] + : typeof item[1] === 'string' + ? [item[0], [item[1]]] : item as [string, string[]]) + ); + + return function (shape, attrs) { + for (let i = 0; i < normalizedDesc.length; i++) { + const item = normalizedDesc[i]; + const val = shape[item[0]]; + if (val != null) { + for (let k = 0; k < item[1].length; k++) { + attrs.push([item[1][k], round4(val) + '']); + } + } + } + }; +} + +const buitinShapesDef: Record = { + rect: [createAttrsConvert( + ['x', 'y', 'width', 'height', ['r', ['rx', 'ry']]] + ), (shape: Rect['shape']) => { + const r = shape.r; + // TODO all r same. + return !isArray(r); + }], + circle: [createAttrsConvert(['cx', 'cy', 'r'])], + polyline: [converPolyShape, validatePolyShape], + polygon: [converPolyShape, validatePolyShape] +}; + export function brushSVGPath(el: Path) { const style = el.style; - if (!el.path) { - el.createPathProxy(); + const shape = el.shape; + const builtinShpDef = buitinShapesDef[el.type]; + const attrs: Attrs = []; + let svgElType = 'path'; + // Using SVG builtin shapes if possible + if (builtinShpDef && !(builtinShpDef[1] && !builtinShpDef[1](shape))) { + svgElType = el.type; + builtinShpDef[0](shape, attrs); } - const path = el.path; + else { + if (!el.path) { + el.createPathProxy(); + } + const path = el.path; - if (el.shapeChanged()) { - path.beginPath(); - el.buildPath(path, el.shape); - el.pathUpdated(); - } - // Because SSR renderer only render once. So always create new to simplify the case. - const svgPathBuilder = new SVGPathRebuilder(); - svgPathBuilder.reset(); - path.rebuildPath(svgPathBuilder, 1); - svgPathBuilder.generateStr(); + if (el.shapeChanged()) { + path.beginPath(); + el.buildPath(path, el.shape); + el.pathUpdated(); + } + // Because SSR renderer only render once. So always create new to simplify the case. + const svgPathBuilder = new SVGPathRebuilder(); + svgPathBuilder.reset(); + path.rebuildPath(svgPathBuilder, 1); + svgPathBuilder.generateStr(); - const attrs: Attrs = [['d', svgPathBuilder.getStr()]]; + attrs.push(['d', svgPathBuilder.getStr()]); + } setTransform(attrs, el.transform); setStyleAttrs(attrs, style, el); return [ - createElementOpen('path', attrs), - createElementClose('path') + createElementOpen(svgElType, attrs), + createElementClose(svgElType) ].join('\n'); } diff --git a/src/svg-ssr/helper.ts b/src/svg-ssr/helper.ts index d17dbf731..87198b76b 100644 --- a/src/svg-ssr/helper.ts +++ b/src/svg-ssr/helper.ts @@ -1,7 +1,7 @@ export function createElement(name: string, attrs?: ([string, string] | [string])[]) { return createElementOpen(name, attrs) + createElementClose(name); } -export function createElementOpen(name: string, attrs?: ([string, string] | [string])[], selfClose?: boolean) { +export function createElementOpen(name: string, attrs?: ([string, string] | [string])[]) { const attrsStr: string[] = []; if (attrs) { for (let i = 0; i < attrs.length; i++) { @@ -12,7 +12,7 @@ export function createElementOpen(name: string, attrs?: ([string, string] | [str attrsStr.push(part); } } - return `<${name} ${attrsStr.join(' ')} ${selfClose ? '/>' : '>'}`; + return `<${name} ${attrsStr.join(' ')}>`; } export function createElementClose(name: string) { diff --git a/test/svg-ssr.html b/test/svg-ssr.html index 38d8fa436..f45b507b9 100644 --- a/test/svg-ssr.html +++ b/test/svg-ssr.html @@ -13,21 +13,36 @@ \ No newline at end of file From f06e85ed74f88bf2d5ed43a022f8849f63e29786 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 24 Sep 2021 14:45:05 +0800 Subject: [PATCH 004/148] wip(svg-ssr): merge from master --- src/svg/SVGPathRebuilder.ts | 40 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/svg/SVGPathRebuilder.ts b/src/svg/SVGPathRebuilder.ts index 0ef7f04e4..753ba603c 100644 --- a/src/svg/SVGPathRebuilder.ts +++ b/src/svg/SVGPathRebuilder.ts @@ -9,11 +9,15 @@ const degree = 180 / PI; export default class SVGPathRebuilder implements PathRebuilder { - _d: (string | number)[] - _str: string - _invalid: boolean + private _d: (string | number)[] + private _str: string + private _invalid: boolean + + // If is start of subpath + private _start: boolean; reset() { + this._start = true; this._d = []; this._str = ''; } @@ -40,9 +44,6 @@ export default class SVGPathRebuilder implements PathRebuilder { endAngle: number, anticlockwise: boolean ) { - - const firstCmd = this._d.length === 0; - let dTheta = endAngle - startAngle; const clockwise = !anticlockwise; @@ -79,15 +80,15 @@ export default class SVGPathRebuilder implements PathRebuilder { } large = true; + } - if (firstCmd) { - // Move to (x0, y0) only when CMD.A comes at the - // first position of a shape. - // For instance, when drawing a ring, CMD.A comes - // after CMD.M, so it's unnecessary to move to - // (x0, y0). - this._add('M', x0, y0); - } + if (this._start) { + // Move to (x0, y0) only when CMD.A comes at the + // first position of a shape. + // For instance, when drawing a ring, CMD.A comes + // after CMD.M, so it's unnecessary to move to + // (x0, y0). + this._add('M', x0, y0); } const x = round4(cx + rx * mathCos(startAngle + dTheta)); @@ -98,7 +99,8 @@ export default class SVGPathRebuilder implements PathRebuilder { } // FIXME Ellipse - this._add('A', round4(rx), round4(ry), Math.round(psi * degree), +large, +clockwise, x, y); + this._add('A', round4(rx), round4(ry), + Math.round(psi * degree), +large, +clockwise, x, y); } rect(x: number, y: number, w: number, h: number) { this._add('M', x, y); @@ -116,20 +118,20 @@ export default class SVGPathRebuilder implements PathRebuilder { } _add(cmd: string, a?: number, b?: number, c?: number, d?: number, e?: number, f?: number, g?: number, h?: number) { - const vals = []; + this._d.push(cmd); for (let i = 1; i < arguments.length; i++) { const val = arguments[i]; if (isNaN(val)) { this._invalid = true; return; } - vals.push(round4(val)); + this._d.push(round4(val)); } - this._d.push(cmd + vals.join(' ')); + this._start = cmd === 'Z'; } generateStr() { - this._str = this._invalid ? '' : this._d.join(''); + this._str = this._invalid ? '' : this._d.join(' '); this._d = []; } getStr() { From 570466297146b4dd1eb87ff8146de15d098fe66d Mon Sep 17 00:00:00 2001 From: pissang Date: Sat, 25 Sep 2021 17:23:33 +0800 Subject: [PATCH 005/148] wip(svg-ssr): support shadow and gradient --- src/svg-ssr/Painter.ts | 25 +++- src/svg-ssr/graphic.ts | 227 ++++++++++++++++++++++-------- src/svg-ssr/helper.ts | 11 +- src/svg/helper/ClippathManager.ts | 9 +- src/svg/helper/Definable.ts | 5 +- src/svg/helper/GradientManager.ts | 87 ++++-------- src/svg/helper/PatternManager.ts | 10 +- src/svg/helper/ShadowManager.ts | 16 +-- src/svg/mapStyleToAttrs.ts | 20 ++- src/svg/shared.ts | 23 +++ test/svg-ssr.html | 28 +++- 11 files changed, 304 insertions(+), 157 deletions(-) diff --git a/src/svg-ssr/Painter.ts b/src/svg-ssr/Painter.ts index 94bdc506f..b68345f12 100644 --- a/src/svg-ssr/Painter.ts +++ b/src/svg-ssr/Painter.ts @@ -2,9 +2,8 @@ * SVG Painter */ -import * as util from '../core/util'; import { - brush + brush, BrushScope } from './graphic'; import Displayable from '../graphic/Displayable'; import Storage from '../Storage'; @@ -12,6 +11,7 @@ import { PainterBase } from '../PainterBase'; import { createElement, createElementClose, createElementOpen } from './helper'; import { SVGNS, XLINKNS } from '../svg/core'; import { normalizeColor } from '../svg/shared'; +import { extend, keys, logError, map } from '../core/util'; function parseInt10(val: string) { return parseInt(val, 10); @@ -39,7 +39,7 @@ class SVGPainter implements PainterBase { constructor(root: HTMLElement, storage: Storage, opts: SVGPainterOption, zrId: number) { this.storage = storage; - this._opts = opts = util.extend({}, opts); + this._opts = opts = extend({}, opts); this.resize(opts.width, opts.height); } @@ -58,6 +58,14 @@ class SVGPainter implements PainterBase { const width = this._width + ''; const height = this._height + ''; + const scope: BrushScope = { + shadowCache: {}, + patternCache: {}, + gradientCache: {}, + clipPathCache: {}, + defs: {} + }; + let backgroundRect; if (bgColor && bgColor !== 'none') { const { color, opacity } = normalizeColor(bgColor); @@ -90,7 +98,10 @@ class SVGPainter implements PainterBase { backgroundRect, // Elements - this._paintList(list), + this._paintList(list, scope), + + // After paint list + createElement('defs', [], map(keys(scope.defs), (id) => scope.defs[id]).join('\n')), createElementClose('svg') ]; @@ -102,7 +113,7 @@ class SVGPainter implements PainterBase { this._backgroundColor = backgroundColor; } - _paintList(list: Displayable[]) { + _paintList(list: Displayable[], scope: BrushScope) { const listLen = list.length; const elStrs: string[] = []; @@ -110,7 +121,7 @@ class SVGPainter implements PainterBase { for (let i = 0; i < listLen; i++) { const displayable = list[i]; if (!displayable.invisible) { - const str = brush(displayable); + const str = brush(displayable, scope); str && elStrs.push(str); } } @@ -153,7 +164,7 @@ class SVGPainter implements PainterBase { // Not supported methods function createMethodNotSupport(method: string): any { return function () { - util.logError('In SVG mode painter not support method "' + method + '"'); + logError('In SVG mode painter not support method "' + method + '"'); }; } diff --git a/src/svg-ssr/graphic.ts b/src/svg-ssr/graphic.ts index 7e2c2bb84..cab8ad3ea 100644 --- a/src/svg-ssr/graphic.ts +++ b/src/svg-ssr/graphic.ts @@ -4,10 +4,15 @@ import { adjustTextY, + getIdURL, getMatrixStr, getShadowKey, hasShadow, isAroundZero, + isGradient, + isLinearGradient, + isRadialGradient, + normalizeColor, round4, TEXT_ALIGN_TO_ANCHOR } from '../svg/shared'; @@ -17,18 +22,23 @@ import { DEFAULT_FONT, getLineHeight } from '../contain/text'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import SVGPathRebuilder from '../svg/SVGPathRebuilder'; import mapStyleToAttrs from '../svg/mapStyleToAttrs'; -import { createElementClose, createElementOpen } from './helper'; +import { SVGAttrs, createElement } from './helper'; import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; -import { LinearGradientObject } from '../graphic/LinearGradient'; -import { RadialGradientObject } from '../graphic/RadialGradient'; import { PatternObject } from '../graphic/Pattern'; import Rect from '../graphic/shape/Rect'; -import { isArray, map } from '../core/util'; +import { isArray, logError, map, retrieve2 } from '../core/util'; import Polyline from '../graphic/shape/Polyline'; import Polygon from '../graphic/shape/Polygon'; +import { GradientObject } from '../graphic/Gradient'; -type Attrs = [string, string][] +export interface BrushScope { + shadowCache: Record + gradientCache: Record + patternCache: Record + clipPathCache: Record + defs: Record +} export interface SVGProxy { brush(el: T): string @@ -37,13 +47,19 @@ export interface SVGProxy { type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; -function setStyleAttrs(attrs: Attrs, style: AllStyleOption, el: Path | TSpan | ZRImage) { +function setStyleAttrs(attrs: SVGAttrs, style: AllStyleOption, el: Path | TSpan | ZRImage, scope: BrushScope) { + const {defs, gradientCache} = scope; + mapStyleToAttrs((key, val) => { - // TODO gradient - if (!val || (val as any).type !== 'linear' && (val as any).type !== 'radial') { + if ((key === 'fill' || key === 'stroke') && isGradient(val)) { + setGradient(style, attrs, key, defs, gradientCache); + } + else { attrs.push([key, val]); } }, style, el, false); + + setShadow(el, attrs, defs, scope.shadowCache); } function noRotateScale(m: MatrixArray) { @@ -57,7 +73,7 @@ function noTranslate(m: MatrixArray) { return isAroundZero(m[4]) && isAroundZero(m[5]); } -function setTransform(attrs: Attrs, m: MatrixArray) { +function setTransform(attrs: SVGAttrs, m: MatrixArray) { if (m && !(noTranslate(m) && noRotateScale(m))) { attrs.push([ 'transform', @@ -68,10 +84,10 @@ function setTransform(attrs: Attrs, m: MatrixArray) { } type ShapeMapDesc = (string | [string, string] | [string, string[]])[]; -type ConvertShapeToAttr = (shape: any, attrs: Attrs) => void; +type ConvertShapeToAttr = (shape: any, attrs: SVGAttrs) => void; type ShapeValidator = (shape: any) => boolean; -function converPolyShape(shape: Polygon['shape'], attrs: Attrs) { +function convertPolyShape(shape: Polygon['shape'], attrs: SVGAttrs) { const points = shape.points; const strArr = []; for (let i = 0; i < points.length; i++) { @@ -99,7 +115,7 @@ function createAttrsConvert(desc: ShapeMapDesc): ConvertShapeToAttr { const val = shape[item[0]]; if (val != null) { for (let k = 0; k < item[1].length; k++) { - attrs.push([item[1][k], round4(val) + '']); + attrs.push([item[1][k], round4(val)]); } } } @@ -115,16 +131,17 @@ const buitinShapesDef: Record = { return !isArray(r); }], circle: [createAttrsConvert(['cx', 'cy', 'r'])], - polyline: [converPolyShape, validatePolyShape], - polygon: [converPolyShape, validatePolyShape] + polyline: [convertPolyShape, validatePolyShape], + polygon: [convertPolyShape, validatePolyShape] + // Ignore line because it will be larger. }; -export function brushSVGPath(el: Path) { +export function brushSVGPath(el: Path, scope: BrushScope) { const style = el.style; const shape = el.shape; const builtinShpDef = buitinShapesDef[el.type]; - const attrs: Attrs = []; + const attrs: SVGAttrs = []; let svgElType = 'path'; // Using SVG builtin shapes if possible if (builtinShpDef && !(builtinShpDef[1] && !builtinShpDef[1](shape))) { @@ -150,16 +167,15 @@ export function brushSVGPath(el: Path) { attrs.push(['d', svgPathBuilder.getStr()]); } + + setTransform(attrs, el.transform); - setStyleAttrs(attrs, style, el); + setStyleAttrs(attrs, style, el, scope); - return [ - createElementOpen(svgElType, attrs), - createElementClose(svgElType) - ].join('\n'); + return createElement(svgElType, attrs); } -export function brushSVGImage(el: ZRImage) { +export function brushSVGImage(el: ZRImage, scope: BrushScope) { const style = el.style; let image = style.image; @@ -174,28 +190,25 @@ export function brushSVGImage(el: ZRImage) { const dw = style.width; const dh = style.height; - const attrs: Attrs = [ + const attrs: SVGAttrs = [ ['href', image as string], - ['width', dw + ''], - ['height', dh + ''] + ['width', dw], + ['height', dh] ]; if (x) { - attrs.push(['x', x + '']); + attrs.push(['x', x]); } if (y) { - attrs.push(['y', y + '']); + attrs.push(['y', y]); } setTransform(attrs, el.transform); - setStyleAttrs(attrs, style, el); + setStyleAttrs(attrs, style, el, scope); - return [ - createElementOpen('image', attrs), - createElementClose('image') - ].join('\n'); + return createElement('image', attrs); }; -export function brushSVGTSpan(el: TSpan) { +export function brushSVGTSpan(el: TSpan, scope: BrushScope) { const style = el.style; let text = style.text; @@ -215,7 +228,7 @@ export function brushSVGTSpan(el: TSpan) { const textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign as keyof typeof TEXT_ALIGN_TO_ANCHOR] || style.textAlign; - const attrs: Attrs = [ + const attrs: SVGAttrs = [ ['style', `font:${font}`], ['dominant-baseline', 'central'], ['text-anchor', textAlign] @@ -224,61 +237,159 @@ export function brushSVGTSpan(el: TSpan) { attrs.push(['xml:space', 'preserve']); } if (x) { - attrs.push(['x', x + '']); + attrs.push(['x', x]); } if (y) { - attrs.push(['y', y + '']); + attrs.push(['y', y]); } setTransform(attrs, el.transform); - setStyleAttrs(attrs, style, el); + setStyleAttrs(attrs, style, el, scope); - return [ - createElementOpen('image', attrs), - text, - createElementClose('image') - ].join('\n'); + return createElement('text', attrs, text); } -export function brush(el: Displayable) { +export function brush(el: Displayable, scope: BrushScope) { if (el instanceof Path) { - return brushSVGPath(el); + return brushSVGPath(el, scope); } else if (el instanceof ZRImage) { - return brushSVGImage(el); + return brushSVGImage(el, scope); } else if (el instanceof TSpan) { - return brushSVGTSpan(el); + return brushSVGTSpan(el, scope); } } -let shadowId = 0; -let gradientId = 0; -let patternId = 0; -let clipPathId = 0; -export function createShadow( +let shadowIdx = 0; +let gradientIdx = 0; +let patternIdx = 0; +let clipPathIdx = 0; +function setShadow( el: Displayable, + attrs: SVGAttrs, defs: Record, shadowCache: Record ) { const style = el.style; if (hasShadow(style)) { const shadowKey = getShadowKey(el); - if (shadowCache[shadowKey]) { - return shadowCache[shadowKey]; + let shadowId = shadowCache[shadowKey]; + if (!shadowId) { + const globalScale = el.getGlobalScale(); + const scaleX = globalScale[0]; + const scaleY = globalScale[1]; + + const offsetX = style.shadowOffsetX || 0; + const offsetY = style.shadowOffsetY || 0; + const blur = style.shadowBlur; + const {opacity, color} = normalizeColor(style.shadowColor); + const stdDx = blur / 2 / scaleX; + const stdDy = blur / 2 / scaleY; + const stdDeviation = stdDx + ' ' + stdDy; + // Use a simple prefix to reduce the size + shadowId = 's' + shadowIdx++; + defs[shadowId] = createElement( + 'filter', [ + ['id', shadowId], + ['x', '-100%'], + ['y', '-100%'], + ['width', '300%'], + ['height', '300%'] + ], + createElement('feDropShadow', [ + ['dx', offsetX / scaleX], + ['dy', offsetY / scaleY], + ['stdDeviation', stdDeviation], + ['flood-color', color], + ['flood-opacity', opacity] + ]) + ); + shadowCache[shadowKey] = shadowId; } - // const shadowStr = + attrs.push(['filter', getIdURL(shadowId)]); } } -export function createGradient( - gradient: LinearGradientObject | RadialGradientObject, +function setGradient( + style: PathStyleProps, + attrs: SVGAttrs, + target: 'fill' | 'stroke', defs: Record, gradientCache: Record ) { + const val = style[target] as GradientObject; + let gradientTag; + let gradientAttrs: SVGAttrs = [ + [ + 'gradientUnits', val.global + ? 'userSpaceOnUse' // x1, x2, y1, y2 in range of 0 to canvas width or height + : 'objectBoundingBox' // x1, x2, y1, y2 in range of 0 to 1] + ] + ]; + if (isLinearGradient(val)) { + gradientTag = 'linearGradient'; + gradientAttrs.push( + ['x1', val.x], + ['y1', val.y], + ['x2', val.x2], + ['y2', val.y2] + ); + } + else if (isRadialGradient(val)) { + gradientTag = 'radialGradient'; + gradientAttrs.push( + ['cx', retrieve2(val.x, 0.5)], + ['cy', retrieve2(val.y, 0.5)], + ['r', retrieve2(val.r, 0.5)] + ); + } + else { + logError('Illegal gradient type.'); + return; + } + const colors = val.colorStops; + + const colorStops = []; + for (let i = 0, len = colors.length; i < len; ++i) { + const offset = round4(colors[i].offset) * 100 + '%'; + + const stopColor = colors[i].color; + // Fix Safari bug that stop-color not recognizing alpha #9014 + const {color, opacity} = normalizeColor(stopColor); + + const stopsAttrs: SVGAttrs = [['offset', offset]]; + // stop-color cannot be color, since: + // The opacity value used for the gradient calculation is the + // *product* of the value of stop-opacity and the opacity of the + // value of stop-color. + // See https://www.w3.org/TR/SVG2/pservers.html#StopOpacityProperty + stopsAttrs.push(['stop-color', color]); + if (opacity < 1) { + stopsAttrs.push(['stop-opacity', opacity]); + } + colorStops.push( + createElement('stop', stopsAttrs) + ); + } + + // Use the whole html as cache key. + const gradientKey = createElement(gradientTag, gradientAttrs, colorStops.join('')); + let gradientId = gradientCache[gradientKey]; + if (!gradientId) { + gradientId = 'g' + gradientIdx++; + gradientCache[gradientKey] = gradientId; + + gradientAttrs.push(['id', gradientId]); + defs[gradientId] = createElement(gradientTag, gradientAttrs, colorStops.join('\n')); + } + + attrs.push( + [target, getIdURL(gradientId)] + ); } -export function createPattern( +function createPattern( pattern: PatternObject, defs: Record, patternCache: Record @@ -286,7 +397,7 @@ export function createPattern( } -export function createAnimation() { +function createAnimation() { } diff --git a/src/svg-ssr/helper.ts b/src/svg-ssr/helper.ts index 87198b76b..ac586cbde 100644 --- a/src/svg-ssr/helper.ts +++ b/src/svg-ssr/helper.ts @@ -1,7 +1,12 @@ -export function createElement(name: string, attrs?: ([string, string] | [string])[]) { - return createElementOpen(name, attrs) + createElementClose(name); + +export type SVGAttrs = [string, string | number | undefined][]; + +export function createElement(name: string, attrs?: SVGAttrs, children?: string) { + return createElementOpen(name, attrs) + + (children ? `\n${children}\n` : '') + + createElementClose(name); } -export function createElementOpen(name: string, attrs?: ([string, string] | [string])[]) { +export function createElementOpen(name: string, attrs?: SVGAttrs) { const attrsStr: string[] = []; if (attrs) { for (let i = 0; i < attrs.length; i++) { diff --git a/src/svg/helper/ClippathManager.ts b/src/svg/helper/ClippathManager.ts index e997a8e6a..8da9deb4e 100644 --- a/src/svg/helper/ClippathManager.ts +++ b/src/svg/helper/ClippathManager.ts @@ -10,7 +10,8 @@ import Path from '../../graphic/Path'; import {SVGProxy} from '../graphic'; import { Dictionary } from '../../core/types'; import { isClipPathChanged } from '../../canvas/helper'; -import { getClipPathsKey } from '../shared'; +import { getClipPathsKey, getIdURL } from '../shared'; +import { createElement } from '../core'; type PathExtended = Path & { _dom: SVGElement @@ -59,7 +60,7 @@ export default class ClippathManager extends Definable { } return this._refGroups[clipPathKey] - || (this._refGroups[clipPathKey] = this.createElement('g')); + || (this._refGroups[clipPathKey] = createElement('g')); } /** @@ -105,7 +106,7 @@ export default class ClippathManager extends Definable { // New id = 'zr' + this._zrId + '-clip-' + this.nextId; ++this.nextId; - clipPathEl = this.createElement('clipPath'); + clipPathEl = createElement('clipPath'); clipPathEl.setAttribute('id', id); defs.appendChild(clipPathEl); @@ -121,7 +122,7 @@ export default class ClippathManager extends Definable { clipPathEl.innerHTML = ''; clipPathEl.appendChild(pathEl); - parentEl.setAttribute('clip-path', 'url(#' + id + ')'); + parentEl.setAttribute('clip-path', getIdURL(id)); if (clipPaths.length > 1) { // Make the other clipPaths recursively diff --git a/src/svg/helper/Definable.ts b/src/svg/helper/Definable.ts index fe8d86df7..8ed130d19 100644 --- a/src/svg/helper/Definable.ts +++ b/src/svg/helper/Definable.ts @@ -51,9 +51,6 @@ export default class Definable { } } - createElement = createElement - - /** * Get the tag for svgRoot; optionally creates one if not exists. * @@ -68,7 +65,7 @@ export default class Definable { // Not exist if (isForceCreating) { let defs = svgRoot.insertBefore( - this.createElement('defs'), // Create new tag + createElement('defs'), // Create new tag svgRoot.firstChild // Insert in the front of svg ) as SVGDefsElement; if (!defs.contains) { diff --git a/src/svg/helper/GradientManager.ts b/src/svg/helper/GradientManager.ts index 93cae2064..102d24d65 100644 --- a/src/svg/helper/GradientManager.ts +++ b/src/svg/helper/GradientManager.ts @@ -5,26 +5,10 @@ import Definable from './Definable'; import * as zrUtil from '../../core/util'; -import * as colorTool from '../../tool/color'; import Displayable from '../../graphic/Displayable'; import { GradientObject } from '../../graphic/Gradient'; -import { LinearGradientObject } from '../../graphic/LinearGradient'; -import { RadialGradientObject } from '../../graphic/RadialGradient'; - -function isLinearGradient(value: GradientObject): value is LinearGradientObject { - return value.type === 'linear'; -} - -function isRadialGradient(value: GradientObject): value is RadialGradientObject { - return value.type === 'radial'; -} - -function isGradient(value: GradientObject | string): value is GradientObject { - return value && ( - (value as GradientObject).type === 'linear' - || (value as GradientObject).type === 'radial' - ); -} +import { getIdURL, isGradient, isLinearGradient, isRadialGradient, normalizeColor, round4 } from '../shared'; +import { createElement } from '../core'; type GradientObjectExtended = GradientObject & { @@ -80,8 +64,7 @@ export default class GradientManager extends Definable { that.markUsed(displayable); - const id = dom.getAttribute('id'); - svgElement.setAttribute(fillOrStroke, 'url(#' + id + ')'); + svgElement.setAttribute(fillOrStroke, getIdURL(dom.getAttribute('id'))); } }); } @@ -96,10 +79,10 @@ export default class GradientManager extends Definable { add(gradient: GradientObject): SVGElement { let dom; if (isLinearGradient(gradient)) { - dom = this.createElement('linearGradient'); + dom = createElement('linearGradient'); } else if (isRadialGradient(gradient)) { - dom = this.createElement('radialGradient'); + dom = createElement('radialGradient'); } else { zrUtil.logError('Illegal gradient type.'); @@ -164,29 +147,26 @@ export default class GradientManager extends Definable { */ updateDom(gradient: GradientObject, dom: SVGElement) { if (isLinearGradient(gradient)) { - dom.setAttribute('x1', gradient.x + ''); - dom.setAttribute('y1', gradient.y + ''); - dom.setAttribute('x2', gradient.x2 + ''); - dom.setAttribute('y2', gradient.y2 + ''); + dom.setAttribute('x1', gradient.x as any); + dom.setAttribute('y1', gradient.y as any); + dom.setAttribute('x2', gradient.x2 as any); + dom.setAttribute('y2', gradient.y2 as any); } else if (isRadialGradient(gradient)) { - dom.setAttribute('cx', gradient.x + ''); - dom.setAttribute('cy', gradient.y + ''); - dom.setAttribute('r', gradient.r + ''); + dom.setAttribute('cx', gradient.x as any); + dom.setAttribute('cy', gradient.y as any); + dom.setAttribute('r', gradient.r as any); } else { zrUtil.logError('Illegal gradient type.'); return; } - if (gradient.global) { - // x1, x2, y1, y2 in range of 0 to canvas width or height - dom.setAttribute('gradientUnits', 'userSpaceOnUse'); - } - else { - // x1, x2, y1, y2 in range of 0 to 1 - dom.setAttribute('gradientUnits', 'objectBoundingBox'); - } + dom.setAttribute('gradientUnits', + gradient.global + ? 'userSpaceOnUse' // x1, x2, y1, y2 in range of 0 to canvas width or height + : 'objectBoundingBox' // x1, x2, y1, y2 in range of 0 to 1 + ); // Remove color stops if exists dom.innerHTML = ''; @@ -194,25 +174,20 @@ export default class GradientManager extends Definable { // Add color stops const colors = gradient.colorStops; for (let i = 0, len = colors.length; i < len; ++i) { - const stop = this.createElement('stop'); - stop.setAttribute('offset', colors[i].offset * 100 + '%'); - - const color = colors[i].color; - if (color.indexOf('rgba') > -1) { - // Fix Safari bug that stop-color not recognizing alpha #9014 - const opacity = colorTool.parse(color)[3]; - const hex = colorTool.toHex(color); - - // stop-color cannot be color, since: - // The opacity value used for the gradient calculation is the - // *product* of the value of stop-opacity and the opacity of the - // value of stop-color. - // See https://www.w3.org/TR/SVG2/pservers.html#StopOpacityProperty - stop.setAttribute('stop-color', '#' + hex); - stop.setAttribute('stop-opacity', opacity + ''); - } - else { - stop.setAttribute('stop-color', colors[i].color); + const stop = createElement('stop'); + stop.setAttribute('offset', round4(colors[i].offset) * 100 + '%'); + + const stopColor = colors[i].color; + // Fix Safari bug that stop-color not recognizing alpha #9014 + const {color, opacity} = normalizeColor(stopColor); + // stop-color cannot be color, since: + // The opacity value used for the gradient calculation is the + // *product* of the value of stop-opacity and the opacity of the + // value of stop-color. + // See https://www.w3.org/TR/SVG2/pservers.html#StopOpacityProperty + stop.setAttribute('stop-color', color); + if (opacity < 1) { + stop.setAttribute('stop-opacity', opacity as any); } dom.appendChild(stop); diff --git a/src/svg/helper/PatternManager.ts b/src/svg/helper/PatternManager.ts index 279d1f163..8207af0f6 100644 --- a/src/svg/helper/PatternManager.ts +++ b/src/svg/helper/PatternManager.ts @@ -9,7 +9,8 @@ import Displayable from '../../graphic/Displayable'; import {PatternObject, ImagePatternObject, SVGPatternObject} from '../../graphic/Pattern'; import {createOrUpdateImage} from '../../graphic/helper/image'; import WeakMap from '../../core/WeakMap'; -import { isPattern } from '../shared'; +import { getIdURL, isPattern } from '../shared'; +import { createElement } from '../core'; const patternDomMap = new WeakMap(); @@ -60,8 +61,7 @@ export default class PatternManager extends Definable { that.markUsed(displayable); - const id = dom.getAttribute('id'); - svgElement.setAttribute(fillOrStroke, 'url(#' + id + ')'); + svgElement.setAttribute(fillOrStroke, getIdURL(dom.getAttribute('id'))); } }); } @@ -78,7 +78,7 @@ export default class PatternManager extends Definable { return; } - let dom = this.createElement('pattern'); + let dom = createElement('pattern'); pattern.id = pattern.id == null ? this.nextId++ : pattern.id; dom.setAttribute('id', 'zr' + this._zrId @@ -147,7 +147,7 @@ export default class PatternManager extends Definable { } else if ((pattern as ImagePatternObject).image) { // Create - img = this.createElement('image'); + img = createElement('image'); } if (img) { diff --git a/src/svg/helper/ShadowManager.ts b/src/svg/helper/ShadowManager.ts index b846f3203..3d1f95278 100644 --- a/src/svg/helper/ShadowManager.ts +++ b/src/svg/helper/ShadowManager.ts @@ -6,7 +6,8 @@ import Definable from './Definable'; import Displayable from '../../graphic/Displayable'; import { Dictionary } from '../../core/types'; -import { getShadowKey, hasShadow, normalizeColor } from '../shared'; +import { getIdURL, getShadowKey, hasShadow, normalizeColor } from '../shared'; +import { createElement } from '../core'; type DisplayableExtended = Displayable & { _shadowDom: SVGElement @@ -33,9 +34,9 @@ export default class ShadowManager extends Definable { private _getFromPool(): SVGFilterElement { let shadowDom = this._shadowDomPool.pop(); // Try to get one from trash. if (!shadowDom) { - shadowDom = this.createElement('filter') as SVGFilterElement; + shadowDom = createElement('filter') as SVGFilterElement; shadowDom.setAttribute('id', 'zr' + this._zrId + '-shadow-' + this.nextId++); - const domChild = this.createElement('feDropShadow'); + const domChild = createElement('feDropShadow'); shadowDom.appendChild(domChild); this.addDom(shadowDom); } @@ -95,9 +96,9 @@ export default class ShadowManager extends Definable { } // TODO: textBoxShadowBlur is not supported yet - let offsetX = style.shadowOffsetX || 0; - let offsetY = style.shadowOffsetY || 0; - let blur = style.shadowBlur; + const offsetX = style.shadowOffsetX || 0; + const offsetY = style.shadowOffsetY || 0; + const blur = style.shadowBlur; const normalizedColor = normalizeColor(style.shadowColor); domChild.setAttribute('dx', offsetX / scaleX + ''); @@ -122,8 +123,7 @@ export default class ShadowManager extends Definable { // dom instances for the same shadow element (displayable as DisplayableExtended)._shadowDom = shadowDom; - const id = shadowDom.getAttribute('id'); - svgElement.setAttribute('filter', 'url(#' + id + ')'); + svgElement.setAttribute('filter', getIdURL(shadowDom.getAttribute('id'))); } removeUnused() { diff --git a/src/svg/mapStyleToAttrs.ts b/src/svg/mapStyleToAttrs.ts index b1bd10499..a3204da17 100644 --- a/src/svg/mapStyleToAttrs.ts +++ b/src/svg/mapStyleToAttrs.ts @@ -1,5 +1,5 @@ -import Path, { PathStyleProps } from '../graphic/Path'; +import Path, { DEFAULT_PATH_STYLE, PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import { normalizeLineDash } from '../graphic/helper/dashStyle'; @@ -21,8 +21,11 @@ function pathHasStroke(style: AllStyleOption): style is PathStyleProps { return stroke != null && stroke !== NONE; } +const strokeProps = ['lineCap', 'miterLimit', 'lineJoin'] as const; +const svgStrokeProps = map(strokeProps, prop => `stroke-${prop.toLowerCase()}`); + export default function mapStyleToAttrs( - updateAttr: (key: string, val: string) => void, + updateAttr: (key: string, val: string | number) => void, style: AllStyleOption, el: Path | TSpan | ZRImage, /** @@ -59,7 +62,7 @@ export default function mapStyleToAttrs( const strokeScale = style.strokeNoScale ? (el as Path).getLineScale() : 1; - const strokeWidth = (strokeScale ? (style.lineWidth || 0) / strokeScale : 0); + const strokeWidth = (strokeScale ? (style.lineWidth || 0) / strokeScale : 0); const strokeOpacity = style.strokeOpacity != null ? style.strokeOpacity * stroke.opacity * opacity : stroke.opacity * opacity; @@ -99,9 +102,14 @@ export default function mapStyleToAttrs( } // PENDING reset - style.lineCap && updateAttr('stroke-linecap', style.lineCap); - style.lineJoin && updateAttr('stroke-linejoin', style.lineJoin); - style.miterLimit && updateAttr('stroke-miterlimit', style.miterLimit + ''); + for (let i = 0; i < strokeProps.length; i++) { + const propName = strokeProps[i]; + if (forceUpdate || style[propName] !== DEFAULT_PATH_STYLE[propName]) { + const val = style[propName] || DEFAULT_PATH_STYLE[propName]; + // TODO reset + val && updateAttr(svgStrokeProps[i], val); + } + } } else if (forceUpdate) { updateAttr('stroke', NONE); diff --git a/src/svg/shared.ts b/src/svg/shared.ts index 409df1e0e..1a0118d20 100644 --- a/src/svg/shared.ts +++ b/src/svg/shared.ts @@ -2,8 +2,11 @@ import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; +import { GradientObject } from '../graphic/Gradient'; +import { LinearGradientObject } from '../graphic/LinearGradient'; import Path from '../graphic/Path'; import { ImagePatternObject, PatternObject, SVGPatternObject } from '../graphic/Pattern'; +import { RadialGradientObject } from '../graphic/RadialGradient'; import { parse } from '../tool/color'; import { mathRound } from './core'; @@ -15,6 +18,7 @@ export function normalizeColor(color: string): { color: string; opacity: number; else if (typeof color === 'string' && color.indexOf('rgba') > -1) { const arr = parse(color); if (arr) { + // TODO use hex? color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')'; opacity = arr[3]; } @@ -101,3 +105,22 @@ export function getClipPathsKey(clipPaths: Path[]) { export function isPattern(value: PatternObject | string): value is PatternObject { return value && (!!(value as ImagePatternObject).image || !!(value as SVGPatternObject).svgElement); } + +export function isLinearGradient(value: GradientObject): value is LinearGradientObject { + return value.type === 'linear'; +} + +export function isRadialGradient(value: GradientObject): value is RadialGradientObject { + return value.type === 'radial'; +} + +export function isGradient(value: any): value is GradientObject { + return value && ( + (value as GradientObject).type === 'linear' + || (value as GradientObject).type === 'radial' + ); +} + +export function getIdURL(id: string) { + return `url(#${id})`; +} \ No newline at end of file diff --git a/test/svg-ssr.html b/test/svg-ssr.html index f45b507b9..e85f8a106 100644 --- a/test/svg-ssr.html +++ b/test/svg-ssr.html @@ -17,16 +17,32 @@ height: 800 }); - for (let i = 0; i < 100; i++) { - for (let k = 0; k < 100; k++) { + for (let i = 0; i < 10; i++) { + for (let k = 0; k < 10; k++) { var circle = new zrender.Circle({ shape: { - cx: i * 8 + 4, - cy: k * 8 + 4, - r: 3 + cx: i * 80 + 40, + cy: k * 80 + 40, + r: 30 }, style: { - fill: 'red' + fill: { + type: 'radial', + x: 0.5, + y: 0.5, + r: 0.5, + colorStops: [{ + offset: 0, + color: 'red' + }, { + offset: 1, + color: 'blue' + }] + }, + stroke: 'blue', + lineWidth: 2, + shadowBlur: 10, + shadowColor: 'rgba(0, 0, 0, 0.5)' } }); zr.add(circle); From 911a205e61d10694fecb8d81021e150389c2e9b1 Mon Sep 17 00:00:00 2001 From: pissang Date: Sun, 26 Sep 2021 14:51:07 +0800 Subject: [PATCH 006/148] wip(svg-ssr): add pattern. upgrade ts --- package-lock.json | 6 +- package.json | 2 +- src/animation/requestAnimationFrame.ts | 1 + src/canvas/Layer.ts | 16 +--- src/canvas/Painter.ts | 7 +- src/core/util.ts | 10 +++ src/graphic/Pattern.ts | 17 +++- src/svg-ssr/graphic.ts | 77 +++++++++++++--- src/svg-ssr/helper.ts | 2 +- src/svg/SVGPathRebuilder.ts | 7 +- src/svg/graphic.ts | 4 +- src/svg/helper/PatternManager.ts | 46 +++++----- src/svg/shared.ts | 10 ++- test/svg-ssr.html | 120 ++++++++++++++++++------- 14 files changed, 227 insertions(+), 98 deletions(-) diff --git a/package-lock.json b/package-lock.json index 36ab4dc2d..28a23e97d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5816,9 +5816,9 @@ } }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index 4a603db66..4d718457d 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "rollup-plugin-typescript2": "^0.25.3", "rollup-plugin-uglify": "^6.0.4", "ts-jest": "^25.2.0", - "typescript": "4.3.5", + "typescript": "^4.4.3", "uglify-js": "^3.10.0" } } diff --git a/src/animation/requestAnimationFrame.ts b/src/animation/requestAnimationFrame.ts index 11b6c728d..829e7161f 100644 --- a/src/animation/requestAnimationFrame.ts +++ b/src/animation/requestAnimationFrame.ts @@ -9,6 +9,7 @@ requestAnimationFrame = ( // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809 || ((window as any).msRequestAnimationFrame && (window as any).msRequestAnimationFrame.bind(window)) || (window as any).mozRequestAnimationFrame + // @ts-ignore || window.webkitRequestAnimationFrame ) ) || function (func: Parameters[0]): number { diff --git a/src/canvas/Layer.ts b/src/canvas/Layer.ts index 2e76a71e1..6900db7a0 100644 --- a/src/canvas/Layer.ts +++ b/src/canvas/Layer.ts @@ -12,10 +12,6 @@ import Displayable from '../graphic/Displayable'; import BoundingRect from '../core/BoundingRect'; import { REDRAW_BIT } from '../graphic/constants'; -function returnFalse() { - return false; -} - function createDom(id: string, painter: CanvasPainter, dpr: number) { const newDom = util.createCanvas(); const width = painter.getWidth(); @@ -127,23 +123,15 @@ export default class Layer extends Eventful { const domStyle = dom.style; if (domStyle) { // Not in node - dom.onselectstart = returnFalse; // 避免页面选中的尴尬 - domStyle.webkitUserSelect = 'none'; - domStyle.userSelect = 'none'; - domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)'; - (domStyle as any)['-webkit-touch-callout'] = 'none'; + util.disableUserSelect(dom); + dom.onselectstart = () => false; domStyle.padding = '0'; domStyle.margin = '0'; domStyle.borderWidth = '0'; } - this.domBack = null; - this.ctxBack = null; - this.painter = painter; - this.config = null; - this.dpr = dpr; } diff --git a/src/canvas/Painter.ts b/src/canvas/Painter.ts index 5f69a4427..60dc8ba2b 100644 --- a/src/canvas/Painter.ts +++ b/src/canvas/Painter.ts @@ -141,11 +141,8 @@ export default class CanvasPainter implements PainterBase { const rootStyle = root.style; if (rootStyle) { - rootStyle.webkitTapHighlightColor = 'transparent'; - rootStyle.webkitUserSelect = 'none'; - rootStyle.userSelect = 'none'; - (rootStyle as any)['-webkit-touch-callout'] = 'none'; - + // @ts-ignore + util.disableUserSelect(root); root.innerHTML = ''; } diff --git a/src/core/util.ts b/src/core/util.ts index 8b39f068a..5431eb883 100644 --- a/src/core/util.ts +++ b/src/core/util.ts @@ -753,6 +753,16 @@ export function createObject(proto?: object, properties?: T): T { return obj; } + +export function disableUserSelect(dom: HTMLElement) { + const domStyle = dom.style; + domStyle.webkitUserSelect = 'none'; + domStyle.userSelect = 'none'; + // @ts-ignore + domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)'; + (domStyle as any)['-webkit-touch-callout'] = 'none'; +} + export function hasOwn(own: object, prop: string): boolean { return own.hasOwnProperty(prop); } diff --git a/src/graphic/Pattern.ts b/src/graphic/Pattern.ts index 188b6ce21..e99fe472e 100644 --- a/src/graphic/Pattern.ts +++ b/src/graphic/Pattern.ts @@ -17,6 +17,15 @@ export interface PatternObjectBase { export interface ImagePatternObject extends PatternObjectBase { image: ImageLike | string repeat?: ImagePatternRepeat + + /** + * Width and height of image. + * `imageWidth` and `imageHeight` are only used in svg-ssr renderer. + * Because we can't get the size of image in svg-ssr renderer. + * They need to be give explictly. + */ + imageWidth?: number + imageHeight?: number } export interface InnerImagePatternObject extends ImagePatternObject { @@ -28,8 +37,10 @@ export interface SVGPatternObject extends PatternObjectBase { /** * svg element can only be used in svg renderer currently. * svgWidth, svgHeight defines width and height used for pattern. + * + * Will be string if using SSR rendering. */ - svgElement?: SVGElement + svgElement?: SVGElement | string svgWidth?: number svgHeight?: number } @@ -43,8 +54,10 @@ class Pattern { image: ImageLike | string /** * svg element can only be used in svg renderer currently. + * + * Will be string if using SSR rendering. */ - svgElement: SVGElement + svgElement: SVGElement | string repeat: ImagePatternRepeat diff --git a/src/svg-ssr/graphic.ts b/src/svg-ssr/graphic.ts index cab8ad3ea..6c14fc221 100644 --- a/src/svg-ssr/graphic.ts +++ b/src/svg-ssr/graphic.ts @@ -10,7 +10,9 @@ import { hasShadow, isAroundZero, isGradient, + isImagePattern, isLinearGradient, + isPattern, isRadialGradient, normalizeColor, round4, @@ -25,12 +27,12 @@ import mapStyleToAttrs from '../svg/mapStyleToAttrs'; import { SVGAttrs, createElement } from './helper'; import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; -import { PatternObject } from '../graphic/Pattern'; import Rect from '../graphic/shape/Rect'; -import { isArray, logError, map, retrieve2 } from '../core/util'; +import { assert, isArray, logError, map, retrieve2 } from '../core/util'; import Polyline from '../graphic/shape/Polyline'; import Polygon from '../graphic/shape/Polygon'; import { GradientObject } from '../graphic/Gradient'; +import { ImagePatternObject, SVGPatternObject } from '../export'; export interface BrushScope { shadowCache: Record @@ -48,11 +50,15 @@ export interface SVGProxy { type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; function setStyleAttrs(attrs: SVGAttrs, style: AllStyleOption, el: Path | TSpan | ZRImage, scope: BrushScope) { - const {defs, gradientCache} = scope; + const {defs} = scope; mapStyleToAttrs((key, val) => { - if ((key === 'fill' || key === 'stroke') && isGradient(val)) { - setGradient(style, attrs, key, defs, gradientCache); + const isFillStroke = key === 'fill' || key === 'stroke'; + if (isFillStroke && isGradient(val)) { + setGradient(style, attrs, key, defs, scope.gradientCache); + } + else if (isFillStroke && isPattern(val)) { + setPattern(style, attrs, key, defs, scope.patternCache); } else { attrs.push([key, val]); @@ -374,27 +380,74 @@ function setGradient( } // Use the whole html as cache key. - const gradientKey = createElement(gradientTag, gradientAttrs, colorStops.join('')); + const colorStopsStr = colorStops.join('\n'); + const gradientKey = createElement(gradientTag, gradientAttrs, colorStopsStr); let gradientId = gradientCache[gradientKey]; if (!gradientId) { gradientId = 'g' + gradientIdx++; gradientCache[gradientKey] = gradientId; gradientAttrs.push(['id', gradientId]); - defs[gradientId] = createElement(gradientTag, gradientAttrs, colorStops.join('\n')); + defs[gradientId] = createElement(gradientTag, gradientAttrs, colorStopsStr); } - attrs.push( - [target, getIdURL(gradientId)] - ); + attrs.push([target, getIdURL(gradientId)]); } -function createPattern( - pattern: PatternObject, +function setPattern( + style: PathStyleProps, + attrs: SVGAttrs, + target: 'fill' | 'stroke', defs: Record, patternCache: Record ) { + const val = style[target] as ImagePatternObject | SVGPatternObject; + const patternAttrs: SVGAttrs = [ + ['patternUnits', 'userSpaceOnUse'] + ]; + let children: string; + if (isImagePattern(val)) { + const errMsg = 'Image width/height must been given explictly in svg-ssr renderer.'; + const imageWidth = val.imageWidth; + const imageHeight = val.imageHeight; + assert(imageWidth, errMsg); + assert(imageHeight, errMsg); + + // TODO Only support string url + children = createElement('image', [ + ['href', val.image as string], + ['width', imageWidth], + ['height', imageHeight] + ]); + patternAttrs.push( + ['width', imageWidth], + ['height', imageHeight] + ); + } + else if (typeof val.svgElement === 'string') { // Only string supported in SSR. + // image can only be string + children = val.svgElement; + patternAttrs.push( + ['width', val.svgWidth], + ['height', val.svgHeight] + ); + } + if (!children) { + return; + } + + // Use the whole html as cache key. + const patternTag = 'pattern'; + const patternKey = createElement(patternTag, patternAttrs, children); + let patternId = patternCache[patternKey]; + if (!patternId) { + patternId = 'p' + patternIdx++; + patternCache[patternKey] = patternId; + patternAttrs.push(['id', patternId]); + defs[patternId] = createElement(patternTag, patternAttrs, children); + } + attrs.push([target, getIdURL(patternId)]); } function createAnimation() { diff --git a/src/svg-ssr/helper.ts b/src/svg-ssr/helper.ts index ac586cbde..32e442c53 100644 --- a/src/svg-ssr/helper.ts +++ b/src/svg-ssr/helper.ts @@ -11,7 +11,7 @@ export function createElementOpen(name: string, attrs?: SVGAttrs) { if (attrs) { for (let i = 0; i < attrs.length; i++) { let part = attrs[i][0]; - if (attrs[i][1]) { + if (attrs[i][1] != null) { part += `="${attrs[i][1]}"`; } attrsStr.push(part); diff --git a/src/svg/SVGPathRebuilder.ts b/src/svg/SVGPathRebuilder.ts index 753ba603c..1d7c6d1bf 100644 --- a/src/svg/SVGPathRebuilder.ts +++ b/src/svg/SVGPathRebuilder.ts @@ -118,20 +118,21 @@ export default class SVGPathRebuilder implements PathRebuilder { } _add(cmd: string, a?: number, b?: number, c?: number, d?: number, e?: number, f?: number, g?: number, h?: number) { - this._d.push(cmd); + const vals = []; for (let i = 1; i < arguments.length; i++) { const val = arguments[i]; if (isNaN(val)) { this._invalid = true; return; } - this._d.push(round4(val)); + vals.push(round4(val)); } + this._d.push(cmd + vals.join(' ')); this._start = cmd === 'Z'; } generateStr() { - this._str = this._invalid ? '' : this._d.join(' '); + this._str = this._invalid ? '' : this._d.join(''); this._d = []; } getStr() { diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 02bfa2a88..ed9059654 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -24,10 +24,10 @@ function setTransform(svgEl: SVGElement, m: matrix.MatrixArray) { } } -function attr(el: SVGElement, key: string, val: string) { +function attr(el: SVGElement, key: string, val: string | number) { if (!val || (val as any).type !== 'linear' && (val as any).type !== 'radial') { // Don't set attribute for gradient, since it need new dom nodes - el.setAttribute(key, val); + el.setAttribute(key, val as any); } } diff --git a/src/svg/helper/PatternManager.ts b/src/svg/helper/PatternManager.ts index 8207af0f6..ed79ff03a 100644 --- a/src/svg/helper/PatternManager.ts +++ b/src/svg/helper/PatternManager.ts @@ -9,7 +9,7 @@ import Displayable from '../../graphic/Displayable'; import {PatternObject, ImagePatternObject, SVGPatternObject} from '../../graphic/Pattern'; import {createOrUpdateImage} from '../../graphic/helper/image'; import WeakMap from '../../core/WeakMap'; -import { getIdURL, isPattern } from '../shared'; +import { getIdURL, isPattern, isSVGPattern } from '../shared'; import { createElement } from '../core'; const patternDomMap = new WeakMap(); @@ -84,8 +84,6 @@ export default class PatternManager extends Definable { dom.setAttribute('id', 'zr' + this._zrId + '-pattern-' + pattern.id); - dom.setAttribute('x', '0'); - dom.setAttribute('y', '0'); dom.setAttribute('patternUnits', 'userSpaceOnUse'); this.updateDom(pattern, dom); @@ -120,22 +118,27 @@ export default class PatternManager extends Definable { * @param patternDom DOM to update */ updateDom(pattern: PatternObject, patternDom: SVGElement) { - const svgElement = (pattern as SVGPatternObject).svgElement; - - if (svgElement instanceof SVGElement) { - if (svgElement.parentNode !== patternDom) { - patternDom.innerHTML = ''; - patternDom.appendChild(svgElement); + if (isSVGPattern(pattern)) { + const svgElement = pattern.svgElement; + const isStringSVG = typeof svgElement === 'string'; + if (isStringSVG || svgElement.parentNode !== patternDom) { + if (isStringSVG) { + patternDom.innerHTML = svgElement; + } + else { + patternDom.innerHTML = ''; + patternDom.appendChild(svgElement); + } - patternDom.setAttribute('width', (pattern as SVGPatternObject).svgWidth + ''); - patternDom.setAttribute('height', (pattern as SVGPatternObject).svgHeight + ''); + patternDom.setAttribute('width', pattern.svgWidth as any); + patternDom.setAttribute('height', pattern.svgHeight as any); } } else { let img: SVGElement; const prevImage = patternDom.getElementsByTagName('image'); if (prevImage.length) { - if ((pattern as ImagePatternObject).image) { + if (pattern.image) { // Update img = prevImage[0]; } @@ -145,14 +148,14 @@ export default class PatternManager extends Definable { return; } } - else if ((pattern as ImagePatternObject).image) { + else if (pattern.image) { // Create img = createElement('image'); } if (img) { let imageSrc; - const patternImage = (pattern as ImagePatternObject).image; + const patternImage = pattern.image; if (typeof patternImage === 'string') { imageSrc = patternImage; } @@ -165,21 +168,20 @@ export default class PatternManager extends Definable { if (imageSrc) { img.setAttribute('href', imageSrc); - img.setAttribute('x', '0'); - img.setAttribute('y', '0'); // No need to re-render so dirty is empty const hostEl = { dirty: () => {} }; - const createdImage = createOrUpdateImage(imageSrc, img as any, hostEl, img => { - patternDom.setAttribute('width', img.width + ''); - patternDom.setAttribute('height', img.height + ''); - }); + const updateSize = (img: HTMLImageElement) => { + patternDom.setAttribute('width', img.width as any); + patternDom.setAttribute('height', img.height as any); + }; + + const createdImage = createOrUpdateImage(imageSrc, img as any, hostEl, updateSize); if (createdImage && createdImage.width && createdImage.height) { // Loaded before - patternDom.setAttribute('width', createdImage.width + ''); - patternDom.setAttribute('height', createdImage.height + ''); + updateSize(createdImage as HTMLImageElement); } patternDom.appendChild(img); diff --git a/src/svg/shared.ts b/src/svg/shared.ts index 1a0118d20..4e26fa362 100644 --- a/src/svg/shared.ts +++ b/src/svg/shared.ts @@ -102,8 +102,14 @@ export function getClipPathsKey(clipPaths: Path[]) { return key.join(','); } -export function isPattern(value: PatternObject | string): value is PatternObject { - return value && (!!(value as ImagePatternObject).image || !!(value as SVGPatternObject).svgElement); +export function isImagePattern(value: any): value is ImagePatternObject { + return value && (!!(value as ImagePatternObject).image); +} +export function isSVGPattern(value: any): value is SVGPatternObject { + return value && (!!(value as SVGPatternObject).svgElement); +} +export function isPattern(value: any): value is PatternObject { + return isImagePattern(value) || isSVGPattern(value); } export function isLinearGradient(value: GradientObject): value is LinearGradientObject { diff --git a/test/svg-ssr.html b/test/svg-ssr.html index e85f8a106..3dd3fbc25 100644 --- a/test/svg-ssr.html +++ b/test/svg-ssr.html @@ -13,41 +13,99 @@ + + +
+ + + + \ No newline at end of file diff --git a/test/svg-ssr.html b/test/svg-ssr.html index f411f2a8f..8e7f6ebd4 100644 --- a/test/svg-ssr.html +++ b/test/svg-ssr.html @@ -158,7 +158,7 @@ group.setClipPath(new zrender.Rect({ x: cellSize + cellSize * 3 * k, y: cellSize * (5 + k), - skewX: 40, + skewX: -1.3, shape: { width: cellSize * 7, height: cellSize From 01b22135588885c4f4692c6e1c1d6d516809bb64 Mon Sep 17 00:00:00 2001 From: pissang Date: Mon, 27 Sep 2021 14:22:18 +0800 Subject: [PATCH 009/148] wip(svg-ssr): name inner store structure to vnode --- src/svg-ssr/Painter.ts | 20 ++++++++-------- src/svg-ssr/animation.ts | 6 ++--- src/svg-ssr/core.ts | 20 ++++++++-------- src/svg-ssr/graphic.ts | 52 ++++++++++++++++++++-------------------- test/svg-ssr-bench.html | 29 ++++++++++++++++++---- 5 files changed, 74 insertions(+), 53 deletions(-) diff --git a/src/svg-ssr/Painter.ts b/src/svg-ssr/Painter.ts index f486966fe..b0ac2509f 100644 --- a/src/svg-ssr/Painter.ts +++ b/src/svg-ssr/Painter.ts @@ -8,7 +8,7 @@ import { import Displayable from '../graphic/Displayable'; import Storage from '../Storage'; import { PainterBase } from '../PainterBase'; -import { createElement, elDefToString, SVGElAttrsDef, SVGElDef } from './core'; +import { createElement, vNodeToString, SVGVNodeAttrs, SVGVNode } from './core'; import { SVGNS, XLINKNS } from '../svg/core'; import { normalizeColor } from '../svg/shared'; import { extend, keys, logError, map } from '../core/util'; @@ -49,7 +49,7 @@ class SVGPainter implements PainterBase { throw 'refresh is not supported in SSR mode'; } - _renderToDef() { + renderToVNode() { const list = this.storage.getDisplayList(true); const bgColor = this._backgroundColor; const width = this._width + ''; @@ -63,7 +63,7 @@ class SVGPainter implements PainterBase { defs: {} }; - const svgEl = createElement('svg', + const svgVNode = createElement('svg', [ ['width', width], ['height', height], @@ -74,11 +74,11 @@ class SVGPainter implements PainterBase { ], [] ); - const children = svgEl.children; + const children = svgVNode.children; if (bgColor && bgColor !== 'none') { const { color, opacity } = normalizeColor(bgColor); - svgEl.children.push(createElement( + svgVNode.children.push(createElement( 'rect', [ ['width', width], @@ -98,21 +98,21 @@ class SVGPainter implements PainterBase { createElement('defs', [], map(keys(scope.defs), (id) => scope.defs[id])) ); - return svgEl; + return svgVNode; } renderToString() { - return elDefToString(this._renderToDef()); + return vNodeToString(this.renderToVNode()); } setBackgroundColor(backgroundColor: string) { this._backgroundColor = backgroundColor; } - _paintList(list: Displayable[], scope: BrushScope, out?: SVGElDef[]) { + _paintList(list: Displayable[], scope: BrushScope, out?: SVGVNode[]) { const listLen = list.length; - const clipPathsGroupsStack: SVGElDef[] = []; + const clipPathsGroupsStack: SVGVNode[] = []; let clipPathsGroupsStackDepth = 0; let currentClipPathGroup; let prevClipPaths: Path[]; @@ -139,7 +139,7 @@ class SVGPainter implements PainterBase { } // Pop clip path group for clipPaths not match the previous. for (let i = lca + 1; i < len; i++) { - const groupAttrs: SVGElAttrsDef = []; + const groupAttrs: SVGVNodeAttrs = []; setClipPath( clipPaths[i], groupAttrs, diff --git a/src/svg-ssr/animation.ts b/src/svg-ssr/animation.ts index c8a41ce48..267733ba5 100644 --- a/src/svg-ssr/animation.ts +++ b/src/svg-ssr/animation.ts @@ -1,4 +1,4 @@ -import { createElement, SVGElDef } from './core'; +import { createElement, SVGVNode } from './core'; import Displayable from '../graphic/Displayable'; import {TransformProp} from '../core/Transformable'; import Animator from '../animation/Animator'; @@ -33,7 +33,7 @@ function getTransformAnimateValues( type SVGTransformType = 'translate' | 'scale' | 'rotate'; -function createTransformAnimateDef(defs: Record, transformType: SVGTransformType) { +function createTransformAnimateDef(defs: Record, transformType: SVGTransformType) { const id = transformType.substr(0, 3); if (defs[id]) { return id; @@ -62,7 +62,7 @@ const transformMaps: [SVGTransformType, TransformProp[]][] = [ ['scale', ['scaleX', 'scaleY']] ]; -export function createAnimates(el: Displayable, defs: Record): SVGElDef[] { +export function createAnimates(el: Displayable, defs: Record): SVGVNode[] { const animators = el.animators; let animatesEls = []; diff --git a/src/svg-ssr/core.ts b/src/svg-ssr/core.ts index 701232b65..ce337d1ed 100644 --- a/src/svg-ssr/core.ts +++ b/src/svg-ssr/core.ts @@ -1,19 +1,19 @@ import { map } from '../core/util'; -export type SVGElAttrsDef = [string, string | number | undefined][]; -export type SVGElDef = { +export type SVGVNodeAttrs = [string, string | number | undefined][]; +export type SVGVNode = { tag: string, - attrs: SVGElAttrsDef, - children?: SVGElDef[], + attrs: SVGVNodeAttrs, + children?: SVGVNode[], textContent?: string }; export function createElement( tag: string, - attrs?: SVGElAttrsDef, - children?: SVGElDef[], + attrs?: SVGVNodeAttrs, + children?: SVGVNode[], textContent?: string -): SVGElDef { +): SVGVNode { return { tag, attrs, @@ -21,7 +21,7 @@ export function createElement( textContent }; } -function createElementOpen(name: string, attrs?: SVGElAttrsDef) { +function createElementOpen(name: string, attrs?: SVGVNodeAttrs) { const attrsStr: string[] = []; if (attrs) { for (let i = 0; i < attrs.length; i++) { @@ -39,12 +39,12 @@ function createElementClose(name: string) { return ``; } -export function elDefToString(el: SVGElDef, opts?: { +export function vNodeToString(el: SVGVNode, opts?: { newline?: boolean }) { opts = opts || {}; const S = opts.newline ? '\n' : ''; - function convertElToString(el: SVGElDef): string { + function convertElToString(el: SVGVNode): string { const {children, tag, attrs} = el; return createElementOpen(tag, attrs) + (el.textContent || '') diff --git a/src/svg-ssr/graphic.ts b/src/svg-ssr/graphic.ts index fd60314c5..e4f3667c5 100644 --- a/src/svg-ssr/graphic.ts +++ b/src/svg-ssr/graphic.ts @@ -24,7 +24,7 @@ import { DEFAULT_FONT, getLineHeight } from '../contain/text'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import SVGPathRebuilder from '../svg/SVGPathRebuilder'; import mapStyleToAttrs from '../svg/mapStyleToAttrs'; -import { SVGElAttrsDef, createElement, SVGElDef, elDefToString } from './core'; +import { SVGVNodeAttrs, createElement, SVGVNode, vNodeToString } from './core'; import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; import { assert, isArray, logError, map, reduce, retrieve2 } from '../core/util'; @@ -39,13 +39,13 @@ export interface BrushScope { gradientCache: Record patternCache: Record clipPathCache: Record - defs: Record + defs: Record } type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; -function setStyleAttrs(attrs: SVGElAttrsDef, style: AllStyleOption, el: Path | TSpan | ZRImage, scope: BrushScope) { +function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | TSpan | ZRImage, scope: BrushScope) { const {defs} = scope; mapStyleToAttrs((key, val) => { @@ -75,7 +75,7 @@ function noTranslate(m: MatrixArray) { return isAroundZero(m[4]) && isAroundZero(m[5]); } -function setTransform(attrs: SVGElAttrsDef, m: MatrixArray) { +function setTransform(attrs: SVGVNodeAttrs, m: MatrixArray) { if (m && !(noTranslate(m) && noRotateScale(m))) { attrs.push([ 'transform', @@ -86,10 +86,10 @@ function setTransform(attrs: SVGElAttrsDef, m: MatrixArray) { } type ShapeMapDesc = (string | [string, string])[]; -type ConvertShapeToAttr = (shape: any, attrs: SVGElAttrsDef) => void; +type ConvertShapeToAttr = (shape: any, attrs: SVGVNodeAttrs) => void; type ShapeValidator = (shape: any) => boolean; -function convertPolyShape(shape: Polygon['shape'], attrs: SVGElAttrsDef) { +function convertPolyShape(shape: Polygon['shape'], attrs: SVGVNodeAttrs) { const points = shape.points; const strArr = []; for (let i = 0; i < points.length; i++) { @@ -131,7 +131,7 @@ export function brushSVGPath(el: Path, scope: BrushScope) { const style = el.style; const shape = el.shape; const builtinShpDef = buitinShapesDef[el.type]; - const attrs: SVGElAttrsDef = []; + const attrs: SVGVNodeAttrs = []; let svgElType = 'path'; // Using SVG builtin shapes if possible if (builtinShpDef && !(builtinShpDef[1] && !builtinShpDef[1](shape))) { @@ -180,7 +180,7 @@ export function brushSVGImage(el: ZRImage, scope: BrushScope) { const dw = style.width; const dh = style.height; - const attrs: SVGElAttrsDef = [ + const attrs: SVGVNodeAttrs = [ ['href', image as string], ['width', dw], ['height', dh] @@ -218,7 +218,7 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { const textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign as keyof typeof TEXT_ALIGN_TO_ANCHOR] || style.textAlign; - const attrs: SVGElAttrsDef = [ + const attrs: SVGVNodeAttrs = [ ['style', `font:${font}`], ['dominant-baseline', 'central'], ['text-anchor', textAlign] @@ -256,7 +256,7 @@ let patternIdx = 0; let clipPathIdx = 0; function setShadow( el: Displayable, - attrs: SVGElAttrsDef, + attrs: SVGVNodeAttrs, defs: BrushScope['defs'], shadowCache: Record ) { @@ -302,14 +302,14 @@ function setShadow( function setGradient( style: PathStyleProps, - attrs: SVGElAttrsDef, + attrs: SVGVNodeAttrs, target: 'fill' | 'stroke', defs: BrushScope['defs'], gradientCache: Record ) { const val = style[target] as GradientObject; let gradientTag; - let gradientAttrs: SVGElAttrsDef = [ + let gradientAttrs: SVGVNodeAttrs = [ [ 'gradientUnits', val.global ? 'userSpaceOnUse' // x1, x2, y1, y2 in range of 0 to canvas width or height @@ -348,7 +348,7 @@ function setGradient( // Fix Safari bug that stop-color not recognizing alpha #9014 const {color, opacity} = normalizeColor(stopColor); - const stopsAttrs: SVGElAttrsDef = [['offset', offset]]; + const stopsAttrs: SVGVNodeAttrs = [['offset', offset]]; // stop-color cannot be color, since: // The opacity value used for the gradient calculation is the // *product* of the value of stop-opacity and the opacity of the @@ -364,15 +364,15 @@ function setGradient( } // Use the whole html as cache key. - const el = createElement(gradientTag, gradientAttrs, colorStops); - const gradientKey = elDefToString(el); + const gradientVNode = createElement(gradientTag, gradientAttrs, colorStops); + const gradientKey = vNodeToString(gradientVNode); let gradientId = gradientCache[gradientKey]; if (!gradientId) { gradientId = 'g' + gradientIdx++; gradientCache[gradientKey] = gradientId; - el.attrs.push(['id', gradientId]); - defs[gradientId] = el; + gradientVNode.attrs.push(['id', gradientId]); + defs[gradientId] = gradientVNode; } attrs.push([target, getIdURL(gradientId)]); @@ -380,16 +380,16 @@ function setGradient( function setPattern( style: PathStyleProps, - attrs: SVGElAttrsDef, + attrs: SVGVNodeAttrs, target: 'fill' | 'stroke', defs: BrushScope['defs'], patternCache: Record ) { const val = style[target] as ImagePatternObject | SVGPatternObject; - const patternAttrs: SVGElAttrsDef = [ + const patternAttrs: SVGVNodeAttrs = [ ['patternUnits', 'userSpaceOnUse'] ]; - let child: SVGElDef; + let child: SVGVNode; let contentStr: string; if (isImagePattern(val)) { // image can only be string @@ -423,14 +423,14 @@ function setPattern( } // Use the whole html as cache key. - const patternEl = createElement('pattern', patternAttrs, [child], contentStr); - const patternKey = elDefToString(patternEl); + const patternVNode = createElement('pattern', patternAttrs, [child], contentStr); + const patternKey = vNodeToString(patternVNode); let patternId = patternCache[patternKey]; if (!patternId) { patternId = 'p' + patternIdx++; patternCache[patternKey] = patternId; - patternEl.attrs.push(['id', patternId]); - defs[patternId] = patternEl; + patternVNode.attrs.push(['id', patternId]); + defs[patternId] = patternVNode; } attrs.push([target, getIdURL(patternId)]); @@ -438,14 +438,14 @@ function setPattern( export function setClipPath( clipPath: Path, - attrs: SVGElAttrsDef, + attrs: SVGVNodeAttrs, scope: BrushScope ) { const {clipPathCache, defs} = scope; let clipPathId = clipPathCache[clipPath.id]; if (!clipPathId) { clipPathId = 'c' + clipPathIdx++; - const clipPathAttrs: SVGElAttrsDef = [ + const clipPathAttrs: SVGVNodeAttrs = [ ['id', clipPathId] ]; diff --git a/test/svg-ssr-bench.html b/test/svg-ssr-bench.html index 909bae5eb..50835aa54 100644 --- a/test/svg-ssr-bench.html +++ b/test/svg-ssr-bench.html @@ -5,12 +5,14 @@ SVG SSR +
+
\ No newline at end of file From a12574a7e93a9477936651f1251947e25c6e5fe3 Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 28 Sep 2021 18:07:08 +0800 Subject: [PATCH 011/148] wip(ssr): use snabbdom patch algorithm. add ssr option in init --- src/PainterBase.ts | 3 +- src/canvas/Painter.ts | 35 +-- src/canvas/helper.ts | 28 +++ src/svg-ssr/Painter.ts | 153 ++++++++++--- src/svg-ssr/animation.ts | 8 +- src/svg-ssr/core.ts | 46 ++-- src/svg-ssr/domapi.ts | 55 +++++ src/svg-ssr/graphic.ts | 147 +++++++++---- src/svg-ssr/patch.ts | 347 ++++++++++++++++++++++++++++++ src/{svg => svg-ssr}/shared.ts | 2 +- src/svg/Painter.ts | 68 +----- src/svg/SVGPathRebuilder.ts | 2 +- src/svg/graphic.ts | 2 +- src/svg/helper/ClippathManager.ts | 2 +- src/svg/helper/GradientManager.ts | 2 +- src/svg/helper/PatternManager.ts | 4 +- src/svg/helper/ShadowManager.ts | 2 +- src/svg/mapStyleToAttrs.ts | 2 +- src/zrender.ts | 19 +- test/svg-ssr-bench.html | 38 ++-- test/svg-ssr.html | 1 + 21 files changed, 740 insertions(+), 226 deletions(-) create mode 100644 src/svg-ssr/domapi.ts create mode 100644 src/svg-ssr/patch.ts rename src/{svg => svg-ssr}/shared.ts (98%) diff --git a/src/PainterBase.ts b/src/PainterBase.ts index 8da11401f..ead6db3ee 100644 --- a/src/PainterBase.ts +++ b/src/PainterBase.ts @@ -16,7 +16,8 @@ export interface PainterBase { // root will be undefined if ssr is true root?: HTMLElement - ssr?: boolean + // If ssr only + ssrOnly?: boolean // constructor(dom: HTMLElement, storage: Storage, opts: PainterOption, id: number): void diff --git a/src/canvas/Painter.ts b/src/canvas/Painter.ts index 60dc8ba2b..6a83140dc 100644 --- a/src/canvas/Painter.ts +++ b/src/canvas/Painter.ts @@ -15,6 +15,7 @@ import BoundingRect from '../core/BoundingRect'; import IncrementalDisplayable from '../graphic/IncrementalDisplayable'; import Path from '../graphic/Path'; import { REDRAW_BIT } from '../graphic/constants'; +import { getSize } from './helper'; const HOVER_LAYER_ZLEVEL = 1e5; const CANVAS_ZLEVEL = 314159; @@ -22,9 +23,6 @@ const CANVAS_ZLEVEL = 314159; const EL_AFTER_INCREMENTAL_INC = 0.01; const INCREMENTAL_INC = 0.001; -function parseInt10(val: string) { - return parseInt(val, 10); -} function isLayerValid(layer: Layer) { if (!layer) { @@ -158,8 +156,8 @@ export default class CanvasPainter implements PainterBase { const layers = this._layers; if (!singleCanvas) { - this._width = this._getSize(0); - this._height = this._getSize(1); + this._width = getSize(root, 0, opts); + this._height = getSize(root, 1, opts); const domRoot = this._domRoot = createRoot( this._width, this._height @@ -857,11 +855,12 @@ export default class CanvasPainter implements PainterBase { // Save input w/h const opts = this._opts; + const root = this.root; width != null && (opts.width = width); height != null && (opts.height = height); - width = this._getSize(0); - height = this._getSize(1); + width = getSize(root, 0, opts); + height = getSize(root, 1, opts); domRoot.style.display = ''; @@ -974,28 +973,6 @@ export default class CanvasPainter implements PainterBase { return this._height; } - _getSize(whIdx: number) { - const opts = this._opts; - const wh = ['width', 'height'][whIdx] as 'width' | 'height'; - const cwh = ['clientWidth', 'clientHeight'][whIdx] as 'clientWidth' | 'clientHeight'; - const plt = ['paddingLeft', 'paddingTop'][whIdx] as 'paddingLeft' | 'paddingTop'; - const prb = ['paddingRight', 'paddingBottom'][whIdx] as 'paddingRight' | 'paddingBottom'; - - if (opts[wh] != null && opts[wh] !== 'auto') { - return parseFloat(opts[wh] as string); - } - - const root = this.root; - // IE8 does not support getComputedStyle, but it use VML. - const stl = document.defaultView.getComputedStyle(root); - - return ( - (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) - - (parseInt10(stl[plt]) || 0) - - (parseInt10(stl[prb]) || 0) - ) | 0; - } - pathToImage(path: Path, dpr?: number): ZRImage { dpr = dpr || this.dpr; diff --git a/src/canvas/helper.ts b/src/canvas/helper.ts index 598dfd68d..ec6cb721e 100644 --- a/src/canvas/helper.ts +++ b/src/canvas/helper.ts @@ -86,4 +86,32 @@ export function isClipPathChanged(clipPaths: Path[], prevClipPaths: Path[]): boo } } return false; +} + +function parseInt10(val: string) { + return parseInt(val, 10); +} +export function getSize( + root: HTMLElement, + whIdx: number, + opts: { width?: number | string, height?: number | string} +) { + + const wh = ['width', 'height'][whIdx] as 'width' | 'height'; + const cwh = ['clientWidth', 'clientHeight'][whIdx] as 'clientWidth' | 'clientHeight'; + const plt = ['paddingLeft', 'paddingTop'][whIdx] as 'paddingLeft' | 'paddingTop'; + const prb = ['paddingRight', 'paddingBottom'][whIdx] as 'paddingRight' | 'paddingBottom'; + + if (opts[wh] != null && opts[wh] !== 'auto') { + return parseFloat(opts[wh] as string); + } + + // IE8 does not support getComputedStyle, but it use VML. + const stl = document.defaultView.getComputedStyle(root); + + return ( + (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) + - (parseInt10(stl[plt]) || 0) + - (parseInt10(stl[prb]) || 0) + ) | 0; } \ No newline at end of file diff --git a/src/svg-ssr/Painter.ts b/src/svg-ssr/Painter.ts index b0ac2509f..acb505d6b 100644 --- a/src/svg-ssr/Painter.ts +++ b/src/svg-ssr/Painter.ts @@ -8,27 +8,35 @@ import { import Displayable from '../graphic/Displayable'; import Storage from '../Storage'; import { PainterBase } from '../PainterBase'; -import { createElement, vNodeToString, SVGVNodeAttrs, SVGVNode } from './core'; -import { SVGNS, XLINKNS } from '../svg/core'; -import { normalizeColor } from '../svg/shared'; +import { createVNode, vNodeToString, SVGVNodeAttrs, SVGVNode } from './core'; +import { createElement, SVGNS, XLINKNS } from '../svg/core'; +import { normalizeColor } from './shared'; import { extend, keys, logError, map } from '../core/util'; import Path from '../graphic/Path'; +import patch from './patch'; +import { getSize } from '../canvas/helper'; interface SVGPainterOption { width?: number height?: number + ssr?: boolean } class SVGPainter implements PainterBase { type = 'svg-ssr' - ssr = true - storage: Storage + root: HTMLElement + + private _svgDom: SVGElement + private _viewport: HTMLElement + private _opts: SVGPainterOption + private _oldVNode: SVGVNode + private _width: number private _height: number @@ -38,6 +46,17 @@ class SVGPainter implements PainterBase { this.storage = storage; this._opts = opts = extend({}, opts); + this.root = root; + + if (root && !opts.ssr) { + const viewport = this._viewport = document.createElement('div'); + viewport.style.cssText = 'overflow:hidden;position:relative'; + const svgDom = this._svgDom = createElement('svg'); + root.appendChild(viewport); + viewport.appendChild(svgDom); + + } + this.resize(opts.width, opts.height); } @@ -45,8 +64,27 @@ class SVGPainter implements PainterBase { return this.type; } + getViewportRoot() { + return this._viewport; + } + getViewportRootOffset() { + const viewportRoot = this.getViewportRoot(); + if (viewportRoot) { + return { + offsetLeft: viewportRoot.offsetLeft || 0, + offsetTop: viewportRoot.offsetTop || 0 + }; + } + } + + getSvgDom() { + return this._svgDom; + } + refresh() { - throw 'refresh is not supported in SSR mode'; + const vnode = this.renderToVNode(); + patch(this._oldVNode || this._svgDom, vnode); + this._oldVNode = vnode; } renderToVNode() { @@ -63,23 +101,13 @@ class SVGPainter implements PainterBase { defs: {} }; - const svgVNode = createElement('svg', - [ - ['width', width], - ['height', height], - ['xmlns', SVGNS], - ['xmlns:xlink', XLINKNS], - ['version', '1.1'], - ['baseProfile', 'full'] - ], - [] - ); - const children = svgVNode.children; + const children: SVGVNode[] = []; if (bgColor && bgColor !== 'none') { const { color, opacity } = normalizeColor(bgColor); - svgVNode.children.push(createElement( + children.push(createVNode( 'rect', + 'bg', [ ['width', width], ['height', height], @@ -95,10 +123,27 @@ class SVGPainter implements PainterBase { this._paintList(list, scope, children); children.push( - createElement('defs', [], map(keys(scope.defs), (id) => scope.defs[id])) + createVNode( + 'defs', + 'defs', + [], + map(keys(scope.defs), (id) => scope.defs[id]) + ) ); - return svgVNode; + return createVNode( + 'svg', + 'root', + [ + ['width', width], + ['height', height], + ['xmlns', SVGNS], + ['xmlns:xlink', XLINKNS], + ['version', '1.1'], + ['baseProfile', 'full'] + ], + children + ); } renderToString() { @@ -107,6 +152,8 @@ class SVGPainter implements PainterBase { setBackgroundColor(backgroundColor: string) { this._backgroundColor = backgroundColor; + // TOOD optimize for change bg only. + this.renderToVNode(); } _paintList(list: Displayable[], scope: BrushScope, out?: SVGVNode[]) { @@ -145,7 +192,12 @@ class SVGPainter implements PainterBase { groupAttrs, scope ); - const g = createElement('g', groupAttrs, []); + const g = createVNode( + 'g', + 'clip-g-' + clipPaths[i].id, + groupAttrs, + [] + ); (currentClipPathGroup ? currentClipPathGroup.children : out).push(g); clipPathsGroupsStack[clipPathsGroupsStackDepth++] = g; currentClipPathGroup = g; @@ -161,8 +213,40 @@ class SVGPainter implements PainterBase { } resize(width: number, height: number) { - this._width = width; - this._height = height; + // Save input w/h + const opts = this._opts; + const root = this.root; + const viewport = this._viewport; + width != null && (opts.width = width); + height != null && (opts.height = height); + + if (root && viewport) { + // FIXME Why ? + viewport.style.display = 'none'; + + width = getSize(root, 0, opts); + height = getSize(root, 1, opts); + + viewport.style.display = ''; + } + + if (this._width !== width || this._height !== height) { + this._width = width; + this._height = height; + + if (viewport) { + const viewportStyle = viewport.style; + viewportStyle.width = width + 'px'; + viewportStyle.height = height + 'px'; + } + + const svgDom = this._svgDom; + if (svgDom) { + // Set width by 'svgRoot.width = width' is invalid + svgDom.setAttribute('width', width as any); + svgDom.setAttribute('height', height as any); + } + } } /** @@ -179,12 +263,23 @@ class SVGPainter implements PainterBase { return this._height; } - dispose() {} - clear() {} - toDataURL() {} + dispose() { + this.root.innerHTML = ''; - getViewportRoot = createMethodNotSupport('getViewportRoot') as PainterBase['getViewportRoot'] - getViewportRootOffset = createMethodNotSupport('getViewportRootOffset') as PainterBase['getViewportRootOffset'] + this._svgDom = + this._viewport = + this.storage = + this._oldVNode = null; + } + clear() { + this._svgDom.innerHTML = null; + this._oldVNode = null; + } + toDataURL() { + const str = this.renderToString(); + const html = encodeURIComponent(str); + return 'data:image/svg+xml;charset=UTF-8,' + html; + } refreshHover = createMethodNotSupport('refreshHover') as PainterBase['refreshHover']; pathToImage = createMethodNotSupport('pathToImage') as PainterBase['pathToImage']; diff --git a/src/svg-ssr/animation.ts b/src/svg-ssr/animation.ts index 267733ba5..7ff6d9055 100644 --- a/src/svg-ssr/animation.ts +++ b/src/svg-ssr/animation.ts @@ -1,4 +1,4 @@ -import { createElement, SVGVNode } from './core'; +import { createVNode, SVGVNode } from './core'; import Displayable from '../graphic/Displayable'; import {TransformProp} from '../core/Transformable'; import Animator from '../animation/Animator'; @@ -39,7 +39,7 @@ function createTransformAnimateDef(defs: Record, transformType return id; } - const el = createElement('animateTransform', [ + const el = createVNode('animateTransform', [ ['attributeName', 'transform'], ['attributeType', 'XML'], ['type', transformType], @@ -50,7 +50,7 @@ function createTransformAnimateDef(defs: Record, transformType } function createAnimateEl(useId: string, values: string) { - return createElement('use', [ + return createVNode('use', [ ['xlink:href', useId], ['values', values] ]); @@ -93,5 +93,5 @@ export function createAnimates(el: Displayable, defs: Record): } } - return; + return animatesEls; } diff --git a/src/svg-ssr/core.ts b/src/svg-ssr/core.ts index ce337d1ed..24094cb49 100644 --- a/src/svg-ssr/core.ts +++ b/src/svg-ssr/core.ts @@ -1,33 +1,49 @@ import { map } from '../core/util'; -export type SVGVNodeAttrs = [string, string | number | undefined][]; -export type SVGVNode = { +export type SVGVNodeAttrs = [string, string | number | undefined | boolean][]; + +type SVGVNodeAttrsMap = Record +export interface SVGVNode { tag: string, - attrs: SVGVNodeAttrs, + attrs: SVGVNodeAttrsMap, children?: SVGVNode[], - textContent?: string -}; + text?: string -export function createElement( + // For patching + elm?: Node + key: string +}; +export function createVNode( tag: string, + key: string, attrs?: SVGVNodeAttrs, children?: SVGVNode[], - textContent?: string + text?: string ): SVGVNode { + const attrsMap: SVGVNodeAttrsMap = {}; + if (attrs) { + for (let i = 0; i < attrs.length; i++) { + attrsMap[attrs[i][0]] = attrs[i][1]; + } + } return { tag, - attrs, + attrs: attrsMap, children, - textContent + text, + key }; } -function createElementOpen(name: string, attrs?: SVGVNodeAttrs) { + +function createElementOpen(name: string, attrs?: SVGVNodeAttrsMap) { const attrsStr: string[] = []; if (attrs) { - for (let i = 0; i < attrs.length; i++) { - let part = attrs[i][0]; - if (attrs[i][1] != null) { - part += `="${attrs[i][1]}"`; + // eslint-disable-next-line + for (let key in attrs) { + const val = attrs[key]; + let part = key; + if (val != null) { + part += `="${val}"`; } attrsStr.push(part); } @@ -47,7 +63,7 @@ export function vNodeToString(el: SVGVNode, opts?: { function convertElToString(el: SVGVNode): string { const {children, tag, attrs} = el; return createElementOpen(tag, attrs) - + (el.textContent || '') + + (el.text || '') + (children ? `${S}${map(children, child => convertElToString(child)).join(S)}${S}` : '') + createElementClose(tag); } diff --git a/src/svg-ssr/domapi.ts b/src/svg-ssr/domapi.ts new file mode 100644 index 000000000..5e7c914f6 --- /dev/null +++ b/src/svg-ssr/domapi.ts @@ -0,0 +1,55 @@ +export function createTextNode(text: string): Text { + return document.createTextNode(text); +} + +export function createComment(text: string): Comment { + return document.createComment(text); +} + +export function insertBefore( + parentNode: Node, + newNode: Node, + referenceNode: Node | null +): void { + parentNode.insertBefore(newNode, referenceNode); +} + +export function removeChild(node: Node, child: Node): void { + node.removeChild(child); +} + +export function appendChild(node: Node, child: Node): void { + node.appendChild(child); +} + +export function parentNode(node: Node): Node | null { + return node.parentNode; +} + +export function nextSibling(node: Node): Node | null { + return node.nextSibling; +} + +export function tagName(elm: Element): string { + return elm.tagName; +} + +export function setTextContent(node: Node, text: string | null): void { + node.textContent = text; +} + +export function getTextContent(node: Node): string | null { + return node.textContent; +} + +export function isElement(node: Node): node is Element { + return node.nodeType === 1; +} + +export function isText(node: Node): node is Text { + return node.nodeType === 3; +} + +export function isComment(node: Node): node is Comment { + return node.nodeType === 8; +} diff --git a/src/svg-ssr/graphic.ts b/src/svg-ssr/graphic.ts index e4f3667c5..9176c41a1 100644 --- a/src/svg-ssr/graphic.ts +++ b/src/svg-ssr/graphic.ts @@ -17,22 +17,24 @@ import { normalizeColor, round4, TEXT_ALIGN_TO_ANCHOR -} from '../svg/shared'; +} from './shared'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; import { DEFAULT_FONT, getLineHeight } from '../contain/text'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import SVGPathRebuilder from '../svg/SVGPathRebuilder'; import mapStyleToAttrs from '../svg/mapStyleToAttrs'; -import { SVGVNodeAttrs, createElement, SVGVNode, vNodeToString } from './core'; +import { SVGVNodeAttrs, createVNode, SVGVNode, vNodeToString } from './core'; import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; -import { assert, isArray, logError, map, reduce, retrieve2 } from '../core/util'; +import { assert, logError, map, retrieve2 } from '../core/util'; import Polyline from '../graphic/shape/Polyline'; import Polygon from '../graphic/shape/Polygon'; import { GradientObject } from '../graphic/Gradient'; import { ImagePatternObject, SVGPatternObject } from '../graphic/Pattern'; import { createAnimates } from './animation'; +import { createOrUpdateImage } from '../graphic/helper/image'; +import { ImageLike } from '../core/types'; export interface BrushScope { shadowCache: Record @@ -54,7 +56,7 @@ function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | T setGradient(style, attrs, key, defs, scope.gradientCache); } else if (isFillStroke && isPattern(val)) { - setPattern(style, attrs, key, defs, scope.patternCache); + setPattern(el, attrs, key, defs, scope.patternCache); } else { attrs.push([key, val]); @@ -144,11 +146,9 @@ export function brushSVGPath(el: Path, scope: BrushScope) { } const path = el.path; - if (el.shapeChanged()) { - path.beginPath(); - el.buildPath(path, el.shape); - el.pathUpdated(); - } + path.beginPath(); + el.buildPath(path, el.shape); + el.pathUpdated(); // Because SSR renderer only render once. So always create new to simplify the case. const svgPathBuilder = new SVGPathRebuilder(); svgPathBuilder.reset(); @@ -162,7 +162,7 @@ export function brushSVGPath(el: Path, scope: BrushScope) { setTransform(attrs, el.transform); setStyleAttrs(attrs, style, el, scope); - return createElement(svgElType, attrs, createAnimates(el, scope.defs)); + return createVNode(svgElType, el.id + '', attrs, createAnimates(el, scope.defs)); } export function brushSVGImage(el: ZRImage, scope: BrushScope) { @@ -195,7 +195,7 @@ export function brushSVGImage(el: ZRImage, scope: BrushScope) { setTransform(attrs, el.transform); setStyleAttrs(attrs, style, el, scope); - return createElement('image', attrs, createAnimates(el, scope.defs)); + return createVNode('image', el.id + '', attrs, createAnimates(el, scope.defs)); }; export function brushSVGTSpan(el: TSpan, scope: BrushScope) { @@ -235,7 +235,7 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { setTransform(attrs, el.transform); setStyleAttrs(attrs, style, el, scope); - return createElement('text', attrs, createAnimates(el, scope.defs), text); + return createVNode('text', el.id + '', attrs, createAnimates(el, scope.defs), text); } export function brush(el: Displayable, scope: BrushScope) { @@ -278,21 +278,24 @@ function setShadow( const stdDeviation = stdDx + ' ' + stdDy; // Use a simple prefix to reduce the size shadowId = 's' + shadowIdx++; - defs[shadowId] = createElement( - 'filter', [ + defs[shadowId] = createVNode( + 'filter', shadowId, + [ ['id', shadowId], ['x', '-100%'], ['y', '-100%'], ['width', '300%'], ['height', '300%'] ], - [createElement('feDropShadow', [ - ['dx', offsetX / scaleX], - ['dy', offsetY / scaleY], - ['stdDeviation', stdDeviation], - ['flood-color', color], - ['flood-opacity', opacity] - ])] + [ + createVNode('feDropShadow', '', [ + ['dx', offsetX / scaleX], + ['dy', offsetY / scaleY], + ['stdDeviation', stdDeviation], + ['flood-color', color], + ['flood-opacity', opacity] + ]) + ] ); shadowCache[shadowKey] = shadowId; } @@ -359,52 +362,98 @@ function setGradient( stopsAttrs.push(['stop-opacity', opacity]); } colorStops.push( - createElement('stop', stopsAttrs) + createVNode('stop', i + '', stopsAttrs) ); } // Use the whole html as cache key. - const gradientVNode = createElement(gradientTag, gradientAttrs, colorStops); + const gradientVNode = createVNode(gradientTag, '', gradientAttrs, colorStops); const gradientKey = vNodeToString(gradientVNode); let gradientId = gradientCache[gradientKey]; if (!gradientId) { gradientId = 'g' + gradientIdx++; gradientCache[gradientKey] = gradientId; - gradientVNode.attrs.push(['id', gradientId]); - defs[gradientId] = gradientVNode; + gradientAttrs.push(['id', gradientId]); + defs[gradientId] = createVNode( + gradientTag, gradientId, gradientAttrs, colorStops + ); } attrs.push([target, getIdURL(gradientId)]); } function setPattern( - style: PathStyleProps, + el: Displayable, attrs: SVGVNodeAttrs, target: 'fill' | 'stroke', defs: BrushScope['defs'], patternCache: Record ) { - const val = style[target] as ImagePatternObject | SVGPatternObject; + const val = el.style[target] as ImagePatternObject | SVGPatternObject; const patternAttrs: SVGVNodeAttrs = [ ['patternUnits', 'userSpaceOnUse'] ]; let child: SVGVNode; let contentStr: string; if (isImagePattern(val)) { - // image can only be string - const errMsg = 'Image width/height must been given explictly in svg-ssr renderer.'; - const imageWidth = val.imageWidth; - const imageHeight = val.imageHeight; - assert(imageWidth, errMsg); - assert(imageHeight, errMsg); + let imageWidth; + let imageHeight; + let imageSrc; + const patternImage = val.image; + if (typeof patternImage === 'string') { + imageSrc = patternImage; + } + else if (patternImage instanceof HTMLImageElement) { + imageSrc = patternImage.src; + } + else if (patternImage instanceof HTMLCanvasElement) { + imageSrc = patternImage.toDataURL(); + } + + if (typeof Image === 'undefined') { + const errMsg = 'Image width/height must been given explictly in svg-ssr renderer.'; + const imageWidth = val.imageWidth; + const imageHeight = val.imageHeight; + assert(imageWidth, errMsg); + assert(imageHeight, errMsg); + } + else { + // TODO + const setSizeToVNode = (vNode: SVGVNode, img: ImageLike) => { + if (vNode) { + const svgEl = vNode.elm as SVGElement; + const width = (vNode.attrs.width = img.width); + const height = (vNode.attrs.height = img.height); + if (svgEl) { + svgEl.setAttribute('width', width as any); + svgEl.setAttribute('height', height as any); + } + } + }; + const createdImage = createOrUpdateImage( + imageSrc, null, el, (img) => { + setSizeToVNode(patternVNode, img); + setSizeToVNode(child, img); + } + ); + if (createdImage && createdImage.width && createdImage.height) { + // Loaded before + imageWidth = createdImage.width; + imageHeight = createdImage.height; + } + } // TODO Only support string url - child = createElement('image', [ - ['href', val.image as string], - ['width', imageWidth], - ['height', imageHeight] - ]); + child = createVNode( + 'image', + 'img', + [ + ['href', val.image as string], + ['width', imageWidth], + ['height', imageHeight] + ] + ); patternAttrs.push( ['width', imageWidth], ['height', imageHeight] @@ -423,14 +472,26 @@ function setPattern( } // Use the whole html as cache key. - const patternVNode = createElement('pattern', patternAttrs, [child], contentStr); + const patternVNode = createVNode( + 'pattern', + '', + patternAttrs, + [child], + contentStr + ); const patternKey = vNodeToString(patternVNode); let patternId = patternCache[patternKey]; if (!patternId) { patternId = 'p' + patternIdx++; patternCache[patternKey] = patternId; - patternVNode.attrs.push(['id', patternId]); - defs[patternId] = patternVNode; + patternAttrs.push(['id', patternId]); + defs[patternId] = createVNode( + 'pattern', + patternId, + patternAttrs, + [child], + contentStr + ); } attrs.push([target, getIdURL(patternId)]); @@ -450,8 +511,8 @@ export function setClipPath( ]; clipPathCache[clipPath.id] = clipPathId; - defs[clipPathId] = createElement( - 'clipPath', clipPathAttrs, + defs[clipPathId] = createVNode( + 'clipPath', clipPathId, clipPathAttrs, [brushSVGPath(clipPath, scope)] ); } diff --git a/src/svg-ssr/patch.ts b/src/svg-ssr/patch.ts new file mode 100644 index 000000000..6e9b82186 --- /dev/null +++ b/src/svg-ssr/patch.ts @@ -0,0 +1,347 @@ +/** + * Virtual DOM patching + * Modified from snabbdom https://github.com/snabbdom/snabbdom/blob/master/src/init.ts + * + * The design has been simplified to focus on the purpose in SVG rendering in SVG. + * + * Licensed under the MIT License + * https://github.com/paldepind/snabbdom/blob/master/LICENSE + */ + +import { isArray, isObject } from '../core/util'; +import { createElement } from '../svg/core'; +import { createVNode, SVGVNode } from './core'; +import * as api from './domapi'; + +const xlinkNS = 'http://www.w3.org/1999/xlink'; +const xmlNS = 'http://www.w3.org/XML/1998/namespace'; +const colonChar = 58; +const xChar = 120; +const emptyNode = createVNode('', '') as SVGVNode; + +type NonUndefined = T extends undefined ? never : T; + +function isUndef(s: any): boolean { + return s === undefined; +} +function isDef(s: A): s is NonUndefined { + return s !== undefined; +} + +type VNodeQueue = SVGVNode[]; + +function createKeyToOldIdx( + children: SVGVNode[], + beginIdx: number, + endIdx: number +): KeyToIndexMap { + const map: KeyToIndexMap = {}; + for (let i = beginIdx; i <= endIdx; ++i) { + const key = children[i].key; + if (key !== undefined) { + map[key as string] = i; + } + } + return map; +} + +function sameVnode(vnode1: SVGVNode, vnode2: SVGVNode): boolean { + if (vnode1 === vnode2) { + return true; + } + const isSameKey = vnode1.key === vnode2.key; + const isSameTag = vnode1.tag === vnode2.tag; + + return isSameTag && isSameKey; +} + +function isVnode(vnode: any): vnode is SVGVNode { + return vnode.tag !== undefined; +} + +type KeyToIndexMap = { [key: string]: number }; + +function emptyNodeAt(elm: Element): SVGVNode { + const id = elm.id ? '#' + elm.id : ''; + + // elm.className doesn't return a string when elm is an SVG element inside a shadowRoot. + // https://stackoverflow.com/questions/29454340/detecting-classname-of-svganimatedstring + const classes = elm.getAttribute('class'); + + const c = classes ? '.' + classes.split(' ').join('.') : ''; + const vnode = createVNode(api.tagName(elm).toLowerCase() + id + c, '', []) as SVGVNode; + vnode.elm = elm; + return vnode; +} + +function createElm(vnode: SVGVNode, insertedVnodeQueue: VNodeQueue): Node { + let i: any; + const children = vnode.children; + const tag = vnode.tag; + if (tag === '!') { + if (isUndef(vnode.text)) { + vnode.text = ''; + } + vnode.elm = api.createComment(vnode.text!); + } + else if (tag !== undefined) { + const elm = (vnode.elm = createElement(tag)); + + updateAttrs(emptyNode, vnode); + + if (isArray(children)) { + for (i = 0; i < children.length; ++i) { + const ch = children[i]; + if (ch != null) { + api.appendChild(elm, createElm(ch as SVGVNode, insertedVnodeQueue)); + } + } + } + else if (!isObject(vnode.text)) { + api.appendChild(elm, api.createTextNode(vnode.text)); + } + } + else { + vnode.elm = api.createTextNode(vnode.text!); + } + return vnode.elm; +} + +function addVnodes( + parentElm: Node, + before: Node | null, + vnodes: SVGVNode[], + startIdx: number, + endIdx: number, + insertedVnodeQueue: VNodeQueue +) { + for (; startIdx <= endIdx; ++startIdx) { + const ch = vnodes[startIdx]; + if (ch != null) { + api.insertBefore(parentElm, createElm(ch, insertedVnodeQueue), before); + } + } +} + +function removeVnodes(parentElm: Node, vnodes: SVGVNode[], startIdx: number, endIdx: number): void { + for (; startIdx <= endIdx; ++startIdx) { + const ch = vnodes[startIdx]; + if (ch != null) { + if (isDef(ch.tag)) { + const parent = api.parentNode(ch.elm) as Node; + api.removeChild(parent, ch.elm); + } + else { + // Text node + api.removeChild(parentElm, ch.elm!); + } + } + } +} + +function updateAttrs(oldVnode: SVGVNode, vnode: SVGVNode): void { + let key: string; + const elm: Element = vnode.elm as Element; + let oldAttrs = oldVnode.attrs || {}; + let attrs = vnode.attrs || {}; + + if (oldAttrs === attrs) { + return; + } + + // update modified attributes, add new attributes + // eslint-disable-next-line + for (key in attrs) { + const cur = attrs[key]; + const old = oldAttrs[key]; + if (old !== cur) { + if (cur === true) { + elm.setAttribute(key, ''); + } + else if (cur === false) { + elm.removeAttribute(key); + } + else { + if (key.charCodeAt(0) !== xChar) { + elm.setAttribute(key, cur as any); + } + // TODO + else if (key === 'xmlns:xlink' || key === 'xmlns') { + elm.setAttributeNS('http://www.w3.org/2000/xmlns/', key, cur as any); + } + else if (key.charCodeAt(3) === colonChar) { + // Assume xml namespace + elm.setAttributeNS(xmlNS, key, cur as any); + } + else if (key.charCodeAt(5) === colonChar) { + // Assume xlink namespace + elm.setAttributeNS(xlinkNS, key, cur as any); + } + else { + elm.setAttribute(key, cur as any); + } + } + } + } + + // remove removed attributes + // use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value) + // the other option is to remove all attributes with value == undefined + for (key in oldAttrs) { + if (!(key in attrs)) { + elm.removeAttribute(key); + } + } +} + + +function updateChildren( + parentElm: Node, oldCh: SVGVNode[], newCh: SVGVNode[], insertedVnodeQueue: VNodeQueue +) { + let oldStartIdx = 0; + let newStartIdx = 0; + let oldEndIdx = oldCh.length - 1; + let oldStartVnode = oldCh[0]; + let oldEndVnode = oldCh[oldEndIdx]; + let newEndIdx = newCh.length - 1; + let newStartVnode = newCh[0]; + let newEndVnode = newCh[newEndIdx]; + let oldKeyToIdx: KeyToIndexMap | undefined; + let idxInOld: number; + let elmToMove: SVGVNode; + let before: any; + + while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { + if (oldStartVnode == null) { + oldStartVnode = oldCh[++oldStartIdx]; // Vnode might have been moved left + } + else if (oldEndVnode == null) { + oldEndVnode = oldCh[--oldEndIdx]; + } + else if (newStartVnode == null) { + newStartVnode = newCh[++newStartIdx]; + } + else if (newEndVnode == null) { + newEndVnode = newCh[--newEndIdx]; + } + else if (sameVnode(oldStartVnode, newStartVnode)) { + patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue); + oldStartVnode = oldCh[++oldStartIdx]; + newStartVnode = newCh[++newStartIdx]; + } + else if (sameVnode(oldEndVnode, newEndVnode)) { + patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); + oldEndVnode = oldCh[--oldEndIdx]; + newEndVnode = newCh[--newEndIdx]; + } + else if (sameVnode(oldStartVnode, newEndVnode)) { + // Vnode moved right + patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); + api.insertBefore(parentElm, oldStartVnode.elm!, api.nextSibling(oldEndVnode.elm!)); + oldStartVnode = oldCh[++oldStartIdx]; + newEndVnode = newCh[--newEndIdx]; + } + else if (sameVnode(oldEndVnode, newStartVnode)) { + // Vnode moved left + patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue); + api.insertBefore(parentElm, oldEndVnode.elm!, oldStartVnode.elm!); + oldEndVnode = oldCh[--oldEndIdx]; + newStartVnode = newCh[++newStartIdx]; + } + else { + if (oldKeyToIdx === undefined) { + oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); + } + idxInOld = oldKeyToIdx[newStartVnode.key as string]; + if (isUndef(idxInOld)) { + // New element + api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm!); + } + else { + elmToMove = oldCh[idxInOld]; + if (elmToMove.tag !== newStartVnode.tag) { + api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm!); + } + else { + patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); + oldCh[idxInOld] = undefined as any; + api.insertBefore(parentElm, elmToMove.elm!, oldStartVnode.elm!); + } + } + newStartVnode = newCh[++newStartIdx]; + } + } + if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) { + if (oldStartIdx > oldEndIdx) { + before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm; + addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue); + } + else { + removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); + } + } +} + +function patchVnode(oldVnode: SVGVNode, vnode: SVGVNode, insertedVnodeQueue: VNodeQueue) { + const elm = (vnode.elm = oldVnode.elm)!; + const oldCh = oldVnode.children as SVGVNode[]; + const ch = vnode.children as SVGVNode[]; + if (oldVnode === vnode) { + return; + } + + updateAttrs(oldVnode, vnode); + + if (isUndef(vnode.text)) { + if (isDef(oldCh) && isDef(ch)) { + if (oldCh !== ch) { + updateChildren(elm, oldCh, ch, insertedVnodeQueue); + } + } + else if (isDef(ch)) { + if (isDef(oldVnode.text)) { + api.setTextContent(elm, ''); + } + addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); + } + else if (isDef(oldCh)) { + removeVnodes(elm, oldCh, 0, oldCh.length - 1); + } + else if (isDef(oldVnode.text)) { + api.setTextContent(elm, ''); + } + } + else if (oldVnode.text !== vnode.text) { + if (isDef(oldCh)) { + removeVnodes(elm, oldCh, 0, oldCh.length - 1); + } + api.setTextContent(elm, vnode.text!); + } +} + +export default function patch(oldVnode: SVGVNode | Element, vnode: SVGVNode): SVGVNode { + let elm: Node; + let parent: Node; + const insertedVnodeQueue: VNodeQueue = []; + + if (!isVnode(oldVnode)) { + oldVnode = emptyNodeAt(oldVnode); + } + + if (sameVnode(oldVnode, vnode)) { + patchVnode(oldVnode, vnode, insertedVnodeQueue); + } + else { + elm = oldVnode.elm!; + parent = api.parentNode(elm) as Node; + + createElm(vnode, insertedVnodeQueue); + + if (parent !== null) { + api.insertBefore(parent, vnode.elm!, api.nextSibling(elm)); + removeVnodes(parent, [oldVnode], 0, 0); + } + } + + return vnode; +} diff --git a/src/svg/shared.ts b/src/svg-ssr/shared.ts similarity index 98% rename from src/svg/shared.ts rename to src/svg-ssr/shared.ts index 4e26fa362..22ec33646 100644 --- a/src/svg/shared.ts +++ b/src/svg-ssr/shared.ts @@ -8,7 +8,7 @@ import Path from '../graphic/Path'; import { ImagePatternObject, PatternObject, SVGPatternObject } from '../graphic/Pattern'; import { RadialGradientObject } from '../graphic/RadialGradient'; import { parse } from '../tool/color'; -import { mathRound } from './core'; +import { mathRound } from '../svg/core'; export function normalizeColor(color: string): { color: string; opacity: number; } { let opacity; diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 7a2166832..c3299ab61 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -3,7 +3,7 @@ */ import {createElement, SVGNS, XLINKNS, XMLNS} from './core'; -import { normalizeColor } from './shared'; +import { normalizeColor } from '../svg-ssr/shared'; import * as util from '../core/util'; import Path from '../graphic/Path'; import ZRImage from '../graphic/Image'; @@ -22,6 +22,7 @@ import { import Displayable from '../graphic/Displayable'; import Storage from '../Storage'; import { PainterBase } from '../PainterBase'; +import { getSize } from '../canvas/helper'; function parseInt10(val: string) { return parseInt(val, 10); @@ -174,9 +175,7 @@ class SVGPainter implements PainterBase { } refresh() { - const list = this.storage.getDisplayList(true); - this._paintList(list); } @@ -332,43 +331,6 @@ class SVGPainter implements PainterBase { this._visibleList = newVisibleList; } - // Not used any more - // _getDefs(isForceCreating?: boolean) { - // let svgRoot = this._svgDom; - // let defs = svgRoot.getElementsByTagName('defs'); - // if (defs.length === 0) { - // // Not exist - // if (isForceCreating) { - // let defs = svgRoot.insertBefore( - // createElement('defs'), // Create new tag - // svgRoot.firstChild // Insert in the front of svg - // ); - // if (!defs.contains) { - // // IE doesn't support contains method - // defs.contains = function (el) { - // const children = defs.children; - // if (!children) { - // return false; - // } - // for (let i = children.length - 1; i >= 0; --i) { - // if (children[i] === el) { - // return true; - // } - // } - // return false; - // }; - // } - // return defs; - // } - // else { - // return null; - // } - // } - // else { - // return defs[0]; - // } - // } - resize(width: number | string, height: number | string) { const viewport = this._viewport; // FIXME Why ? @@ -379,8 +341,8 @@ class SVGPainter implements PainterBase { width != null && (opts.width = width); height != null && (opts.height = height); - width = this._getSize(0); - height = this._getSize(1); + width = getSize(this.root, 0, opts); + height = getSize(this.root, 1, opts); viewport.style.display = ''; @@ -418,28 +380,6 @@ class SVGPainter implements PainterBase { return this._height; } - _getSize(whIdx: number) { - const opts = this._opts; - const wh = ['width', 'height'][whIdx] as 'width' | 'height'; - const cwh = ['clientWidth', 'clientHeight'][whIdx] as 'clientWidth' | 'clientHeight'; - const plt = ['paddingLeft', 'paddingTop'][whIdx] as 'paddingLeft' | 'paddingTop'; - const prb = ['paddingRight', 'paddingBottom'][whIdx] as 'paddingRight' | 'paddingBottom'; - - if (opts[wh] != null && opts[wh] !== 'auto') { - return parseFloat(opts[wh] as string); - } - - const root = this.root; - // IE8 does not support getComputedStyle, but it use VML. - const stl = document.defaultView.getComputedStyle(root); - - return ( - (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) - - (parseInt10(stl[plt]) || 0) - - (parseInt10(stl[prb]) || 0) - ) | 0; - } - dispose() { this.root.innerHTML = ''; diff --git a/src/svg/SVGPathRebuilder.ts b/src/svg/SVGPathRebuilder.ts index 39be8964f..08ae209cc 100644 --- a/src/svg/SVGPathRebuilder.ts +++ b/src/svg/SVGPathRebuilder.ts @@ -1,5 +1,5 @@ import { PathRebuilder } from '../core/PathProxy'; -import { isAroundZero, round4 } from './shared'; +import { isAroundZero, round4 } from '../svg-ssr/shared'; const mathSin = Math.sin; const mathCos = Math.cos; diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index ed9059654..ece9d51e9 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -3,7 +3,7 @@ // 2. Image: sx, sy, sw, sh import {createElement, XLINKNS } from './core'; -import { getMatrixStr, TEXT_ALIGN_TO_ANCHOR, adjustTextY } from './shared'; +import { getMatrixStr, TEXT_ALIGN_TO_ANCHOR, adjustTextY } from '../svg-ssr/shared'; import * as matrix from '../core/matrix'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; diff --git a/src/svg/helper/ClippathManager.ts b/src/svg/helper/ClippathManager.ts index 396817d26..8fa51af39 100644 --- a/src/svg/helper/ClippathManager.ts +++ b/src/svg/helper/ClippathManager.ts @@ -10,7 +10,7 @@ import Path from '../../graphic/Path'; import {path} from '../graphic'; import { Dictionary } from '../../core/types'; import { isClipPathChanged } from '../../canvas/helper'; -import { getClipPathsKey, getIdURL } from '../shared'; +import { getClipPathsKey, getIdURL } from '../../svg-ssr/shared'; import { createElement } from '../core'; type PathExtended = Path & { diff --git a/src/svg/helper/GradientManager.ts b/src/svg/helper/GradientManager.ts index 102d24d65..bb9dccf71 100644 --- a/src/svg/helper/GradientManager.ts +++ b/src/svg/helper/GradientManager.ts @@ -7,7 +7,7 @@ import Definable from './Definable'; import * as zrUtil from '../../core/util'; import Displayable from '../../graphic/Displayable'; import { GradientObject } from '../../graphic/Gradient'; -import { getIdURL, isGradient, isLinearGradient, isRadialGradient, normalizeColor, round4 } from '../shared'; +import { getIdURL, isGradient, isLinearGradient, isRadialGradient, normalizeColor, round4 } from '../../svg-ssr/shared'; import { createElement } from '../core'; diff --git a/src/svg/helper/PatternManager.ts b/src/svg/helper/PatternManager.ts index ed79ff03a..62b348139 100644 --- a/src/svg/helper/PatternManager.ts +++ b/src/svg/helper/PatternManager.ts @@ -6,10 +6,10 @@ import Definable from './Definable'; import * as zrUtil from '../../core/util'; import Displayable from '../../graphic/Displayable'; -import {PatternObject, ImagePatternObject, SVGPatternObject} from '../../graphic/Pattern'; +import {PatternObject} from '../../graphic/Pattern'; import {createOrUpdateImage} from '../../graphic/helper/image'; import WeakMap from '../../core/WeakMap'; -import { getIdURL, isPattern, isSVGPattern } from '../shared'; +import { getIdURL, isPattern, isSVGPattern } from '../../svg-ssr/shared'; import { createElement } from '../core'; const patternDomMap = new WeakMap(); diff --git a/src/svg/helper/ShadowManager.ts b/src/svg/helper/ShadowManager.ts index 3d1f95278..3a3876f2b 100644 --- a/src/svg/helper/ShadowManager.ts +++ b/src/svg/helper/ShadowManager.ts @@ -6,7 +6,7 @@ import Definable from './Definable'; import Displayable from '../../graphic/Displayable'; import { Dictionary } from '../../core/types'; -import { getIdURL, getShadowKey, hasShadow, normalizeColor } from '../shared'; +import { getIdURL, getShadowKey, hasShadow, normalizeColor } from '../../svg-ssr/shared'; import { createElement } from '../core'; type DisplayableExtended = Displayable & { diff --git a/src/svg/mapStyleToAttrs.ts b/src/svg/mapStyleToAttrs.ts index a3204da17..2832ce65f 100644 --- a/src/svg/mapStyleToAttrs.ts +++ b/src/svg/mapStyleToAttrs.ts @@ -4,7 +4,7 @@ import ZRImage, { ImageStyleProps } from '../graphic/Image'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import { normalizeLineDash } from '../graphic/helper/dashStyle'; import { map } from '../core/util'; -import { normalizeColor } from './shared'; +import { normalizeColor } from '../svg-ssr/shared'; type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; diff --git a/src/zrender.ts b/src/zrender.ts index 9fda1c237..9dac18d67 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -124,23 +124,23 @@ class ZRender { : opts.useDirtyRect; const painter = new painterCtors[rendererType](dom, storage, opts, id); - const SSRMode = painter.ssr; + const ssrMode = opts.ssr || painter.ssrOnly; this.storage = storage; this.painter = painter; - const handerProxy = (!env.node && !env.worker && !SSRMode) + const handerProxy = (!env.node && !env.worker && !ssrMode) ? new HandlerProxy(painter.getViewportRoot(), painter.root) : null; this.handler = new Handler(storage, painter, handerProxy, painter.root); this.animation = new Animation({ stage: { - update: SSRMode ? null : () => this._flush(true) + update: ssrMode ? null : () => this._flush(true) } }); - if (!SSRMode) { + if (!ssrMode) { this.animation.start(); } } @@ -242,16 +242,6 @@ class ZRender { this._flush(false); } - renderToString() { - const painter = this.painter; - if (!painter.renderToString) { - throw 'Can only use renderToString in svg-ssr'; - } - // pending - this.animation.update(true); - return painter.renderToString(); - } - private _flush(fromInside?: boolean) { let triggerRendered; @@ -452,6 +442,7 @@ export interface ZRenderInitOpt { width?: number | string // 10, 10px, 'auto' height?: number | string useDirtyRect?: boolean + ssr?: boolean // If enable ssr mode. } /** diff --git a/test/svg-ssr-bench.html b/test/svg-ssr-bench.html index f9d6cec4e..2a2cb6dfc 100644 --- a/test/svg-ssr-bench.html +++ b/test/svg-ssr-bench.html @@ -15,9 +15,9 @@ + + \ No newline at end of file From e2b06857c71f32a0faab522f67ca4d7cb0b56086 Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 5 Oct 2021 21:18:10 +0800 Subject: [PATCH 034/148] wip(ssr): fix some escape chars --- src/contain/textWidthMap.ts | 5 +++-- test/asciiWidthMap.html | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/contain/textWidthMap.ts b/src/contain/textWidthMap.ts index a7d2e9f11..921d7db39 100644 --- a/src/contain/textWidthMap.ts +++ b/src/contain/textWidthMap.ts @@ -12,13 +12,14 @@ // const ratio = Math.round(width / 12 * 100); // mapStr += String.fromCharCode(ratio + 20)) // } -// mapStr; +// mapStr.replace(/\\/g, '\\\\'); export const OFFSET = 20; export const SCALE = 100; export const DEFAULT_FONT_SIZE = 12; +// TODO other basic fonts? // eslint-disable-next-line -const defaultWidthMapStr = "007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\WQb\0FWLg\bWb\WQ\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\FFF5.5N"; +const defaultWidthMapStr = `007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N`; function getMap(mapStr: string): Record { const map: Record = {}; diff --git a/test/asciiWidthMap.html b/test/asciiWidthMap.html index 9904e61cb..d158b29c5 100644 --- a/test/asciiWidthMap.html +++ b/test/asciiWidthMap.html @@ -19,7 +19,8 @@ console.log(ratio + 20); mapStr += String.fromCharCode(ratio + 20); } - console.log(mapStr); + const a = mapStr.replace(/\\/g, '\\\\'); + document.write(a); \ No newline at end of file From 4f7803a91f2cf3a3b003f570111b222fbff1b139 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 6 Oct 2021 15:05:48 +0800 Subject: [PATCH 035/148] style: naming tweak --- src/graphic/helper/roundSector.ts | 66 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 0a0c9f934..f11b6b8b9 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -15,10 +15,10 @@ const e = 1e-4; type CornerTangents = { cx: number cy: number - x01: number - y01: number - x11: number - y11: number + x0: number + y0: number + x1: number + y1: number }; function intersect( @@ -82,10 +82,10 @@ function computeCornerTangents( return { cx: cx0, cy: cy0, - x01: -ox, - y01: -oy, - x11: cx0 * (radius / r - 1), - y11: cy0 * (radius / r - 1) + x0: -ox, + y0: -oy, + x1: cx0 * (radius / r - 1), + y1: cy0 * (radius / r - 1) }; } @@ -138,29 +138,29 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { arc = mathAbs(tmpAngles[0] - tmpAngles[1]); } - const x = shape.cx; - const y = shape.cy; + const cx = shape.cx; + const cy = shape.cy; const cornerRadius = shape.cornerRadius || 0; const innerCornerRadius = shape.innerCornerRadius || 0; // is a point if (!(radius > e)) { - ctx.moveTo(x, y); + ctx.moveTo(cx, cy); } // is a circle or annulus else if (arc > PI2 - e) { ctx.moveTo( - x + radius * mathCos(startAngle), - y + radius * mathSin(startAngle) + cx + radius * mathCos(startAngle), + cy + radius * mathSin(startAngle) ); - ctx.arc(x, y, radius, startAngle, endAngle, !clockwise); + ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); if (innerRadius > e) { ctx.moveTo( - x + innerRadius * mathCos(endAngle), - y + innerRadius * mathSin(endAngle) + cx + innerRadius * mathCos(endAngle), + cy + innerRadius * mathSin(endAngle) ); - ctx.arc(x, y, innerRadius, endAngle, startAngle, clockwise); + ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); } } // is a circular or annular sector @@ -208,67 +208,67 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { // the sector is collapsed to a line if (!(arc > e)) { - ctx.moveTo(x + xrs, y + yrs); + ctx.moveTo(cx + xrs, cy + yrs); } // the outer ring has corners else if (cr1 > e) { const ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, cr1, clockwise); const ct1 = computeCornerTangents(xre, yre, xire, yire, radius, cr1, clockwise); - ctx.moveTo(x + ct0.cx + ct0.x01, y + ct0.cy + ct0.y01); + ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); // Have the corners merged? if (cr1 < cr) { // eslint-disable-next-line max-len - ctx.arc(x + ct0.cx, y + ct0.cy, cr1, mathATan2(ct0.y01, ct0.x01), mathATan2(ct1.y01, ct1.x01), !clockwise); + ctx.arc(cx + ct0.cx, cy + ct0.cy, cr1, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); } else { // draw the two corners and the ring // eslint-disable-next-line max-len - ctx.arc(x + ct0.cx, y + ct0.cy, cr1, mathATan2(ct0.y01, ct0.x01), mathATan2(ct0.y11, ct0.x11), !clockwise); + ctx.arc(cx + ct0.cx, cy + ct0.cy, cr1, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); // eslint-disable-next-line max-len - ctx.arc(x, y, radius, mathATan2(ct0.cy + ct0.y11, ct0.cx + ct0.x11), mathATan2(ct1.cy + ct1.y11, ct1.cx + ct1.x11), !clockwise); + ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise); // eslint-disable-next-line max-len - ctx.arc(x + ct1.cx, y + ct1.cy, cr1, mathATan2(ct1.y11, ct1.x11), mathATan2(ct1.y01, ct1.x01), !clockwise); + ctx.arc(cx + ct1.cx, cy + ct1.cy, cr1, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); } } // the outer ring is a circular arc else { - ctx.moveTo(x + xrs, y + yrs); - ctx.arc(x, y, radius, startAngle, endAngle, !clockwise); + ctx.moveTo(cx + xrs, cy + yrs); + ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); } // no inner ring, is a circular sector if (!(innerRadius > e) || !(arc > e)) { - ctx.lineTo(x + xire, y + yire); + ctx.lineTo(cx + xire, cy + yire); } // the inner ring has corners else if (cr0 > e) { const ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -cr0, clockwise); const ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -cr0, clockwise); - ctx.lineTo(x + ct0.cx + ct0.x01, y + ct0.cy + ct0.y01); + ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); // Have the corners merged? if (cr0 < icr) { // eslint-disable-next-line max-len - ctx.arc(x + ct0.cx, y + ct0.cy, cr0, mathATan2(ct0.y01, ct0.x01), mathATan2(ct1.y01, ct1.x01), !clockwise); + ctx.arc(cx + ct0.cx, cy + ct0.cy, cr0, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); } // draw the two corners and the ring else { // eslint-disable-next-line max-len - ctx.arc(x + ct0.cx, y + ct0.cy, cr0, mathATan2(ct0.y01, ct0.x01), mathATan2(ct0.y11, ct0.x11), !clockwise); + ctx.arc(cx + ct0.cx, cy + ct0.cy, cr0, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); // eslint-disable-next-line max-len - ctx.arc(x, y, innerRadius, mathATan2(ct0.cy + ct0.y11, ct0.cx + ct0.x11), mathATan2(ct1.cy + ct1.y11, ct1.cx + ct1.x11), clockwise); + ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise); // eslint-disable-next-line max-len - ctx.arc(x + ct1.cx, y + ct1.cy, cr0, mathATan2(ct1.y11, ct1.x11), mathATan2(ct1.y01, ct1.x01), !clockwise); + ctx.arc(cx + ct1.cx, cy + ct1.cy, cr0, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); } } // the inner ring is just a circular arc else { // FIXME: if no lineTo, svg renderer will perform an abnormal drawing behavior. - ctx.lineTo(x + xire, y + yire); + ctx.lineTo(cx + xire, cy + yire); - ctx.arc(x, y, innerRadius, endAngle, startAngle, clockwise); + ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); } } From 2074583bf0a9a244f91e8429c7f45469c6aa1d0e Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 6 Oct 2021 15:25:49 +0800 Subject: [PATCH 036/148] wip(ssr): disable animation when path data don't match. --- src/svg/cssAnimation.ts | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 118b6906b..d541d67eb 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -5,7 +5,7 @@ import Path from '../graphic/Path'; import SVGPathRebuilder from './SVGPathRebuilder'; import PathProxy from '../core/PathProxy'; import { getPathPrecision, getSRTTransformString } from './helper'; -import { each, extend, isEmptyObject, isString, keys, map } from '../core/util'; +import { each, extend, isString, keys } from '../core/util'; import Animator from '../animation/Animator'; import { CompoundPath } from '../export'; @@ -45,11 +45,10 @@ function sameTransform(a: any, b: any) { return true; } -function buildPathString(el: Path, kfShape: Path['shape']) { +function buildPathString(el: Path, kfShape: Path['shape'], path: PathProxy) { const shape = extend({}, el.shape); extend(shape, kfShape); - const path = new PathProxy(); el.buildPath(path, shape); const svgPathBuilder = new SVGPathRebuilder(); svgPathBuilder.reset(getPathPrecision(el)); @@ -237,7 +236,8 @@ export function createCSSAnimation( } } - map(keys(transformKfs), percent => { + // eslint-disable-next-line + for (let percent in transformKfs) { const transform = {} as Transformable; copyTransform(transform, el); extend(transform, transformKfs[percent]); @@ -245,12 +245,34 @@ export function createCSSAnimation( transform: getSRTTransformString(transform) }; setTransformOrigin(finalKfs[percent], transform); - }); + }; + - map(keys(shapeKfs), percent => { + let path: PathProxy; + let canAnimateShape = true; + // eslint-disable-next-line + for (let percent in shapeKfs) { finalKfs[percent] = finalKfs[percent] || {}; - finalKfs[percent].d = buildPathString(el as Path, shapeKfs[percent]); - }); + const isFirst = !path; + if (isFirst) { + path = new PathProxy(); + } + let len = path.len(); + path.reset(); + finalKfs[percent].d = buildPathString(el as Path, shapeKfs[percent], path); + let newLen = path.len(); + // Path data don't match. + if (!isFirst && len !== newLen) { + canAnimateShape = false; + break; + } + }; + if (!canAnimateShape) { + // eslint-disable-next-line + for (let percent in finalKfs) { + delete finalKfs[percent].d; + } + } if (!onlyShape) { for (let i = 0; i < len; i++) { From 93a5facf39e1897c4958b038329d56ef64382268 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 6 Oct 2021 15:32:15 +0800 Subject: [PATCH 037/148] wip(ssr): not add css class if there is no property can animate. --- src/svg/cssAnimation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index d541d67eb..6a290feac 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -5,7 +5,7 @@ import Path from '../graphic/Path'; import SVGPathRebuilder from './SVGPathRebuilder'; import PathProxy from '../core/PathProxy'; import { getPathPrecision, getSRTTransformString } from './helper'; -import { each, extend, isString, keys } from '../core/util'; +import { each, extend, isEmptyObject, isString, keys } from '../core/util'; import Animator from '../animation/Animator'; import { CompoundPath } from '../export'; @@ -287,7 +287,7 @@ export function createCSSAnimation( } const percents = keys(finalKfs); - if (percents.length) { + if (percents.length && !isEmptyObject(finalKfs[percents[0]])) { const animationName = addAnimation(finalKfs, scope); // eslint-disable-next-line for (let attrName in finalKfs[percents[0]]) { From b055250895c86a7a588b16d4888d5320b8050719 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 6 Oct 2021 20:59:30 +0800 Subject: [PATCH 038/148] chore: add node env --- build/build.js | 74 +++++++++++++++--------- package-lock.json | 56 ++++++++++++++++++ package.json | 1 + src/Element.ts | 35 ++++++----- src/canvas/Painter.ts | 8 ++- src/core/timsort.ts | 1 - src/global.d.ts | 5 ++ src/graphic/Group.ts | 6 +- src/graphic/Text.ts | 4 +- src/svg-legacy/Painter.ts | 4 +- src/svg-legacy/helper/GradientManager.ts | 8 ++- src/svg/Painter.ts | 4 +- src/svg/graphic.ts | 4 +- src/tool/parseSVG.ts | 6 +- src/zrender.ts | 12 ++-- 15 files changed, 171 insertions(+), 57 deletions(-) create mode 100644 src/global.d.ts diff --git a/build/build.js b/build/build.js index 885fa5efc..a0b189809 100644 --- a/build/build.js +++ b/build/build.js @@ -1,5 +1,6 @@ // const typescript = require('@rollup/plugin-typescript'); const typescript = require('rollup-plugin-typescript2'); +const replace = require('@rollup/plugin-replace'); const rollup = require('rollup'); const path = require('path'); const processs = require('process'); @@ -12,24 +13,33 @@ function current() { return (new Date()).toLocaleString(); } -const inputOption = { - input: path.resolve(__dirname, '../index.ts'), - plugins: [typescript({ - tsconfigOverride: { - compilerOptions: { - // Rollup don't use CommonJS by default. - module: 'ES2015', - sourceMap: true, - // Use the esm d.ts - declaration: false - } - } - }), progress({ - scope: { - total: 0 - } - })] -}; +function createInputOption(env, isWatch) { + return { + input: path.resolve(__dirname, '../index.ts'), + plugins: [ + typescript({ + clean: !isWatch, + tsconfigOverride: { + compilerOptions: { + // Rollup don't use CommonJS by default. + module: 'ES2015', + sourceMap: true, + // Use the esm d.ts + declaration: false + } + } + }), + replace({ + 'process.env.NODE_ENV': JSON.stringify(env) + }), + progress({ + scope: { + total: 0 + } + }) + ] + }; +} const outputOption = { format: 'umd', @@ -39,18 +49,17 @@ const outputOption = { }; function minify(outPath) { - const fileMinPath = outPath.replace(/.js$/, '.min.js'); const code = fs.readFileSync(outPath, 'utf-8'); const uglifyResult = UglifyJS.minify(code); if (uglifyResult.error) { throw new Error(uglifyResult.error); } - fs.writeFileSync(fileMinPath, uglifyResult.code, 'utf-8'); + fs.writeFileSync(outPath, uglifyResult.code, 'utf-8'); } if (processs.argv.includes('--watch')) { const watcher = rollup.watch({ - ...inputOption, + ...createInputOption('development', true), output: [outputOption], watch: { clearScreen: true @@ -83,13 +92,24 @@ if (processs.argv.includes('--watch')) { }); } else { + // Unminified rollup.rollup({ - ...inputOption + ...createInputOption('development', false) }).then(bundle => { - bundle.write(outputOption).then(function () { - if (process.argv.indexOf('--minify') >= 0) { - minify(outputOption.file); - } - }); + bundle.write(outputOption); }); + // Minified + if (process.argv.indexOf('--minify') >= 0) { + rollup.rollup({ + ...createInputOption('production', false) + }).then(bundle => { + const file = outputOption.file.replace(/.js$/, '.min.js'); + bundle.write(Object.assign(outputOption, { + file, + sourcemap: false + })).then(function () { + minify(file); + }); + }); + } } diff --git a/package-lock.json b/package-lock.json index 28a23e97d..58889833d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -738,6 +738,47 @@ "fastq": "^1.6.0" } }, + "@rollup/plugin-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-3.0.0.tgz", + "integrity": "sha512-3c7JCbMuYXM4PbPWT4+m/4Y6U60SgsnDT/cCyAyUKwFHg7pTSfsSQzIpETha3a3ig6OdOKzZz87D9ZXIK3qsDg==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + } + } + }, "@sinonjs/commons": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.0.tgz", @@ -4027,6 +4068,15 @@ "yallist": "^4.0.0" } }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, "make-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", @@ -5346,6 +5396,12 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", diff --git a/package.json b/package.json index 4d718457d..e36941dcc 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ ], "devDependencies": { "@microsoft/api-extractor": "^7.7.2", + "@rollup/plugin-replace": "^3.0.0", "@types/jest": "^25.1.2", "@typescript-eslint/eslint-plugin": "^4.9.1", "@typescript-eslint/parser": "^4.9.1", diff --git a/src/Element.ts b/src/Element.ts index c5c7a885e..a8749c317 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -1196,11 +1196,17 @@ class Element { */ private _attachComponent(componentEl: Element) { if (componentEl.__zr && !componentEl.__hostTarget) { - throw new Error('Text element has been added to zrender.'); + if (process.env.NODE_ENV !== 'production') { + throw new Error('Text element has been added to zrender.'); + } + return; } if (componentEl === this) { - throw new Error('Recursive component attachment.'); + if (process.env.NODE_ENV !== 'production') { + throw new Error('Recursive component attachment.'); + } + return; } const zr = this.__zr; @@ -1277,9 +1283,10 @@ class Element { if (previousTextContent && previousTextContent !== textEl) { this.removeTextContent(); } - - if (textEl.__zr && !textEl.__hostTarget) { - throw new Error('Text element has been added to zrender.'); + if (process.env.NODE_ENV !== 'production') { + if (textEl.__zr && !textEl.__hostTarget) { + throw new Error('Text element has been added to zrender.'); + } } textEl.innerTransformable = new Transformable(); @@ -1463,14 +1470,16 @@ class Element { animate(key?: string, loop?: boolean) { let target = key ? (this as any)[key] : this; - if (!target) { - logError( - 'Property "' - + key - + '" is not existed in element ' - + this.id - ); - return; + if (process.env.NODE_ENV !== 'production') { + if (!target) { + logError( + 'Property "' + + key + + '" is not existed in element ' + + this.id + ); + return; + } } const animator = new Animator(target, loop); diff --git a/src/canvas/Painter.ts b/src/canvas/Painter.ts index 6a83140dc..1f6e98a14 100644 --- a/src/canvas/Painter.ts +++ b/src/canvas/Painter.ts @@ -571,12 +571,16 @@ export default class CanvasPainter implements PainterBase { let i = -1; if (layersMap[zlevel]) { - util.logError('ZLevel ' + zlevel + ' has been used already'); + if (process.env.NODE_ENV !== 'production') { + util.logError('ZLevel ' + zlevel + ' has been used already'); + } return; } // Check if is a valid layer if (!isLayerValid(layer)) { - util.logError('Layer of zlevel ' + zlevel + ' is not valid'); + if (process.env.NODE_ENV !== 'production') { + util.logError('Layer of zlevel ' + zlevel + ' is not valid'); + } return; } diff --git a/src/core/timsort.ts b/src/core/timsort.ts index fdd1cbe0e..ee602f229 100644 --- a/src/core/timsort.ts +++ b/src/core/timsort.ts @@ -448,7 +448,6 @@ function TimSort(array: T[], compare: CompareFunc) { } else if (length1 === 0) { throw new Error(); - // throw new Error('mergeLow preconditions were not respected'); } else { for (i = 0; i < length1; i++) { diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 000000000..07a39ec11 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,5 @@ +declare namespace NodeJS { + interface ProcessEnv { + NODE_ENV: 'production' | 'development' + } +} \ No newline at end of file diff --git a/src/graphic/Group.ts b/src/graphic/Group.ts index 337a8822b..8406f6efd 100644 --- a/src/graphic/Group.ts +++ b/src/graphic/Group.ts @@ -86,8 +86,10 @@ class Group extends Element { this._children.push(child); this._doAdd(child); } - if (child.__hostTarget) { - throw 'This elemenet has been used as an attachment'; + if (process.env.NODE_ENV !== 'production') { + if (child.__hostTarget) { + throw 'This elemenet has been used as an attachment'; + } } } diff --git a/src/graphic/Text.ts b/src/graphic/Text.ts index eb4709e3d..0190b6fae 100644 --- a/src/graphic/Text.ts +++ b/src/graphic/Text.ts @@ -412,7 +412,9 @@ class ZRText extends Displayable implements GroupLike { } setTextContent(textContent: never) { - throw new Error('Can\'t attach text on another text'); + if (process.env.NODE_ENV !== 'production') { + throw new Error('Can\'t attach text on another text'); + } } // getDefaultStyleValue(key: T): TextStyleProps[T] { diff --git a/src/svg-legacy/Painter.ts b/src/svg-legacy/Painter.ts index 171616a88..cd25d6d7b 100644 --- a/src/svg-legacy/Painter.ts +++ b/src/svg-legacy/Painter.ts @@ -412,7 +412,9 @@ class SVGPainter implements PainterBase { // Not supported methods function createMethodNotSupport(method: string): any { return function () { - util.logError('In SVG mode painter not support method "' + method + '"'); + if (process.env.NODE_ENV !== 'production') { + util.logError('In SVG mode painter not support method "' + method + '"'); + } }; } diff --git a/src/svg-legacy/helper/GradientManager.ts b/src/svg-legacy/helper/GradientManager.ts index c8b27751b..da8512c70 100644 --- a/src/svg-legacy/helper/GradientManager.ts +++ b/src/svg-legacy/helper/GradientManager.ts @@ -85,7 +85,9 @@ export default class GradientManager extends Definable { dom = createElement('radialGradient'); } else { - zrUtil.logError('Illegal gradient type.'); + if (process.env.NODE_ENV !== 'production') { + zrUtil.logError('Illegal gradient type.'); + } return null; } @@ -158,7 +160,9 @@ export default class GradientManager extends Definable { dom.setAttribute('r', gradient.r as any); } else { - zrUtil.logError('Illegal gradient type.'); + if (process.env.NODE_ENV !== 'production') { + zrUtil.logError('Illegal gradient type.'); + } return; } diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index ea3280976..6b7740568 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -345,7 +345,9 @@ class SVGPainter implements PainterBase { // Not supported methods function createMethodNotSupport(method: string): any { return function () { - logError('In SVG mode painter not support method "' + method + '"'); + if (process.env.NODE_ENV !== 'production') { + logError('In SVG mode painter not support method "' + method + '"'); + } }; } diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 9142f0c15..3bcb9c14d 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -370,7 +370,9 @@ function setGradient( gradientAttrs.r = retrieve2(val.r, 0.5); } else { - logError('Illegal gradient type.'); + if (process.env.NODE_ENV !== 'production') { + logError('Illegal gradient type.'); + } return; } diff --git a/src/tool/parseSVG.ts b/src/tool/parseSVG.ts index 7f52f19dc..cbcc2a804 100644 --- a/src/tool/parseSVG.ts +++ b/src/tool/parseSVG.ts @@ -136,8 +136,10 @@ class SVGParser { const svg = parseXML(xml); - if (!svg) { - throw new Error('Illegal svg'); + if (process.env.NODE_ENV !== 'production') { + if (!svg) { + throw new Error('Illegal svg'); + } } this._defsUsePending = []; diff --git a/src/zrender.ts b/src/zrender.ts index de4250d8b..f454670b1 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -107,16 +107,20 @@ class ZRender { let rendererType = opts.renderer || 'canvas'; // TODO WebGL - if (useVML) { - throw new Error('IE8 support has been dropped since 5.0'); + if (process.env.NODE_ENV !== 'production') { + if (useVML) { + throw new Error('IE8 support has been dropped since 5.0'); + } } if (!painterCtors[rendererType]) { // Use the first registered renderer. rendererType = zrUtil.keys(painterCtors)[0]; } - if (!painterCtors[rendererType]) { - throw new Error(`Renderer '${rendererType}' is not imported. Please import it first.`); + if (process.env.NODE_ENV !== 'production') { + if (!painterCtors[rendererType]) { + throw new Error(`Renderer '${rendererType}' is not imported. Please import it first.`); + } } opts.useDirtyRect = opts.useDirtyRect == null From 5cb9a678966ae620cdc58ae718769636da02d215 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 6 Oct 2021 21:09:29 +0800 Subject: [PATCH 039/148] refact: remove code for ancient browsers. 1. lineDash that don't support in IE<=10 2. event handlers and canvas that don't support in IE <= 8 --- src/Storage.ts | 8 +- src/canvas/graphic.ts | 6 - src/core/PathProxy.ts | 190 +------- src/core/env.ts | 5 - src/core/event.ts | 87 ++-- src/vml/Painter.ts | 200 -------- src/vml/core.ts | 48 -- src/vml/graphic.ts | 1061 ----------------------------------------- src/vml/vml.ts | 7 - src/zrender.ts | 9 - 10 files changed, 33 insertions(+), 1588 deletions(-) delete mode 100644 src/vml/Painter.ts delete mode 100644 src/vml/core.ts delete mode 100644 src/vml/graphic.ts delete mode 100644 src/vml/vml.ts diff --git a/src/Storage.ts b/src/Storage.ts index 2abd1fcb8..72228c3fa 100644 --- a/src/Storage.ts +++ b/src/Storage.ts @@ -22,12 +22,6 @@ function logInvalidZError() { function shapeCompareFunc(a: Displayable, b: Displayable) { if (a.zlevel === b.zlevel) { if (a.z === b.z) { - // if (a.z2 === b.z2) { - // // FIXME Slow has renderidx compare - // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement - // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012 - // return a.__renderidx - b.__renderidx; - // } return a.z2 - b.z2; } return a.z - b.z; @@ -85,7 +79,7 @@ export default class Storage { displayList.length = this._displayListLen; - env.canvasSupported && timsort(displayList, shapeCompareFunc); + timsort(displayList, shapeCompareFunc); } private _updateAndAddDisplayable( diff --git a/src/canvas/graphic.ts b/src/canvas/graphic.ts index bd5243d5f..f2eb3527d 100644 --- a/src/canvas/graphic.ts +++ b/src/canvas/graphic.ts @@ -218,12 +218,6 @@ function brushPath(ctx: CanvasRenderingContext2D, el: Path, style: PathStyleProp } path.reset(); - // Setting line dash before build path - if (lineDash && !ctxLineDash) { - path.setLineDash(lineDash); - path.setLineDashOffset(lineDashOffset); - } - el.buildPath(path, el.shape, inBatch); path.toStatic(); diff --git a/src/core/PathProxy.ts b/src/core/PathProxy.ts index 0d520d891..620b7353d 100644 --- a/src/core/PathProxy.ts +++ b/src/core/PathProxy.ts @@ -147,13 +147,6 @@ export default class PathProxy { private _ux: number private _uy: number - // For dash shim. - private _lineDash: number[] - private _needsDash: boolean - private _dashOffset: number - private _dashIdx: number - private _dashSum: number - static CMD = CMD constructor(notSaveData?: boolean) { @@ -217,11 +210,6 @@ export default class PathProxy { this._len = 0; } - if (this._lineDash) { - this._lineDash = null; - this._dashOffset = 0; - } - if (this._pathSegLen) { this._pathSegLen = null; this._pathLen = 0; @@ -259,8 +247,7 @@ export default class PathProxy { this.addData(CMD.L, x, y); if (this._ctx && exceedUnit) { - this._needsDash ? this._dashedLineTo(x, y) - : this._ctx.lineTo(x, y); + this._ctx.lineTo(x, y); } if (exceedUnit) { this._xi = x; @@ -285,8 +272,7 @@ export default class PathProxy { this.addData(CMD.C, x1, y1, x2, y2, x3, y3); if (this._ctx) { - this._needsDash ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3) - : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); + this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); } this._xi = x3; this._yi = y3; @@ -298,8 +284,7 @@ export default class PathProxy { this.addData(CMD.Q, x1, y1, x2, y2); if (this._ctx) { - this._needsDash ? this._dashedQuadraticTo(x1, y1, x2, y2) - : this._ctx.quadraticCurveTo(x1, y1, x2, y2); + this._ctx.quadraticCurveTo(x1, y1, x2, y2); } this._xi = x2; this._yi = y2; @@ -358,7 +343,6 @@ export default class PathProxy { const x0 = this._x0; const y0 = this._y0; if (ctx) { - this._needsDash && this._dashedLineTo(x0, y0); ctx.closePath(); } @@ -377,41 +361,6 @@ export default class PathProxy { this.toStatic(); } - /** - * 必须在其它绘制命令前调用 - * Must be invoked before all other path drawing methods - */ - setLineDash(lineDash: number[] | false) { - if (lineDash instanceof Array) { - this._lineDash = lineDash; - - this._dashIdx = 0; - - let lineDashSum = 0; - for (let i = 0; i < lineDash.length; i++) { - lineDashSum += lineDash[i]; - } - this._dashSum = lineDashSum; - - this._needsDash = true; - } - else { - // Clear - this._lineDash = null; - this._needsDash = false; - } - return this; - } - - /** - * 必须在其它绘制命令前调用 - * Must be invoked before all other path drawing methods - */ - setLineDashOffset(offset: number) { - this._dashOffset = offset; - return this; - } - len() { return this._len; } @@ -502,135 +451,6 @@ export default class PathProxy { } } - private _dashedLineTo(x1: number, y1: number) { - const dashSum = this._dashSum; - const lineDash = this._lineDash; - const ctx = this._ctx; - let offset = this._dashOffset; - - let x0 = this._xi; - let y0 = this._yi; - let dx = x1 - x0; - let dy = y1 - y0; - let dist = mathSqrt(dx * dx + dy * dy); - let x = x0; - let y = y0; - let nDash = lineDash.length; - let dash; - let idx; - dx /= dist; - dy /= dist; - - if (offset < 0) { - // Convert to positive offset - offset = dashSum + offset; - } - offset %= dashSum; - x -= offset * dx; - y -= offset * dy; - - while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1) - || (dx === 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) { - idx = this._dashIdx; - dash = lineDash[idx]; - x += dx * dash; - y += dy * dash; - this._dashIdx = (idx + 1) % nDash; - // Skip positive offset - if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) { - continue; - } - ctx[idx % 2 ? 'moveTo' : 'lineTo']( - dx >= 0 ? mathMin(x, x1) : mathMax(x, x1), - dy >= 0 ? mathMin(y, y1) : mathMax(y, y1) - ); - } - // Offset for next lineTo - dx = x - x1; - dy = y - y1; - this._dashOffset = -mathSqrt(dx * dx + dy * dy); - } - - // Not accurate dashed line to - private _dashedBezierTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) { - const ctx = this._ctx; - - let dashSum = this._dashSum; - let offset = this._dashOffset; - let lineDash = this._lineDash; - - let x0 = this._xi; - let y0 = this._yi; - let bezierLen = 0; - let idx = this._dashIdx; - let nDash = lineDash.length; - - let t; - let dx; - let dy; - - let x; - let y; - - let tmpLen = 0; - - if (offset < 0) { - // Convert to positive offset - offset = dashSum + offset; - } - offset %= dashSum; - // Bezier approx length - for (t = 0; t < 1; t += 0.1) { - dx = cubicAt(x0, x1, x2, x3, t + 0.1) - - cubicAt(x0, x1, x2, x3, t); - dy = cubicAt(y0, y1, y2, y3, t + 0.1) - - cubicAt(y0, y1, y2, y3, t); - bezierLen += mathSqrt(dx * dx + dy * dy); - } - - // Find idx after add offset - for (; idx < nDash; idx++) { - tmpLen += lineDash[idx]; - if (tmpLen > offset) { - break; - } - } - t = (tmpLen - offset) / bezierLen; - - while (t <= 1) { - - x = cubicAt(x0, x1, x2, x3, t); - y = cubicAt(y0, y1, y2, y3, t); - - // Use line to approximate dashed bezier - // Bad result if dash is long - idx % 2 ? ctx.moveTo(x, y) - : ctx.lineTo(x, y); - - t += lineDash[idx] / bezierLen; - - idx = (idx + 1) % nDash; - } - - // Finish the last segment and calculate the new offset - (idx % 2 !== 0) && ctx.lineTo(x3, y3); - dx = x3 - x; - dy = y3 - y; - this._dashOffset = -mathSqrt(dx * dx + dy * dy); - } - - private _dashedQuadraticTo(x1: number, y1: number, x2: number, y2: number) { - // Convert quadratic to cubic using degree elevation - const x3 = x2; - const y3 = y2; - x2 = (x2 + 2 * x1) / 3; - y2 = (y2 + 2 * y1) / 3; - x1 = (this._xi + 2 * x1) / 3; - y1 = (this._yi + 2 * y1) / 3; - - this._dashedBezierTo(x1, y1, x2, y2, x3, y3); - } - /** * Convert dynamic array to static Float32Array * @@ -1156,10 +976,6 @@ export default class PathProxy { private static initDefaultProps = (function () { const proto = PathProxy.prototype; proto._saveData = true; - proto._needsDash = false; - proto._dashOffset = 0; - proto._dashIdx = 0; - proto._dashSum = 0; proto._ux = 0; proto._uy = 0; proto._pendingPtDist = 0; diff --git a/src/core/env.ts b/src/core/env.ts index 010ef9f0c..fced5aa0b 100644 --- a/src/core/env.ts +++ b/src/core/env.ts @@ -17,7 +17,6 @@ class Env { wxa = false worker = false - canvasSupported = false svgSupported = false touchEventsSupported = false pointerEventsSupported = false @@ -30,18 +29,15 @@ const env = new Env(); if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') { env.wxa = true; - env.canvasSupported = true; env.touchEventsSupported = true; } else if (typeof document === 'undefined' && typeof self !== 'undefined') { // In worker env.worker = true; - env.canvasSupported = true; } else if (typeof navigator === 'undefined') { // In node env.node = true; - env.canvasSupported = true; env.svgSupported = true; } else { @@ -83,7 +79,6 @@ function detect(ua: string, env: Env) { browser.weChat = true; } - env.canvasSupported = !!document.createElement('canvas').getContext; env.svgSupported = typeof SVGRect !== 'undefined'; env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge; env.pointerEventsSupported = 'onpointerdown' in window diff --git a/src/core/event.ts b/src/core/event.ts index c5fc28117..4b7d10d6a 100644 --- a/src/core/event.ts +++ b/src/core/event.ts @@ -7,8 +7,6 @@ import env from './env'; import { ZRRawEvent } from './types'; import {isCanvasEl, transformCoordWithViewport} from './dom'; -const isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener; - const MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/; const _calcOut: number[] = []; @@ -55,8 +53,7 @@ export function clientToLocal( // not support the properties. // (see http://www.jacklmoore.com/notes/mouse-position/) // In zr painter.dom, padding edge equals to border edge. - - if (calculate || !env.canvasSupported) { + if (calculate) { calculateZrXY(el, e as ZRRawEvent, out); } // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned @@ -207,7 +204,6 @@ function getWheelDeltaMayPolyfill(e: ZRRawEvent): number { // we currently do not break it. // But event "wheel" in firefox do not has "wheelDelta", so we calculate // "wheelDeta" from "deltaX", "deltaY" (which is the props in spec). - const rawWheelDelta = (e as any).wheelDelta; // Theroetically `e.wheelDelta` won't be 0 unless some day it has been deprecated // by agent like Chrome or Safari. So we also calculate it if rawWheelDelta is 0. @@ -249,34 +245,28 @@ export function addEventListener( handler: AddEventListenerParams[1], opt?: AddEventListenerParams[2] ) { - if (isDomLevel2) { - // Reproduct the console warning: - // [Violation] Added non-passive event listener to a scroll-blocking event. - // Consider marking event handler as 'passive' to make the page more responsive. - // Just set console log level: verbose in chrome dev tool. - // then the warning log will be printed when addEventListener called. - // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md - // We have not yet found a neat way to using passive. Because in zrender the dom event - // listener delegate all of the upper events of element. Some of those events need - // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts. - // Before passive can be adopted, these issues should be considered: - // (1) Whether and how a zrender user specifies an event listener passive. And by default, - // passive or not. - // (2) How to tread that some zrender event listener is passive, and some is not. If - // we use other way but not preventDefault of mousewheel and touchmove, browser - // compatibility should be handled. - - // const opts = (env.passiveSupported && name === 'mousewheel') - // ? {passive: true} - // // By default, the third param of el.addEventListener is `capture: false`. - // : void 0; - // el.addEventListener(name, handler /* , opts */); - el.addEventListener(name, handler, opt); - } - else { - // For simplicity, do not implement `setCapture` for IE9-. - (el as any).attachEvent('on' + name, handler); - } + // Reproduct the console warning: + // [Violation] Added non-passive event listener to a scroll-blocking event. + // Consider marking event handler as 'passive' to make the page more responsive. + // Just set console log level: verbose in chrome dev tool. + // then the warning log will be printed when addEventListener called. + // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md + // We have not yet found a neat way to using passive. Because in zrender the dom event + // listener delegate all of the upper events of element. Some of those events need + // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts. + // Before passive can be adopted, these issues should be considered: + // (1) Whether and how a zrender user specifies an event listener passive. And by default, + // passive or not. + // (2) How to tread that some zrender event listener is passive, and some is not. If + // we use other way but not preventDefault of mousewheel and touchmove, browser + // compatibility should be handled. + + // const opts = (env.passiveSupported && name === 'mousewheel') + // ? {passive: true} + // // By default, the third param of el.addEventListener is `capture: false`. + // : void 0; + // el.addEventListener(name, handler /* , opts */); + el.addEventListener(name, handler, opt); } /** @@ -292,12 +282,7 @@ export function removeEventListener( handler: RemoveEventListenerParams[1], opt: RemoveEventListenerParams[2] ) { - if (isDomLevel2) { - el.removeEventListener(name, handler, opt); - } - else { - (el as any).detachEvent('on' + name, handler); - } + el.removeEventListener(name, handler, opt); } /** @@ -307,16 +292,11 @@ export function removeEventListener( * * @param {Event} e A mouse or touch event. */ -export const stop = isDomLevel2 - ? function (e: MouseEvent | TouchEvent | PointerEvent) { - e.preventDefault(); - e.stopPropagation(); - e.cancelBubble = true; - } - : function (e: MouseEvent | TouchEvent | PointerEvent) { - e.returnValue = false; - e.cancelBubble = true; - }; +export const stop = function (e: MouseEvent | TouchEvent | PointerEvent) { + e.preventDefault(); + e.stopPropagation(); + e.cancelBubble = true; +}; /** * This method only works for mouseup and mousedown. The functionality is restricted @@ -328,15 +308,6 @@ export function isMiddleOrRightButtonOnMouseUpDown(e: { which: number }) { return e.which === 2 || e.which === 3; } -/** - * To be removed. - * @deprecated - */ -export function notLeftMouse(e: MouseEvent) { - // If e.which is undefined, considered as left mouse event. - return e.which > 1; -} - // For backward compatibility export {Eventful as Dispatcher}; diff --git a/src/vml/Painter.ts b/src/vml/Painter.ts deleted file mode 100644 index 2d55f5726..000000000 --- a/src/vml/Painter.ts +++ /dev/null @@ -1,200 +0,0 @@ -// @ts-nocheck -/** - * VML Painter. - */ - -import {logError, each} from '../core/util'; -import * as vmlCore from './core'; - -function parseInt10(val) { - return parseInt(val, 10); -} - -/** - * @alias module:zrender/vml/Painter - */ -function VMLPainter(root, storage) { - - vmlCore.initVML(); - - this.root = root; - - this.storage = storage; - - var vmlViewport = document.createElement('div'); - - var vmlRoot = document.createElement('div'); - - vmlViewport.style.cssText = 'display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;'; - - vmlRoot.style.cssText = 'position:absolute;left:0;top:0;'; - - root.appendChild(vmlViewport); - - this._vmlRoot = vmlRoot; - this._vmlViewport = vmlViewport; - - this.resize(); - - // Modify storage - var oldDelFromStorage = storage.delFromStorage; - var oldAddToStorage = storage.addToStorage; - storage.delFromStorage = function (el) { - oldDelFromStorage.call(storage, el); - - if (el) { - el.onRemove && el.onRemove(vmlRoot); - } - }; - - storage.addToStorage = function (el) { - // Displayable already has a vml node - el.onAdd && el.onAdd(vmlRoot); - - oldAddToStorage.call(storage, el); - }; - - this._firstPaint = true; -} - -VMLPainter.prototype = { - - constructor: VMLPainter, - - getType: function () { - return 'vml'; - }, - - /** - * @return {HTMLDivElement} - */ - getViewportRoot: function () { - return this._vmlViewport; - }, - - getViewportRootOffset: function () { - var viewportRoot = this.getViewportRoot(); - if (viewportRoot) { - return { - offsetLeft: viewportRoot.offsetLeft || 0, - offsetTop: viewportRoot.offsetTop || 0 - }; - } - }, - - /** - * 刷新 - */ - refresh: function () { - - var list = this.storage.getDisplayList(true, true); - - this._paintList(list); - }, - - _paintList: function (list) { - var vmlRoot = this._vmlRoot; - for (var i = 0; i < list.length; i++) { - var el = list[i]; - if (el.invisible || el.ignore) { - if (!el.__alreadyNotVisible) { - el.onRemove(vmlRoot); - } - // Set as already invisible - el.__alreadyNotVisible = true; - } - else { - if (el.__alreadyNotVisible) { - el.onAdd(vmlRoot); - } - el.__alreadyNotVisible = false; - if (el.__dirty) { - el.beforeBrush && el.beforeBrush(); - (el.brushVML || el.brush).call(el, vmlRoot); - el.afterBrush && el.afterBrush(); - } - } - el.__dirty = false; - } - - if (this._firstPaint) { - // Detached from document at first time - // to avoid page refreshing too many times - - // FIXME 如果每次都先 removeChild 可能会导致一些填充和描边的效果改变 - this._vmlViewport.appendChild(vmlRoot); - this._firstPaint = false; - } - }, - - resize: function (width, height) { - var width = width == null ? this._getWidth() : width; - var height = height == null ? this._getHeight() : height; - - if (this._width !== width || this._height !== height) { - this._width = width; - this._height = height; - - var vmlViewportStyle = this._vmlViewport.style; - vmlViewportStyle.width = width + 'px'; - vmlViewportStyle.height = height + 'px'; - } - }, - - dispose: function () { - this.root.innerHTML = ''; - - this._vmlRoot = - this._vmlViewport = - this.storage = null; - }, - - getWidth: function () { - return this._width; - }, - - getHeight: function () { - return this._height; - }, - - clear: function () { - if (this._vmlViewport) { - this.root.removeChild(this._vmlViewport); - } - }, - - _getWidth: function () { - var root = this.root; - var stl = root.currentStyle; - - return ((root.clientWidth || parseInt10(stl.width)) - - parseInt10(stl.paddingLeft) - - parseInt10(stl.paddingRight)) | 0; - }, - - _getHeight: function () { - var root = this.root; - var stl = root.currentStyle; - - return ((root.clientHeight || parseInt10(stl.height)) - - parseInt10(stl.paddingTop) - - parseInt10(stl.paddingBottom)) | 0; - } -}; - -// Not supported methods -function createMethodNotSupport(method) { - return function () { - logError('In IE8.0 VML mode painter not support method "' + method + '"'); - }; -} - -// Unsupported methods -each([ - 'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers', - 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage' -], function (name) { - VMLPainter.prototype[name] = createMethodNotSupport(name); -}); - -export default VMLPainter; \ No newline at end of file diff --git a/src/vml/core.ts b/src/vml/core.ts deleted file mode 100644 index fe84edae6..000000000 --- a/src/vml/core.ts +++ /dev/null @@ -1,48 +0,0 @@ -// @ts-nocheck -import env from '../core/env'; - - -var urn = 'urn:schemas-microsoft-com:vml'; -var win = typeof window === 'undefined' ? null : window; - -var vmlInited = false; - -export var doc = win && win.document; - -export function createNode(tagName) { - return doCreateNode(tagName); -} - -// Avoid assign to an exported variable, for transforming to cjs. -var doCreateNode; - -if (doc && !env.canvasSupported) { - try { - !doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn); - doCreateNode = function (tagName) { - return doc.createElement(''); - }; - } - catch (e) { - doCreateNode = function (tagName) { - return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">'); - }; - } -} - -// From raphael -export function initVML() { - if (vmlInited || !doc) { - return; - } - vmlInited = true; - - var styleSheets = doc.styleSheets; - if (styleSheets.length < 31) { - doc.createStyleSheet().addRule('.zrvml', 'behavior:url(#default#VML)'); - } - else { - // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx - styleSheets[0].addRule('.zrvml', 'behavior:url(#default#VML)'); - } -} diff --git a/src/vml/graphic.ts b/src/vml/graphic.ts deleted file mode 100644 index b81ea6e07..000000000 --- a/src/vml/graphic.ts +++ /dev/null @@ -1,1061 +0,0 @@ -// http://www.w3.org/TR/NOTE-VML -// TODO Use proxy like svg instead of overwrite brush methods -// @ts-nocheck - -import env from '../core/env'; -import {applyTransform} from '../core/vector'; -import BoundingRect from '../core/BoundingRect'; -import * as colorTool from '../tool/color'; -import * as textContain from '../graphic/text/parse'; -import Displayable from '../graphic/Displayable'; -import ZRImage from '../graphic/Image'; -import Text from '../graphic/TSpan'; -import Path from '../graphic/Path'; -import PathProxy from '../core/PathProxy'; -import Gradient from '../graphic/Gradient'; -import * as vmlCore from './core'; - -var CMD = PathProxy.CMD; -var round = Math.round; -var sqrt = Math.sqrt; -var abs = Math.abs; -var cos = Math.cos; -var sin = Math.sin; -var mathMax = Math.max; - -if (!env.canvasSupported) { - - var comma = ','; - var imageTransformPrefix = 'progid:DXImageTransform.Microsoft'; - - var Z = 21600; - var Z2 = Z / 2; - - var ZLEVEL_BASE = 100000; - var Z_BASE = 1000; - - var initRootElStyle = function (el) { - el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;'; - el.coordsize = Z + ',' + Z; - el.coordorigin = '0,0'; - }; - - var encodeHtmlAttribute = function (s) { - return String(s).replace(/&/g, '&').replace(/"/g, '"'); - }; - - var rgb2Str = function (r, g, b) { - return 'rgb(' + [r, g, b].join(',') + ')'; - }; - - var append = function (parent, child) { - if (child && parent && child.parentNode !== parent) { - parent.appendChild(child); - } - }; - - var remove = function (parent, child) { - if (child && parent && child.parentNode === parent) { - parent.removeChild(child); - } - }; - - var getZIndex = function (zlevel, z, z2) { - // z 的取值范围为 [0, 1000] - return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE + z2; - }; - - var parsePercent = textHelper.parsePercent; - - /*************************************************** - * PATH - **************************************************/ - - var setColorAndOpacity = function (el, color, opacity) { - var colorArr = colorTool.parse(color); - opacity = +opacity; - if (isNaN(opacity)) { - opacity = 1; - } - if (colorArr) { - el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]); - el.opacity = opacity * colorArr[3]; - } - }; - - var getColorAndAlpha = function (color) { - var colorArr = colorTool.parse(color); - return [ - rgb2Str(colorArr[0], colorArr[1], colorArr[2]), - colorArr[3] - ]; - }; - - var updateFillNode = function (el, style, zrEl) { - // TODO pattern - var fill = style.fill; - if (fill != null) { - // Modified from excanvas - if (fill instanceof Gradient) { - var gradientType; - var angle = 0; - var focus = [0, 0]; - // additional offset - var shift = 0; - // scale factor for offset - var expansion = 1; - var rect = zrEl.getBoundingRect(); - var rectWidth = rect.width; - var rectHeight = rect.height; - if (fill.type === 'linear') { - gradientType = 'gradient'; - var transform = zrEl.transform; - var p0 = [fill.x * rectWidth, fill.y * rectHeight]; - var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight]; - if (transform) { - applyTransform(p0, p0, transform); - applyTransform(p1, p1, transform); - } - var dx = p1[0] - p0[0]; - var dy = p1[1] - p0[1]; - angle = Math.atan2(dx, dy) * 180 / Math.PI; - // The angle should be a non-negative number. - if (angle < 0) { - angle += 360; - } - - // Very small angles produce an unexpected result because they are - // converted to a scientific notation string. - if (angle < 1e-6) { - angle = 0; - } - } - else { - gradientType = 'gradientradial'; - var p0 = [fill.x * rectWidth, fill.y * rectHeight]; - var transform = zrEl.transform; - var scale = zrEl.scale; - var width = rectWidth; - var height = rectHeight; - focus = [ - // Percent in bounding rect - (p0[0] - rect.x) / width, - (p0[1] - rect.y) / height - ]; - if (transform) { - applyTransform(p0, p0, transform); - } - - width /= scale[0] * Z; - height /= scale[1] * Z; - var dimension = mathMax(width, height); - shift = 2 * 0 / dimension; - expansion = 2 * fill.r / dimension - shift; - } - - // We need to sort the color stops in ascending order by offset, - // otherwise IE won't interpret it correctly. - var stops = fill.colorStops.slice(); - stops.sort(function (cs1, cs2) { - return cs1.offset - cs2.offset; - }); - - var length = stops.length; - // Color and alpha list of first and last stop - var colorAndAlphaList = []; - var colors = []; - for (var i = 0; i < length; i++) { - var stop = stops[i]; - var colorAndAlpha = getColorAndAlpha(stop.color); - colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]); - if (i === 0 || i === length - 1) { - colorAndAlphaList.push(colorAndAlpha); - } - } - - if (length >= 2) { - var color1 = colorAndAlphaList[0][0]; - var color2 = colorAndAlphaList[1][0]; - var opacity1 = colorAndAlphaList[0][1] * style.opacity; - var opacity2 = colorAndAlphaList[1][1] * style.opacity; - - el.type = gradientType; - el.method = 'none'; - el.focus = '100%'; - el.angle = angle; - el.color = color1; - el.color2 = color2; - el.colors = colors.join(','); - // When colors attribute is used, the meanings of opacity and o:opacity2 - // are reversed. - el.opacity = opacity2; - // FIXME g_o_:opacity ? - el.opacity2 = opacity1; - } - if (gradientType === 'radial') { - el.focusposition = focus.join(','); - } - } - else { - // FIXME Change from Gradient fill to color fill - setColorAndOpacity(el, fill, style.opacity); - } - } - }; - - var updateStrokeNode = function (el, style) { - // if (style.lineJoin != null) { - // el.joinstyle = style.lineJoin; - // } - // if (style.miterLimit != null) { - // el.miterlimit = style.miterLimit * Z; - // } - // if (style.lineCap != null) { - // el.endcap = style.lineCap; - // } - if (style.lineDash) { - el.dashstyle = style.lineDash.join(' '); - } - if (style.stroke != null && !(style.stroke instanceof Gradient)) { - setColorAndOpacity(el, style.stroke, style.opacity); - } - }; - - var updateFillAndStroke = function (vmlEl, type, style, zrEl) { - var isFill = type === 'fill'; - var el = vmlEl.getElementsByTagName(type)[0]; - // Stroke must have lineWidth - if (style[type] != null && style[type] !== 'none' && (isFill || (!isFill && style.lineWidth))) { - vmlEl[isFill ? 'filled' : 'stroked'] = 'true'; - // FIXME Remove before updating, or set `colors` will throw error - if (style[type] instanceof Gradient) { - remove(vmlEl, el); - } - if (!el) { - el = vmlCore.createNode(type); - } - - isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style); - append(vmlEl, el); - } - else { - vmlEl[isFill ? 'filled' : 'stroked'] = 'false'; - remove(vmlEl, el); - } - }; - - var points = [[], [], []]; - var pathDataToString = function (path, m) { - var M = CMD.M; - var C = CMD.C; - var L = CMD.L; - var A = CMD.A; - var Q = CMD.Q; - - var str = []; - var nPoint; - var cmdStr; - var cmd; - var i; - var xi; - var yi; - var data = path.data; - var dataLength = path.len(); - for (i = 0; i < dataLength;) { - cmd = data[i++]; - cmdStr = ''; - nPoint = 0; - switch (cmd) { - case M: - cmdStr = ' m '; - nPoint = 1; - xi = data[i++]; - yi = data[i++]; - points[0][0] = xi; - points[0][1] = yi; - break; - case L: - cmdStr = ' l '; - nPoint = 1; - xi = data[i++]; - yi = data[i++]; - points[0][0] = xi; - points[0][1] = yi; - break; - case Q: - case C: - cmdStr = ' c '; - nPoint = 3; - var x1 = data[i++]; - var y1 = data[i++]; - var x2 = data[i++]; - var y2 = data[i++]; - var x3; - var y3; - if (cmd === Q) { - // Convert quadratic to cubic using degree elevation - x3 = x2; - y3 = y2; - x2 = (x2 + 2 * x1) / 3; - y2 = (y2 + 2 * y1) / 3; - x1 = (xi + 2 * x1) / 3; - y1 = (yi + 2 * y1) / 3; - } - else { - x3 = data[i++]; - y3 = data[i++]; - } - points[0][0] = x1; - points[0][1] = y1; - points[1][0] = x2; - points[1][1] = y2; - points[2][0] = x3; - points[2][1] = y3; - - xi = x3; - yi = y3; - break; - case A: - var x = 0; - var y = 0; - var sx = 1; - var sy = 1; - var angle = 0; - if (m) { - // Extract SRT from matrix - x = m[4]; - y = m[5]; - sx = sqrt(m[0] * m[0] + m[1] * m[1]); - sy = sqrt(m[2] * m[2] + m[3] * m[3]); - angle = Math.atan2(-m[1] / sy, m[0] / sx); - } - - var cx = data[i++]; - var cy = data[i++]; - var rx = data[i++]; - var ry = data[i++]; - var startAngle = data[i++] + angle; - var endAngle = data[i++] + startAngle + angle; - // FIXME - // var psi = data[i++]; - i++; - var clockwise = data[i++]; - - var x0 = cx + cos(startAngle) * rx; - var y0 = cy + sin(startAngle) * ry; - - var x1 = cx + cos(endAngle) * rx; - var y1 = cy + sin(endAngle) * ry; - - var type = clockwise ? ' wa ' : ' at '; - if (Math.abs(x0 - x1) < 1e-4) { - // IE won't render arches drawn counter clockwise if x0 == x1. - if (Math.abs(endAngle - startAngle) > 1e-2) { - // Offset x0 by 1/80 of a pixel. Use something - // that can be represented in binary - if (clockwise) { - x0 += 270 / Z; - } - } - else { - // Avoid case draw full circle - if (Math.abs(y0 - cy) < 1e-4) { - if ((clockwise && x0 < cx) || (!clockwise && x0 > cx)) { - y1 -= 270 / Z; - } - else { - y1 += 270 / Z; - } - } - else if ((clockwise && y0 < cy) || (!clockwise && y0 > cy)) { - x1 += 270 / Z; - } - else { - x1 -= 270 / Z; - } - } - } - str.push( - type, - round(((cx - rx) * sx + x) * Z - Z2), comma, - round(((cy - ry) * sy + y) * Z - Z2), comma, - round(((cx + rx) * sx + x) * Z - Z2), comma, - round(((cy + ry) * sy + y) * Z - Z2), comma, - round((x0 * sx + x) * Z - Z2), comma, - round((y0 * sy + y) * Z - Z2), comma, - round((x1 * sx + x) * Z - Z2), comma, - round((y1 * sy + y) * Z - Z2) - ); - - xi = x1; - yi = y1; - break; - case CMD.R: - var p0 = points[0]; - var p1 = points[1]; - // x0, y0 - p0[0] = data[i++]; - p0[1] = data[i++]; - // x1, y1 - p1[0] = p0[0] + data[i++]; - p1[1] = p0[1] + data[i++]; - - if (m) { - applyTransform(p0, p0, m); - applyTransform(p1, p1, m); - } - - p0[0] = round(p0[0] * Z - Z2); - p1[0] = round(p1[0] * Z - Z2); - p0[1] = round(p0[1] * Z - Z2); - p1[1] = round(p1[1] * Z - Z2); - str.push( - // x0, y0 - ' m ', p0[0], comma, p0[1], - // x1, y0 - ' l ', p1[0], comma, p0[1], - // x1, y1 - ' l ', p1[0], comma, p1[1], - // x0, y1 - ' l ', p0[0], comma, p1[1] - ); - break; - case CMD.Z: - // FIXME Update xi, yi - str.push(' x '); - } - - if (nPoint > 0) { - str.push(cmdStr); - for (var k = 0; k < nPoint; k++) { - var p = points[k]; - - m && applyTransform(p, p, m); - // 不 round 会非常慢 - str.push( - round(p[0] * Z - Z2), comma, round(p[1] * Z - Z2), - k < nPoint - 1 ? comma : '' - ); - } - } - } - - return str.join(''); - }; - - // Rewrite the original path method - Path.prototype.brushVML = function (vmlRoot) { - var style = this.style; - - var vmlEl = this._vmlEl; - if (!vmlEl) { - vmlEl = vmlCore.createNode('shape'); - initRootElStyle(vmlEl); - - this._vmlEl = vmlEl; - } - - updateFillAndStroke(vmlEl, 'fill', style, this); - updateFillAndStroke(vmlEl, 'stroke', style, this); - - var m = this.transform; - var needTransform = m != null; - var strokeEl = vmlEl.getElementsByTagName('stroke')[0]; - if (strokeEl) { - var lineWidth = style.lineWidth; - // Get the line scale. - // Determinant of this.m_ means how much the area is enlarged by the - // transformation. So its square root can be used as a scale factor - // for width. - if (needTransform && !style.strokeNoScale) { - var det = m[0] * m[3] - m[1] * m[2]; - lineWidth *= sqrt(abs(det)); - } - strokeEl.weight = lineWidth + 'px'; - } - - var path = this.path || (this.path = new PathProxy()); - if (this.__dirtyPath) { - path.beginPath(); - path.subPixelOptimize = false; - this.buildPath(path, this.shape); - path.toStatic(); - this.__dirtyPath = false; - } - - vmlEl.path = pathDataToString(path, this.transform); - - vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); - - // Append to root - append(vmlRoot, vmlEl); - - // Text - if (style.text != null) { - this.drawRectText(vmlRoot, this.getBoundingRect()); - } - else { - this.removeRectText(vmlRoot); - } - }; - - Path.prototype.onRemove = function (vmlRoot) { - remove(vmlRoot, this._vmlEl); - this.removeRectText(vmlRoot); - }; - - Path.prototype.onAdd = function (vmlRoot) { - append(vmlRoot, this._vmlEl); - this.appendRectText(vmlRoot); - }; - - /*************************************************** - * IMAGE - **************************************************/ - var isImage = function (img) { - // FIXME img instanceof Image 如果 img 是一个字符串的时候,IE8 下会报错 - return (typeof img === 'object') && img.tagName && img.tagName.toUpperCase() === 'IMG'; - // return img instanceof Image; - }; - - // Rewrite the original path method - ZRImage.prototype.brushVML = function (vmlRoot) { - var style = this.style; - var image = style.image; - - // Image original width, height - var ow; - var oh; - - if (isImage(image)) { - var src = image.src; - if (src === this._imageSrc) { - ow = this._imageWidth; - oh = this._imageHeight; - } - else { - var imageRuntimeStyle = image.runtimeStyle; - var oldRuntimeWidth = imageRuntimeStyle.width; - var oldRuntimeHeight = imageRuntimeStyle.height; - imageRuntimeStyle.width = 'auto'; - imageRuntimeStyle.height = 'auto'; - - // get the original size - ow = image.width; - oh = image.height; - - // and remove overides - imageRuntimeStyle.width = oldRuntimeWidth; - imageRuntimeStyle.height = oldRuntimeHeight; - - // Caching image original width, height and src - this._imageSrc = src; - this._imageWidth = ow; - this._imageHeight = oh; - } - image = src; - } - else { - if (image === this._imageSrc) { - ow = this._imageWidth; - oh = this._imageHeight; - } - } - if (!image) { - return; - } - - var x = style.x || 0; - var y = style.y || 0; - - var dw = style.width; - var dh = style.height; - - var sw = style.sWidth; - var sh = style.sHeight; - var sx = style.sx || 0; - var sy = style.sy || 0; - - var hasCrop = sw && sh; - - var vmlEl = this._vmlEl; - if (!vmlEl) { - // FIXME 使用 group 在 left, top 都不是 0 的时候就无法显示了。 - // vmlEl = vmlCore.createNode('group'); - vmlEl = vmlCore.doc.createElement('div'); - initRootElStyle(vmlEl); - - this._vmlEl = vmlEl; - } - - var vmlElStyle = vmlEl.style; - var hasRotation = false; - var m; - var scaleX = 1; - var scaleY = 1; - if (this.transform) { - m = this.transform; - scaleX = sqrt(m[0] * m[0] + m[1] * m[1]); - scaleY = sqrt(m[2] * m[2] + m[3] * m[3]); - - hasRotation = m[1] || m[2]; - } - if (hasRotation) { - // If filters are necessary (rotation exists), create them - // filters are bog-slow, so only create them if abbsolutely necessary - // The following check doesn't account for skews (which don't exist - // in the canvas spec (yet) anyway. - // From excanvas - var p0 = [x, y]; - var p1 = [x + dw, y]; - var p2 = [x, y + dh]; - var p3 = [x + dw, y + dh]; - applyTransform(p0, p0, m); - applyTransform(p1, p1, m); - applyTransform(p2, p2, m); - applyTransform(p3, p3, m); - - var maxX = mathMax(p0[0], p1[0], p2[0], p3[0]); - var maxY = mathMax(p0[1], p1[1], p2[1], p3[1]); - - var transformFilter = []; - transformFilter.push('M11=', m[0] / scaleX, comma, - 'M12=', m[2] / scaleY, comma, - 'M21=', m[1] / scaleX, comma, - 'M22=', m[3] / scaleY, comma, - 'Dx=', round(x * scaleX + m[4]), comma, - 'Dy=', round(y * scaleY + m[5])); - - vmlElStyle.padding = '0 ' + round(maxX) + 'px ' + round(maxY) + 'px 0'; - // FIXME DXImageTransform 在 IE11 的兼容模式下不起作用 - vmlElStyle.filter = imageTransformPrefix + '.Matrix(' - + transformFilter.join('') + ', SizingMethod=clip)'; - - } - else { - if (m) { - x = x * scaleX + m[4]; - y = y * scaleY + m[5]; - } - vmlElStyle.filter = ''; - vmlElStyle.left = round(x) + 'px'; - vmlElStyle.top = round(y) + 'px'; - } - - var imageEl = this._imageEl; - var cropEl = this._cropEl; - - if (!imageEl) { - imageEl = vmlCore.doc.createElement('div'); - this._imageEl = imageEl; - } - var imageELStyle = imageEl.style; - if (hasCrop) { - // Needs know image original width and height - if (!(ow && oh)) { - var tmpImage = new Image(); - var self = this; - tmpImage.onload = function () { - tmpImage.onload = null; - ow = tmpImage.width; - oh = tmpImage.height; - // Adjust image width and height to fit the ratio destinationSize / sourceSize - imageELStyle.width = round(scaleX * ow * dw / sw) + 'px'; - imageELStyle.height = round(scaleY * oh * dh / sh) + 'px'; - - // Caching image original width, height and src - self._imageWidth = ow; - self._imageHeight = oh; - self._imageSrc = image; - }; - tmpImage.src = image; - } - else { - imageELStyle.width = round(scaleX * ow * dw / sw) + 'px'; - imageELStyle.height = round(scaleY * oh * dh / sh) + 'px'; - } - - if (!cropEl) { - cropEl = vmlCore.doc.createElement('div'); - cropEl.style.overflow = 'hidden'; - this._cropEl = cropEl; - } - var cropElStyle = cropEl.style; - cropElStyle.width = round((dw + sx * dw / sw) * scaleX); - cropElStyle.height = round((dh + sy * dh / sh) * scaleY); - cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx=' - + (-sx * dw / sw * scaleX) + ',Dy=' + (-sy * dh / sh * scaleY) + ')'; - - if (!cropEl.parentNode) { - vmlEl.appendChild(cropEl); - } - if (imageEl.parentNode !== cropEl) { - cropEl.appendChild(imageEl); - } - } - else { - imageELStyle.width = round(scaleX * dw) + 'px'; - imageELStyle.height = round(scaleY * dh) + 'px'; - - vmlEl.appendChild(imageEl); - - if (cropEl && cropEl.parentNode) { - vmlEl.removeChild(cropEl); - this._cropEl = null; - } - } - - var filterStr = ''; - var alpha = style.opacity; - if (alpha < 1) { - filterStr += '.Alpha(opacity=' + round(alpha * 100) + ') '; - } - filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)'; - - imageELStyle.filter = filterStr; - - vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); - - // Append to root - append(vmlRoot, vmlEl); - - // Text - if (style.text != null) { - this.drawRectText(vmlRoot, this.getBoundingRect()); - } - }; - - ZRImage.prototype.onRemove = function (vmlRoot) { - remove(vmlRoot, this._vmlEl); - - this._vmlEl = null; - this._cropEl = null; - this._imageEl = null; - - this.removeRectText(vmlRoot); - }; - - ZRImage.prototype.onAdd = function (vmlRoot) { - append(vmlRoot, this._vmlEl); - this.appendRectText(vmlRoot); - }; - - - /*************************************************** - * TEXT - **************************************************/ - - var DEFAULT_STYLE_NORMAL = 'normal'; - - var fontStyleCache = {}; - var fontStyleCacheCount = 0; - var MAX_FONT_CACHE_SIZE = 100; - var fontEl = document.createElement('div'); - - var getFontStyle = function (fontString) { - var fontStyle = fontStyleCache[fontString]; - if (!fontStyle) { - // Clear cache - if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) { - fontStyleCacheCount = 0; - fontStyleCache = {}; - } - - var style = fontEl.style; - var fontFamily; - try { - style.font = fontString; - fontFamily = style.fontFamily.split(',')[0]; - } - catch (e) { - } - - fontStyle = { - style: style.fontStyle || DEFAULT_STYLE_NORMAL, - variant: style.fontVariant || DEFAULT_STYLE_NORMAL, - weight: style.fontWeight || DEFAULT_STYLE_NORMAL, - size: parseFloat(style.fontSize || 12) | 0, - family: fontFamily || 'Microsoft YaHei' - }; - - fontStyleCache[fontString] = fontStyle; - fontStyleCacheCount++; - } - return fontStyle; - }; - - var textMeasureEl; - // Overwrite measure text method - textContain.$override('measureText', function (text, textFont) { - var doc = vmlCore.doc; - if (!textMeasureEl) { - textMeasureEl = doc.createElement('div'); - textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;' - + 'padding:0;margin:0;border:none;white-space:pre;'; - vmlCore.doc.body.appendChild(textMeasureEl); - } - - try { - textMeasureEl.style.font = textFont; - } - catch (ex) { - // Ignore failures to set to invalid font. - } - textMeasureEl.innerHTML = ''; - // Don't use innerHTML or innerText because they allow markup/whitespace. - textMeasureEl.appendChild(doc.createTextNode(text)); - return { - width: textMeasureEl.offsetWidth - }; - }); - - var tmpRect = new BoundingRect(0, 0, 0, 0); - - var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) { - - var style = this.style; - - // Optimize, avoid normalize every time. - this.__dirty && textHelper.normalizeTextStyle(style, true); - - var text = style.text; - // Convert to string - text != null && (text += ''); - if (!text) { - return; - } - - // Convert rich text to plain text. Rich text is not supported in - // IE8-, but tags in rich text template will be removed. - if (style.rich) { - var contentBlock = textContain.parseRichText(text, style); - text = []; - for (var i = 0; i < contentBlock.lines.length; i++) { - var tokens = contentBlock.lines[i].tokens; - var textLine = []; - for (var j = 0; j < tokens.length; j++) { - textLine.push(tokens[j].text); - } - text.push(textLine.join('')); - } - text = text.join('\n'); - } - - var x; - var y; - var align = style.textAlign; - var verticalAlign = style.textVerticalAlign; - - var fontStyle = getFontStyle(style.font); - // FIXME encodeHtmlAttribute ? - var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' ' - + fontStyle.size + 'px "' + fontStyle.family + '"'; - - textRect = textRect || textContain.getBoundingRect( - text, font, align, verticalAlign, style.textPadding, style.textLineHeight - ); - - // Transform rect to view space - var m = this.transform; - // Ignore transform for text in other element - if (m && !fromTextEl) { - tmpRect.copy(rect); - tmpRect.applyTransform(m); - rect = tmpRect; - } - - if (!fromTextEl) { - var textPosition = style.textPosition; - // Text position represented by coord - if (textPosition instanceof Array) { - x = rect.x + parsePercent(textPosition[0], rect.width); - y = rect.y + parsePercent(textPosition[1], rect.height); - - align = align || 'left'; - } - else { - var res = this.calculateTextPosition - ? this.calculateTextPosition({}, style, rect) - : textContain.calculateTextPosition({}, style, rect); - x = res.x; - y = res.y; - - // Default align and baseline when has textPosition - align = align || res.align; - verticalAlign = verticalAlign || res.verticalAlign; - } - } - else { - x = rect.x; - y = rect.y; - } - - x = textContain.adjustTextX(x, textRect.width, align); - y = textContain.adjustTextY(y, textRect.height, verticalAlign); - - // Force baseline 'middle' - y += textRect.height / 2; - - // var fontSize = fontStyle.size; - // 1.75 is an arbitrary number, as there is no info about the text baseline - // switch (baseline) { - // case 'hanging': - // case 'top': - // y += fontSize / 1.75; - // break; - // case 'middle': - // break; - // default: - // // case null: - // // case 'alphabetic': - // // case 'ideographic': - // // case 'bottom': - // y -= fontSize / 2.25; - // break; - // } - - // switch (align) { - // case 'left': - // break; - // case 'center': - // x -= textRect.width / 2; - // break; - // case 'right': - // x -= textRect.width; - // break; - // case 'end': - // align = elementStyle.direction == 'ltr' ? 'right' : 'left'; - // break; - // case 'start': - // align = elementStyle.direction == 'rtl' ? 'right' : 'left'; - // break; - // default: - // align = 'left'; - // } - - var createNode = vmlCore.createNode; - - var textVmlEl = this._textVmlEl; - var pathEl; - var textPathEl; - var skewEl; - if (!textVmlEl) { - textVmlEl = createNode('line'); - pathEl = createNode('path'); - textPathEl = createNode('textpath'); - skewEl = createNode('skew'); - - // FIXME Why here is not cammel case - // Align 'center' seems wrong - textPathEl.style['v-text-align'] = 'left'; - - initRootElStyle(textVmlEl); - - pathEl.textpathok = true; - textPathEl.on = true; - - textVmlEl.from = '0 0'; - textVmlEl.to = '1000 0.05'; - - append(textVmlEl, skewEl); - append(textVmlEl, pathEl); - append(textVmlEl, textPathEl); - - this._textVmlEl = textVmlEl; - } - else { - // 这里是在前面 appendChild 保证顺序的前提下 - skewEl = textVmlEl.firstChild; - pathEl = skewEl.nextSibling; - textPathEl = pathEl.nextSibling; - } - - var coords = [x, y]; - var textVmlElStyle = textVmlEl.style; - // Ignore transform for text in other element - if (m && fromTextEl) { - applyTransform(coords, coords, m); - - skewEl.on = true; - - skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma - + m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0'; - - // Text position - skewEl.offset = (round(coords[0]) || 0) + ',' + (round(coords[1]) || 0); - // Left top point as origin - skewEl.origin = '0 0'; - - textVmlElStyle.left = '0px'; - textVmlElStyle.top = '0px'; - } - else { - skewEl.on = false; - textVmlElStyle.left = round(x) + 'px'; - textVmlElStyle.top = round(y) + 'px'; - } - - textPathEl.string = encodeHtmlAttribute(text); - // TODO - try { - textPathEl.style.font = font; - } - // Error font format - catch (e) {} - - updateFillAndStroke(textVmlEl, 'fill', { - fill: style.textFill, - opacity: style.opacity - }, this); - updateFillAndStroke(textVmlEl, 'stroke', { - stroke: style.textStroke, - opacity: style.opacity, - lineDash: style.lineDash || null // style.lineDash can be `false`. - }, this); - - textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); - - // Attached to root - append(vmlRoot, textVmlEl); - }; - - var removeRectText = function (vmlRoot) { - remove(vmlRoot, this._textVmlEl); - this._textVmlEl = null; - }; - - var appendRectText = function (vmlRoot) { - append(vmlRoot, this._textVmlEl); - }; - - var list = [RectText, Displayable, ZRImage, Path, Text]; - - // In case Displayable has been mixed in RectText - for (var i = 0; i < list.length; i++) { - var proto = list[i].prototype; - proto.drawRectText = drawRectText; - proto.removeRectText = removeRectText; - proto.appendRectText = appendRectText; - } - - Text.prototype.brushVML = function (vmlRoot) { - var style = this.style; - if (style.text != null) { - this.drawRectText(vmlRoot, { - x: style.x || 0, y: style.y || 0, - width: 0, height: 0 - }, this.getBoundingRect(), true); - } - else { - this.removeRectText(vmlRoot); - } - }; - - Text.prototype.onRemove = function (vmlRoot) { - this.removeRectText(vmlRoot); - }; - - Text.prototype.onAdd = function (vmlRoot) { - this.appendRectText(vmlRoot); - }; -} \ No newline at end of file diff --git a/src/vml/vml.ts b/src/vml/vml.ts deleted file mode 100644 index bc06baf33..000000000 --- a/src/vml/vml.ts +++ /dev/null @@ -1,7 +0,0 @@ -// @ts-nocheck - -import './graphic'; -import {registerPainter} from '../zrender'; -import Painter from './Painter'; - -registerPainter('vml', Painter); \ No newline at end of file diff --git a/src/zrender.ts b/src/zrender.ts index f454670b1..c32cecbee 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -28,8 +28,6 @@ import Path from './graphic/Path'; import Group from './graphic/Group'; -const useVML = !env.canvasSupported; - type PainterBaseCtor = { new(dom: HTMLElement, storage: Storage, ...args: any[]): PainterBase } @@ -106,13 +104,6 @@ class ZRender { let rendererType = opts.renderer || 'canvas'; - // TODO WebGL - if (process.env.NODE_ENV !== 'production') { - if (useVML) { - throw new Error('IE8 support has been dropped since 5.0'); - } - } - if (!painterCtors[rendererType]) { // Use the first registered renderer. rendererType = zrUtil.keys(painterCtors)[0]; From f9be13c4197301849fe410f1b0d7ad0dfa7d7dad Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 6 Oct 2021 23:08:18 +0800 Subject: [PATCH 040/148] wip(svg): optimize transform origin output in animation --- src/svg/cssAnimation.ts | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 6a290feac..15a73930f 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -5,7 +5,7 @@ import Path from '../graphic/Path'; import SVGPathRebuilder from './SVGPathRebuilder'; import PathProxy from '../core/PathProxy'; import { getPathPrecision, getSRTTransformString } from './helper'; -import { each, extend, isEmptyObject, isString, keys } from '../core/util'; +import { each, extend, filter, isEmptyObject, isString, keys } from '../core/util'; import Animator from '../animation/Animator'; import { CompoundPath } from '../export'; @@ -35,6 +35,8 @@ export const EASING_MAP: Record = { // TODO elastic, bounce }; +const transformOriginKey = 'transform-origin'; + function sameTransform(a: any, b: any) { for (let i = 0; i < TRANSFORMABLE_PROPS.length; i++) { const prop = TRANSFORMABLE_PROPS[i]; @@ -69,7 +71,7 @@ function col2str(rgba: number[]): string { function setTransformOrigin(target: Record, transform: Transformable) { const {originX, originY} = transform; if (originX || originY) { - target['transform-origin'] = `${originX}px ${originY}px`; + target[transformOriginKey] = `${originX}px ${originY}px`; } } @@ -241,9 +243,11 @@ export function createCSSAnimation( const transform = {} as Transformable; copyTransform(transform, el); extend(transform, transformKfs[percent]); - finalKfs[percent] = { - transform: getSRTTransformString(transform) - }; + const str = getSRTTransformString(transform); + finalKfs[percent] = str ? { + transform: str + } : {}; + // TODO set transform origin in element? setTransformOrigin(finalKfs[percent], transform); }; @@ -287,10 +291,31 @@ export function createCSSAnimation( } const percents = keys(finalKfs); - if (percents.length && !isEmptyObject(finalKfs[percents[0]])) { + + // Set transform origin in attribute to reduce the size. + let allTransformOriginSame = true; + for (let i = 1; i < percents.length; i++) { + const p0 = percents[i - 1]; + const p1 = percents[i]; + if (finalKfs[p0][transformOriginKey] && finalKfs[p0][transformOriginKey] !== finalKfs[p1][transformOriginKey]) { + allTransformOriginSame = false; + break; + } + } + if (allTransformOriginSame) { + for (const percent in finalKfs) { + if (finalKfs[percent][transformOriginKey]) { + delete finalKfs[percent][transformOriginKey]; + } + } + } + + if (filter( + percents, (percent) => !isEmptyObject(finalKfs[percent]) + ).length) { const animationName = addAnimation(finalKfs, scope); // eslint-disable-next-line - for (let attrName in finalKfs[percents[0]]) { + for (const attrName in finalKfs[percents[0]]) { // Remove the attrs in the element because it will be set by animation. // Reduce the size. attrs[attrName] = false; From 5a511ac8053ee63563a5ffbb0b53a39e310ce830 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 6 Oct 2021 23:14:38 +0800 Subject: [PATCH 041/148] wip(svg): fix transform origin in animation --- src/svg/cssAnimation.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 15a73930f..7b0c9542b 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -294,20 +294,23 @@ export function createCSSAnimation( // Set transform origin in attribute to reduce the size. let allTransformOriginSame = true; + let transformOrigin; for (let i = 1; i < percents.length; i++) { const p0 = percents[i - 1]; const p1 = percents[i]; - if (finalKfs[p0][transformOriginKey] && finalKfs[p0][transformOriginKey] !== finalKfs[p1][transformOriginKey]) { + if (finalKfs[p0][transformOriginKey] !== finalKfs[p1][transformOriginKey]) { allTransformOriginSame = false; break; } + transformOrigin = finalKfs[p0][transformOriginKey]; } - if (allTransformOriginSame) { + if (allTransformOriginSame && transformOrigin) { for (const percent in finalKfs) { if (finalKfs[percent][transformOriginKey]) { delete finalKfs[percent][transformOriginKey]; } } + attrs[transformOriginKey] = transformOrigin; } if (filter( From 1fe5a47ac40e55db40d3af354e9d017f233ed606 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 7 Oct 2021 14:07:29 +0800 Subject: [PATCH 042/148] wip(svg): fix class and id conflicts in multiple instances. optimize circle path difference with previous version --- src/svg/Painter.ts | 21 +++++++++++++++------ src/svg/SVGPathRebuilder.ts | 18 ++++++++++++++---- src/svg/core.ts | 5 ++++- src/svg/cssAnimation.ts | 6 +++--- src/svg/graphic.ts | 8 ++++---- src/svg/patch.ts | 7 ++++++- 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 6b7740568..a9ed063f7 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -21,11 +21,12 @@ import { createBrushScope } from './core'; import { normalizeColor } from './helper'; -import { extend, keys, logError, map } from '../core/util'; +import { disableUserSelect, extend, keys, logError, map } from '../core/util'; import Path from '../graphic/Path'; import patch from './patch'; import { getSize } from '../canvas/helper'; +let svgId = 0; interface SVGPainterOption { width?: number @@ -55,11 +56,19 @@ class SVGPainter implements PainterBase { private _backgroundColor: string - constructor(root: HTMLElement, storage: Storage, opts: SVGPainterOption, zrId: number) { + private _id: string + + constructor(root: HTMLElement, storage: Storage, opts: SVGPainterOption) { this.storage = storage; this._opts = opts = extend({}, opts); this.root = root; + // A unique id for generating svg ids. + this._id = 'zr' + svgId++; + + if (root && root.style) { + disableUserSelect(root); + } if (root && !opts.ssr) { const viewport = this._viewport = document.createElement('div'); @@ -67,7 +76,6 @@ class SVGPainter implements PainterBase { const svgDom = this._svgDom = createElement('svg'); root.appendChild(viewport); viewport.appendChild(svgDom); - } this.resize(opts.width, opts.height); @@ -105,7 +113,7 @@ class SVGPainter implements PainterBase { } renderOneToVNode(el: Displayable) { - return brush(el, createBrushScope()); + return brush(el, createBrushScope(this._id)); } renderToVNode(opts?: { @@ -121,7 +129,7 @@ class SVGPainter implements PainterBase { const width = this._width + ''; const height = this._height + ''; - const scope = createBrushScope(); + const scope = createBrushScope(this._id); scope.animation = opts.animation; scope.willUpdate = opts.willUpdate; scope.compress = opts.compress; @@ -214,6 +222,7 @@ class SVGPainter implements PainterBase { let clipPathsGroupsStackDepth = 0; let currentClipPathGroup; let prevClipPaths: Path[]; + let clipGroupNodeIdx = 0; for (let i = 0; i < listLen; i++) { const displayable = list[i]; if (!displayable.invisible) { @@ -245,7 +254,7 @@ class SVGPainter implements PainterBase { ); const g = createVNode( 'g', - 'clip-g-' + clipPaths[i].id, + 'clip-g-' + clipGroupNodeIdx++, groupAttrs, [] ); diff --git a/src/svg/SVGPathRebuilder.ts b/src/svg/SVGPathRebuilder.ts index 3ad4f665d..e463464ea 100644 --- a/src/svg/SVGPathRebuilder.ts +++ b/src/svg/SVGPathRebuilder.ts @@ -84,12 +84,22 @@ export default class SVGPathRebuilder implements PathRebuilder { // It will not draw if start point and end point are exactly the same // We need to add two arcs if (isCircle) { + const p = this._p; + const dTheta = (clockwise ? 1 : -1) * (PI2 - 1 / p); this._add( - 'A', rx, ry, xRot, +large, +clockwise, - cx + rx * mathCos(startAngle + PI), - cy + ry * mathSin(startAngle + PI) + 'A', rx, ry, xRot, 1, +clockwise, + cx + rx * mathCos(startAngle + dTheta), + cy + ry * mathSin(startAngle + dTheta) ); - this._add('A', rx, ry, xRot, +large, +clockwise, x0, y0); + // TODO. + // Usually we can simply divide the circle into two halfs arcs. + // But it will cause slightly diff with previous screenshot. + // We can't tell it but visual regression test can. To avoid too much breaks. + // We keep the logic on the browser as before. + // But in SSR mode wich has lower precision. We close the circle by adding another arc. + if (p > 1e-2) { + this._add('A', rx, ry, xRot, 0, +clockwise, x0, y0); + } } else { const x = cx + rx * mathCos(endAngle); diff --git a/src/svg/core.ts b/src/svg/core.ts index 75b1dffd9..194528750 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -115,6 +115,8 @@ export function getCssString( export interface BrushScope { + zrId: string + shadowCache: Record gradientCache: Record patternCache: Record @@ -149,8 +151,9 @@ export interface BrushScope { compress?: boolean } -export function createBrushScope(): BrushScope { +export function createBrushScope(zrId: string): BrushScope { return { + zrId, shadowCache: {}, patternCache: {}, gradientCache: {}, diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 7b0c9542b..9e200e57b 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -1,6 +1,6 @@ import Transformable, { copyTransform, TRANSFORMABLE_PROPS } from '../core/Transformable'; import Displayable from '../graphic/Displayable'; -import { SVGVNodeAttrs, BrushScope, createBrushScope } from './core'; +import { SVGVNodeAttrs, BrushScope, createBrushScope} from './core'; import Path from '../graphic/Path'; import SVGPathRebuilder from './SVGPathRebuilder'; import PathProxy from '../core/PathProxy'; @@ -101,7 +101,7 @@ function createCompoundPathCSSAnimation( let cssAnimationCfg: string; let cssAnimationName: string; each(paths, path => { - const subScope = createBrushScope(); + const subScope = createBrushScope(scope.zrId); subScope.animation = true; createCSSAnimation(path, {}, subScope, true); const cssAnims = subScope.cssAnims; @@ -337,7 +337,7 @@ export function createCSSAnimation( } if (cssAnimations.length) { - const className = 'zr-cls-' + scope.cssClassIdx++; + const className = scope.zrId + '-cls-' + scope.cssClassIdx++; scope.cssNodes['.' + className] = { animation: cssAnimations.join(',') }; diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 3bcb9c14d..44a7364ba 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -317,7 +317,7 @@ function setShadow( const stdDy = blur / 2 / scaleY; const stdDeviation = stdDx + ' ' + stdDy; // Use a simple prefix to reduce the size - shadowId = 's' + scope.shadowIdx++; + shadowId = scope.zrId + '-s' + scope.shadowIdx++; scope.defs[shadowId] = createVNode( 'filter', shadowId, { @@ -410,7 +410,7 @@ function setGradient( const gradientCache = scope.gradientCache; let gradientId = gradientCache[gradientKey]; if (!gradientId) { - gradientId = 'g' + scope.gradientIdx++; + gradientId = scope.zrId + '-g' + scope.gradientIdx++; gradientCache[gradientKey] = gradientId; gradientAttrs.id = gradientId; @@ -514,7 +514,7 @@ function setPattern( const patternCache = scope.patternCache; let patternId = patternCache[patternKey]; if (!patternId) { - patternId = 'p' + scope.patternIdx++; + patternId = scope.zrId + '-p' + scope.patternIdx++; patternCache[patternKey] = patternId; patternAttrs.id = patternId; patternVNode = scope.defs[patternId] = createVNode( @@ -536,7 +536,7 @@ export function setClipPath( const {clipPathCache, defs} = scope; let clipPathId = clipPathCache[clipPath.id]; if (!clipPathId) { - clipPathId = 'c' + scope.clipPathIdx++; + clipPathId = scope.zrId + '-c' + scope.clipPathIdx++; const clipPathAttrs: SVGVNodeAttrs = { id: clipPathId }; diff --git a/src/svg/patch.ts b/src/svg/patch.ts index 10867939e..34f5d92c5 100644 --- a/src/svg/patch.ts +++ b/src/svg/patch.ts @@ -38,6 +38,11 @@ function createKeyToOldIdx( for (let i = beginIdx; i <= endIdx; ++i) { const key = children[i].key; if (key !== undefined) { + if (process.env.NODE_ENV !== 'production') { + if (map[key] != null) { + console.error(`Duplicate key ${key}`); + } + } map[key as string] = i; } } @@ -246,7 +251,7 @@ function updateChildren( newStartVnode = newCh[++newStartIdx]; } else { - if (oldKeyToIdx === undefined) { + if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); } idxInOld = oldKeyToIdx[newStartVnode.key as string]; From 3ee98e1f67adeea2064cf2a644f7577ce34efa78 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 7 Oct 2021 16:13:50 +0800 Subject: [PATCH 043/148] wip(svg): fix typo --- src/svg/SVGPathRebuilder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/svg/SVGPathRebuilder.ts b/src/svg/SVGPathRebuilder.ts index e463464ea..1c9cc2245 100644 --- a/src/svg/SVGPathRebuilder.ts +++ b/src/svg/SVGPathRebuilder.ts @@ -84,8 +84,8 @@ export default class SVGPathRebuilder implements PathRebuilder { // It will not draw if start point and end point are exactly the same // We need to add two arcs if (isCircle) { - const p = this._p; - const dTheta = (clockwise ? 1 : -1) * (PI2 - 1 / p); + const p = 1 / this._p; + const dTheta = (clockwise ? 1 : -1) * (PI2 - p); this._add( 'A', rx, ry, xRot, 1, +clockwise, cx + rx * mathCos(startAngle + dTheta), From 294de3416916ff27d84275c1ea3b7c6085c7493d Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 7 Oct 2021 16:26:01 +0800 Subject: [PATCH 044/148] wip(svg): keep text empty space logic --- src/svg/graphic.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 44a7364ba..e34622bda 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -265,7 +265,8 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { 'dominant-baseline': 'central', 'text-anchor': textAlign }; - if (text.match(/\s\s/)) { + if (text.match(/\s/)) { + // only enabled when have space in text. attrs['xml:space'] = 'preserve'; } if (x) { From 7140c05ad4d4c54bbf716d783c11948d90201877 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 7 Oct 2021 19:05:30 +0800 Subject: [PATCH 045/148] wip(svg): fix animation name. --- src/svg/cssAnimation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 9e200e57b..e3dfd734d 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -86,7 +86,7 @@ export const ANIMATE_STYLE_MAP: Record = { type CssKF = Record; function addAnimation(cssAnim: Record, scope: BrushScope) { - const animationName = 'zr-ani-' + scope.cssAnimIdx++; + const animationName = scope.zrId + '-ani-' + scope.cssAnimIdx++; scope.cssAnims[animationName] = cssAnim; return animationName; } From 95215f5ba6344a2f24e002d63bb7f6cb536016e7 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 10:09:52 +0800 Subject: [PATCH 046/148] wip(svg): fix some issues after review --- src/core/util.ts | 5 ----- src/svg-legacy/helper/PatternManager.ts | 1 + src/svg/Painter.ts | 6 +++-- src/svg/cssAnimation.ts | 4 ++-- src/svg/helper.ts | 30 ++++++++++++------------- zrender | 1 - 6 files changed, 22 insertions(+), 25 deletions(-) delete mode 120000 zrender diff --git a/src/core/util.ts b/src/core/util.ts index adb8b2a75..2fa3c08cd 100644 --- a/src/core/util.ts +++ b/src/core/util.ts @@ -453,11 +453,6 @@ export function keys(obj: T): (KeyOfDistributive & string)[ return keyList; } -export function isEmptyObject(val: any): boolean { - return !keys(val).length; -} - - // Remove this type in returned function. Or it will conflicts wicth callback with given context. Like Eventful. // According to lib.es5.d.ts /* eslint-disable max-len*/ diff --git a/src/svg-legacy/helper/PatternManager.ts b/src/svg-legacy/helper/PatternManager.ts index c678bd4a9..f2474a32a 100644 --- a/src/svg-legacy/helper/PatternManager.ts +++ b/src/svg-legacy/helper/PatternManager.ts @@ -119,6 +119,7 @@ export default class PatternManager extends Definable { */ updateDom(pattern: PatternObject, patternDom: SVGElement) { if (isSVGPattern(pattern)) { + // New SVGPattern will not been supported in the legacy SVG renderer. // const svgElement = pattern.svgElement; // const isStringSVG = typeof svgElement === 'string'; // if (isStringSVG || svgElement.parentNode !== patternDom) { diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index a9ed063f7..280f0dc92 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -148,7 +148,7 @@ class SVGPainter implements PainterBase { y: '0', id: '0', fill: color, - fillOpacity: opacity + 'fill-opacity': opacity } ); children.push(this._bgVNode); @@ -331,7 +331,9 @@ class SVGPainter implements PainterBase { this._svgDom = this._viewport = this.storage = - this._oldVNode = null; + this._oldVNode = + this._bgVNode = + this._mainVNode = null; } clear() { if (this._svgDom) { diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index e3dfd734d..1f852e012 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -5,7 +5,7 @@ import Path from '../graphic/Path'; import SVGPathRebuilder from './SVGPathRebuilder'; import PathProxy from '../core/PathProxy'; import { getPathPrecision, getSRTTransformString } from './helper'; -import { each, extend, filter, isEmptyObject, isString, keys } from '../core/util'; +import { each, extend, filter, isString, keys } from '../core/util'; import Animator from '../animation/Animator'; import { CompoundPath } from '../export'; @@ -314,7 +314,7 @@ export function createCSSAnimation( } if (filter( - percents, (percent) => !isEmptyObject(finalKfs[percent]) + percents, (percent) => keys(finalKfs[percent]).length > 0 ).length) { const animationName = addAnimation(finalKfs, scope); // eslint-disable-next-line diff --git a/src/svg/helper.ts b/src/svg/helper.ts index df87d69be..df49c461a 100644 --- a/src/svg/helper.ts +++ b/src/svg/helper.ts @@ -67,7 +67,7 @@ export const TEXT_ALIGN_TO_ANCHOR = { }; export function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number { - // TODO Other transformues. + // TODO Other baselines. if (textBaseline === 'top') { y += lineHeight / 2; } @@ -108,28 +108,28 @@ export function getClipPathsKey(clipPaths: Path[]) { return key.join(','); } -export function isImagePattern(transformue: any): transformue is ImagePatternObject { - return transformue && (!!(transformue as ImagePatternObject).image); +export function isImagePattern(val: any): val is ImagePatternObject { + return val && (!!(val as ImagePatternObject).image); } -export function isSVGPattern(transformue: any): transformue is SVGPatternObject { - return transformue && (!!(transformue as SVGPatternObject).svgElement); +export function isSVGPattern(val: any): val is SVGPatternObject { + return val && (!!(val as SVGPatternObject).svgElement); } -export function isPattern(transformue: any): transformue is PatternObject { - return isImagePattern(transformue) || isSVGPattern(transformue); +export function isPattern(val: any): val is PatternObject { + return isImagePattern(val) || isSVGPattern(val); } -export function isLinearGradient(transformue: GradientObject): transformue is LinearGradientObject { - return transformue.type === 'linear'; +export function isLinearGradient(val: GradientObject): val is LinearGradientObject { + return val.type === 'linear'; } -export function isRadialGradient(transformue: GradientObject): transformue is RadialGradientObject { - return transformue.type === 'radial'; +export function isRadialGradient(val: GradientObject): val is RadialGradientObject { + return val.type === 'radial'; } -export function isGradient(transformue: any): transformue is GradientObject { - return transformue && ( - (transformue as GradientObject).type === 'linear' - || (transformue as GradientObject).type === 'radial' +export function isGradient(val: any): val is GradientObject { + return val && ( + (val as GradientObject).type === 'linear' + || (val as GradientObject).type === 'radial' ); } diff --git a/zrender b/zrender deleted file mode 120000 index 475836947..000000000 --- a/zrender +++ /dev/null @@ -1 +0,0 @@ -/Users/lang/Develop/zrender \ No newline at end of file From 38d9436aa9d15294502a3e878d48e0a32a17924b Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 10:13:00 +0800 Subject: [PATCH 047/148] wip(svg): add explain comment --- src/svg-legacy/helper/PatternManager.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/svg-legacy/helper/PatternManager.ts b/src/svg-legacy/helper/PatternManager.ts index f2474a32a..38afcbda3 100644 --- a/src/svg-legacy/helper/PatternManager.ts +++ b/src/svg-legacy/helper/PatternManager.ts @@ -120,6 +120,8 @@ export default class PatternManager extends Definable { updateDom(pattern: PatternObject, patternDom: SVGElement) { if (isSVGPattern(pattern)) { // New SVGPattern will not been supported in the legacy SVG renderer. + // svg-legacy will been removed soon. + // const svgElement = pattern.svgElement; // const isStringSVG = typeof svgElement === 'string'; // if (isStringSVG || svgElement.parentNode !== patternDom) { From b0ede2ca0a8e22763c7a7add0157642b37d658dc Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 10:24:36 +0800 Subject: [PATCH 048/148] fix entry js --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 0631cc0d0..a95f37b0a 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,8 @@ export * from './lib/zrender'; export * from './lib/export'; -import {registerPainter} from './src/zrender'; -import CanvasPainter from './src/canvas/Painter'; -import SVGPainter from './src/svg/Painter'; +import {registerPainter} from './lib/zrender'; +import CanvasPainter from './lib/canvas/Painter'; +import SVGPainter from './lib/svg/Painter'; registerPainter('canvas', CanvasPainter); registerPainter('svg', SVGPainter); From b88a611877ae35f27fc0d519998afcc8ece65748 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 10:31:43 +0800 Subject: [PATCH 049/148] wip(svg): fix shadow error log when scale is 0 --- src/svg/graphic.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index e34622bda..ba5fcac04 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -309,6 +309,9 @@ function setShadow( const globalScale = el.getGlobalScale(); const scaleX = globalScale[0]; const scaleY = globalScale[1]; + if (!scaleX || !scaleY) { + return; + } const offsetX = style.shadowOffsetX || 0; const offsetY = style.shadowOffsetY || 0; From b95c3f8bbfa97618a213239dcac1450a9219a177 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 10:45:43 +0800 Subject: [PATCH 050/148] wip(svg): add cssAnimation option in renderToString --- src/svg/Painter.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 280f0dc92..3b86b3834 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -21,7 +21,7 @@ import { createBrushScope } from './core'; import { normalizeColor } from './helper'; -import { disableUserSelect, extend, keys, logError, map } from '../core/util'; +import { defaults, disableUserSelect, extend, keys, logError, map } from '../core/util'; import Path from '../graphic/Path'; import patch from './patch'; import { getSize } from '../canvas/helper'; @@ -191,9 +191,19 @@ class SVGPainter implements PainterBase { ); } - renderToString() { + renderToString(opts?: { + /** + * If add css animation. + */ + cssAnimation: boolean + }) { + const defaultOpts = { + cssAnimation: true + }; + opts = defaults(opts || {}, defaultOpts); + return vNodeToString(this.renderToVNode({ - animation: true, + animation: defaultOpts.cssAnimation, willUpdate: false, compress: true }), { newline: true }); From dd57edfd3924ac2c4728e9da10f358909b224d3c Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 14:33:15 +0800 Subject: [PATCH 051/148] wip(svg): set font attributes instead of style. apache/echarts#15064 --- .gitignore | 5 +++- src/contain/text.ts | 6 +++-- src/contain/textWidthMap.ts | 1 - src/core/types.ts | 3 +++ src/graphic/TSpan.ts | 12 ++++++++- src/graphic/Text.ts | 50 ++++++++++++++++++++++++++++--------- src/svg/graphic.ts | 25 +++++++++++++++++-- src/tool/color.ts | 10 ++++---- 8 files changed, 88 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 40f3f8981..16977e7c0 100644 --- a/.gitignore +++ b/.gitignore @@ -164,4 +164,7 @@ pip-log.txt /lib /esm -node_modules \ No newline at end of file +node_modules + +# rollup visualizer +/stats.html \ No newline at end of file diff --git a/src/contain/text.ts b/src/contain/text.ts index 866df12ff..c113320c3 100644 --- a/src/contain/text.ts +++ b/src/contain/text.ts @@ -6,7 +6,9 @@ import { DEFAULT_TEXT_WIDTH_MAP } from './textWidthMap'; let textWidthCache: Dictionary> = {}; -export const DEFAULT_FONT = '12px sans-serif'; +export const DEFAULT_FONT_SIZE = 12; +export const DEFAULT_FONT_FAMILY = 'sans-serif'; +export const DEFAULT_FONT = `${DEFAULT_FONT_SIZE}px ${DEFAULT_FONT_FAMILY}`; let _ctx: CanvasRenderingContext2D; let _cachedFont: string; @@ -27,7 +29,7 @@ function defaultMeasureText(text: string, font?: string): { width: number } { font = font || DEFAULT_FONT; // Use font size if there is no other method can be used. const res = /^([0-9]*?)px$/.exec(font); - const fontSize = +(res && res[1]) || 12; + const fontSize = +(res && res[1]) || DEFAULT_FONT_SIZE; let width = 0; if (font.indexOf('mono') >= 0) { // is monospace width = fontSize * text.length; diff --git a/src/contain/textWidthMap.ts b/src/contain/textWidthMap.ts index 921d7db39..857d44eee 100644 --- a/src/contain/textWidthMap.ts +++ b/src/contain/textWidthMap.ts @@ -16,7 +16,6 @@ export const OFFSET = 20; export const SCALE = 100; -export const DEFAULT_FONT_SIZE = 12; // TODO other basic fonts? // eslint-disable-next-line const defaultWidthMapStr = `007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N`; diff --git a/src/core/types.ts b/src/core/types.ts index 41b035d65..e04a295f5 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -22,6 +22,9 @@ export type TextVerticalAlign = 'top' | 'middle' | 'bottom' export type TextAlign = 'left' | 'center' | 'right' // | 'middle' // DEPRECATED +export type FontWeight = 'normal' | 'bold' | 'bolder' | 'lighter' | number; +export type FontStyle = 'normal' | 'italic' | 'oblique'; + export type BuiltinTextPosition = 'left' | 'right' | 'top' | 'bottom' | 'inside' | 'insideLeft' | 'insideRight' | 'insideTop' | 'insideBottom' | 'insideTopLeft' | 'insideTopRight'| 'insideBottomLeft' | 'insideBottomRight'; diff --git a/src/graphic/TSpan.ts b/src/graphic/TSpan.ts index c08af5545..33fe682a9 100644 --- a/src/graphic/TSpan.ts +++ b/src/graphic/TSpan.ts @@ -3,7 +3,7 @@ import { getBoundingRect, DEFAULT_FONT } from '../contain/text'; import BoundingRect from '../core/BoundingRect'; import { PathStyleProps, DEFAULT_PATH_STYLE } from './Path'; import { createObject, defaults } from '../core/util'; -import { TextAlign, TextVerticalAlign } from '../core/types'; +import { FontStyle, FontWeight, TextAlign, TextVerticalAlign } from '../core/types'; export interface TSpanStyleProps extends PathStyleProps { @@ -13,8 +13,18 @@ export interface TSpanStyleProps extends PathStyleProps { // TODO Text is assigned inside zrender text?: string + // Final generated font string + // Used in canvas, and when developers specified it. font?: string + // Value for each part of font + // Used in svg. + // NOTE: font should always been sync with these 4 properties. + fontSize?: number + fontWeight?: FontWeight + fontStyle?: FontStyle + fontFamily?: string + textAlign?: CanvasTextAlign textBaseline?: CanvasTextBaseline diff --git a/src/graphic/Text.ts b/src/graphic/Text.ts index 0190b6fae..052565032 100644 --- a/src/graphic/Text.ts +++ b/src/graphic/Text.ts @@ -2,7 +2,7 @@ * RichText is a container that manages complex text label. * It will parse text string and create sub displayble elements respectively. */ -import { TextAlign, TextVerticalAlign, ImageLike, Dictionary, MapToType } from '../core/types'; +import { TextAlign, TextVerticalAlign, ImageLike, Dictionary, MapToType, FontWeight, FontStyle } from '../core/types'; import { parseRichText, parsePlainText } from './helper/parseText'; import TSpan, { TSpanStyleProps } from './TSpan'; import { retrieve2, each, normalizeCssArray, trim, retrieve3, extend, keys, defaults } from '../core/util'; @@ -65,11 +65,11 @@ export interface TextStylePropsPart { /** * It helps merging respectively, rather than parsing an entire font string. */ - fontStyle?: 'normal' | 'italic' | 'oblique' + fontStyle?: FontStyle /** * It helps merging respectively, rather than parsing an entire font string. */ - fontWeight?: 'normal' | 'bold' | 'bolder' | 'lighter' | number + fontWeight?: FontWeight /** * It helps merging respectively, rather than parsing an entire font string. */ @@ -595,6 +595,7 @@ class ZRText extends Displayable implements GroupLike { } subElStyle.font = textFont; + setSeparateFont(subElStyle, style); textY += lineHeight; @@ -765,7 +766,7 @@ class ZRText extends Displayable implements GroupLike { ); const hasShadow = tokenStyle.textShadowBlur > 0 - || style.textShadowBlur > 0; + || style.textShadowBlur > 0; subElStyle.text = token.text; subElStyle.x = x; @@ -784,6 +785,10 @@ class ZRText extends Displayable implements GroupLike { subElStyle.font = token.font || DEFAULT_FONT; subElStyle.opacity = retrieve3(tokenStyle.opacity, style.opacity, 1); + + // TODO inherit each item from top style in token style? + setSeparateFont(subElStyle, tokenStyle); + if (textStroke) { subElStyle.lineWidth = retrieve3(tokenStyle.lineWidth, style.lineWidth, defaultLineWidth); subElStyle.lineDash = retrieve2(tokenStyle.lineDash, style.lineDash); @@ -884,20 +889,21 @@ class ZRText extends Displayable implements GroupLike { // FIXME in node-canvas fontWeight is before fontStyle // Use `fontSize` `fontFamily` to check whether font properties are defined. let font = ''; - if (style.fontSize || style.fontFamily || style.fontWeight) { + if (hasSeparateFont(style)) { let fontSize = ''; + const rawFontSize = style.fontSize; if ( - typeof style.fontSize === 'string' + typeof rawFontSize === 'string' && ( - style.fontSize.indexOf('px') !== -1 - || style.fontSize.indexOf('rem') !== -1 - || style.fontSize.indexOf('em') !== -1 + rawFontSize.indexOf('px') !== -1 + || rawFontSize.indexOf('rem') !== -1 + || rawFontSize.indexOf('em') !== -1 ) ) { - fontSize = style.fontSize; + fontSize = rawFontSize; } - else if (!isNaN(+style.fontSize)) { - fontSize = style.fontSize + 'px'; + else if (!isNaN(+rawFontSize)) { + fontSize = rawFontSize + 'px'; } else { fontSize = '12px'; @@ -918,8 +924,28 @@ class ZRText extends Displayable implements GroupLike { const VALID_TEXT_ALIGN = {left: true, right: 1, center: 1}; const VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1}; +const FONT_PARTS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'] as const; + +function setSeparateFont( + targetStyle: TSpanStyleProps, + sourceStyle: TextStylePropsPart +) { + for (let i = 0; i < FONT_PARTS.length; i++) { + const fontProp = FONT_PARTS[i]; + const val = sourceStyle[fontProp]; + if (val != null) { + (targetStyle as any)[fontProp] = val; + } + } +} + +export function hasSeparateFont(style: Pick) { + return style.fontSize || style.fontFamily || style.fontWeight; +} + export function normalizeTextStyle(style: TextStyleProps): TextStyleProps { normalizeStyle(style); + // TODO inherit each item from top style in token style? each(style.rich, normalizeStyle); return style; } diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index ba5fcac04..d006239b6 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -22,7 +22,7 @@ import { } from './helper'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; -import { DEFAULT_FONT, getLineHeight } from '../contain/text'; +import { DEFAULT_FONT, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, getLineHeight } from '../contain/text'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import SVGPathRebuilder from './SVGPathRebuilder'; import mapStyleToAttrs from './mapStyleToAttrs'; @@ -37,6 +37,7 @@ import { ImagePatternObject, SVGPatternObject } from '../graphic/Pattern'; import { createOrUpdateImage } from '../graphic/helper/image'; import { ImageLike } from '../core/types'; import { createCSSAnimation } from './cssAnimation'; +import { hasSeparateFont } from '../graphic/Text'; const round = Math.round; @@ -261,10 +262,30 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { || style.textAlign; const attrs: SVGVNodeAttrs = { - 'style': `font:${font}`, 'dominant-baseline': 'central', 'text-anchor': textAlign }; + if (hasSeparateFont(style)) { + // Set separate font attributes if possible. Or some platform like PowerPoint may not support it. + const fontStyle = style.fontStyle; + const fontSize = style.fontSize || DEFAULT_FONT_SIZE; + const fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY; + const fontWeight = style.fontWeight; + attrs['font-size'] = fontSize + 'px'; + attrs['font-family'] = fontFamily; + + // TODO reduce the attribute to set. But should it inherit from the container element? + if (fontStyle && fontStyle !== 'normal') { + attrs['font-style'] = fontStyle; + } + if (fontWeight && fontWeight !== 'normal') { + attrs['font-weight'] = fontWeight; + } + } + else { + // If user set font manually. + attrs.style = `font: ${font}`; + } if (text.match(/\s/)) { // only enabled when have space in text. attrs['xml:space'] = 'preserve'; diff --git a/src/tool/color.ts b/src/tool/color.ts index fb0cab500..39ca53418 100644 --- a/src/tool/color.ts +++ b/src/tool/color.ts @@ -559,9 +559,9 @@ export function lum(color: string, backgroundLum: number) { * Generate a random color */ export function random(): string { - let r = Math.round(Math.random() * 255); - let g = Math.round(Math.random() * 255); - let b = Math.round(Math.random() * 255); - - return 'rgb(' + r + ',' + g + ',' + b + ')'; + return stringify([ + Math.round(Math.random() * 255), + Math.round(Math.random() * 255), + Math.round(Math.random() * 255) + ], 'rgb'); } \ No newline at end of file From af0d89de29978033a81c90d8776025088ab0705d Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 17:17:53 +0800 Subject: [PATCH 052/148] wip(text): always set font in style to avoid other css overwrite it --- src/svg/graphic.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index d006239b6..eb34fee21 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -268,7 +268,7 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { if (hasSeparateFont(style)) { // Set separate font attributes if possible. Or some platform like PowerPoint may not support it. const fontStyle = style.fontStyle; - const fontSize = style.fontSize || DEFAULT_FONT_SIZE; + const fontSize = style.fontSize || DEFAULT_FONT_SIZE; const fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY; const fontWeight = style.fontWeight; attrs['font-size'] = fontSize + 'px'; @@ -282,10 +282,13 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { attrs['font-weight'] = fontWeight; } } - else { - // If user set font manually. - attrs.style = `font: ${font}`; - } + // FIXME + // Always set font in style to avoid developers set font in the CSS of page and overwrite the attributes. + // For example: + // text { font: 12px } + attrs.style = `font: ${font}`; + + if (text.match(/\s/)) { // only enabled when have space in text. attrs['xml:space'] = 'preserve'; From 81826843eb6d3e87f61ea047c981b9b94809b81e Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 18:01:49 +0800 Subject: [PATCH 053/148] wip(svg): use separated font css in style --- src/svg/graphic.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index eb34fee21..d918b9155 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -17,6 +17,7 @@ import { isPattern, isRadialGradient, normalizeColor, + round1, round4, TEXT_ALIGN_TO_ANCHOR } from './helper'; @@ -265,28 +266,33 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { 'dominant-baseline': 'central', 'text-anchor': textAlign }; + if (hasSeparateFont(style)) { // Set separate font attributes if possible. Or some platform like PowerPoint may not support it. + let separatedFontStr = ''; const fontStyle = style.fontStyle; - const fontSize = style.fontSize || DEFAULT_FONT_SIZE; + const fontSize = round1(retrieve2(style.fontSize, DEFAULT_FONT_SIZE)); + if (!fontSize) { + return; + } + const fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY; const fontWeight = style.fontWeight; - attrs['font-size'] = fontSize + 'px'; - attrs['font-family'] = fontFamily; + separatedFontStr += `font-size:${fontSize}px;font-family:${fontFamily};`; // TODO reduce the attribute to set. But should it inherit from the container element? if (fontStyle && fontStyle !== 'normal') { - attrs['font-style'] = fontStyle; + separatedFontStr += `font-style:${fontStyle};`; } if (fontWeight && fontWeight !== 'normal') { - attrs['font-weight'] = fontWeight; + separatedFontStr += `font-weight:${fontWeight};`; } + attrs.style = separatedFontStr; + } + else { + // Use set font manually + attrs.style = `font: ${font}`; } - // FIXME - // Always set font in style to avoid developers set font in the CSS of page and overwrite the attributes. - // For example: - // text { font: 12px } - attrs.style = `font: ${font}`; if (text.match(/\s/)) { From b1d9cdee2680447d1c4ee76fa053d70ebb64aef7 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 8 Oct 2021 18:27:26 +0800 Subject: [PATCH 054/148] wip(svg): fix font size with px postfix --- src/graphic/Text.ts | 41 +++++++++++++++++++++-------------------- src/svg/graphic.ts | 8 ++++---- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/graphic/Text.ts b/src/graphic/Text.ts index 052565032..b62f879a4 100644 --- a/src/graphic/Text.ts +++ b/src/graphic/Text.ts @@ -6,7 +6,7 @@ import { TextAlign, TextVerticalAlign, ImageLike, Dictionary, MapToType, FontWei import { parseRichText, parsePlainText } from './helper/parseText'; import TSpan, { TSpanStyleProps } from './TSpan'; import { retrieve2, each, normalizeCssArray, trim, retrieve3, extend, keys, defaults } from '../core/util'; -import { DEFAULT_FONT, adjustTextX, adjustTextY } from '../contain/text'; +import { DEFAULT_FONT, adjustTextX, adjustTextY, DEFAULT_FONT_SIZE } from '../contain/text'; import ZRImage from './Image'; import Rect from './shape/Rect'; import BoundingRect from '../core/BoundingRect'; @@ -890,28 +890,10 @@ class ZRText extends Displayable implements GroupLike { // Use `fontSize` `fontFamily` to check whether font properties are defined. let font = ''; if (hasSeparateFont(style)) { - let fontSize = ''; - const rawFontSize = style.fontSize; - if ( - typeof rawFontSize === 'string' - && ( - rawFontSize.indexOf('px') !== -1 - || rawFontSize.indexOf('rem') !== -1 - || rawFontSize.indexOf('em') !== -1 - ) - ) { - fontSize = rawFontSize; - } - else if (!isNaN(+rawFontSize)) { - fontSize = rawFontSize + 'px'; - } - else { - fontSize = '12px'; - } font = [ style.fontStyle, style.fontWeight, - fontSize, + parseFontSize(style.fontSize), // If font properties are defined, `fontFamily` should not be ignored. style.fontFamily || 'sans-serif' ].join(' '); @@ -926,6 +908,25 @@ const VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1}; const FONT_PARTS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'] as const; +export function parseFontSize(fontSize: number | string) { + if ( + typeof fontSize === 'string' + && ( + fontSize.indexOf('px') !== -1 + || fontSize.indexOf('rem') !== -1 + || fontSize.indexOf('em') !== -1 + ) + ) { + return fontSize; + } + else if (!isNaN(+fontSize)) { + return fontSize + 'px'; + } + else { + return DEFAULT_FONT_SIZE + 'px'; + } +} + function setSeparateFont( targetStyle: TSpanStyleProps, sourceStyle: TextStylePropsPart diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index d918b9155..009e52948 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -38,7 +38,7 @@ import { ImagePatternObject, SVGPatternObject } from '../graphic/Pattern'; import { createOrUpdateImage } from '../graphic/helper/image'; import { ImageLike } from '../core/types'; import { createCSSAnimation } from './cssAnimation'; -import { hasSeparateFont } from '../graphic/Text'; +import { hasSeparateFont, parseFontSize } from '../graphic/Text'; const round = Math.round; @@ -271,14 +271,14 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { // Set separate font attributes if possible. Or some platform like PowerPoint may not support it. let separatedFontStr = ''; const fontStyle = style.fontStyle; - const fontSize = round1(retrieve2(style.fontSize, DEFAULT_FONT_SIZE)); - if (!fontSize) { + const fontSize = parseFontSize(style.fontSize); + if (!parseFloat(fontSize)) { // is 0px return; } const fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY; const fontWeight = style.fontWeight; - separatedFontStr += `font-size:${fontSize}px;font-family:${fontFamily};`; + separatedFontStr += `font-size:${fontSize};font-family:${fontFamily};`; // TODO reduce the attribute to set. But should it inherit from the container element? if (fontStyle && fontStyle !== 'normal') { From 7b734b7af1135f2ef219d6dc4de253bb81830cc7 Mon Sep 17 00:00:00 2001 From: pissang Date: Sat, 9 Oct 2021 17:46:28 +0800 Subject: [PATCH 055/148] wip(svg): use null check in fontSize --- src/graphic/Text.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphic/Text.ts b/src/graphic/Text.ts index b62f879a4..eb4e65720 100644 --- a/src/graphic/Text.ts +++ b/src/graphic/Text.ts @@ -941,7 +941,7 @@ function setSeparateFont( } export function hasSeparateFont(style: Pick) { - return style.fontSize || style.fontFamily || style.fontWeight; + return style.fontSize != null || style.fontFamily || style.fontWeight; } export function normalizeTextStyle(style: TextStyleProps): TextStyleProps { From 1e269b9c59446dcfbf398d0214b3048e25355aa6 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 12 Oct 2021 10:53:42 +0800 Subject: [PATCH 056/148] fix(event): fix firefox version comparison flaw. --- src/core/event.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/event.ts b/src/core/event.ts index 4b7d10d6a..6cf141fa1 100644 --- a/src/core/event.ts +++ b/src/core/event.ts @@ -9,6 +9,10 @@ import {isCanvasEl, transformCoordWithViewport} from './dom'; const MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/; const _calcOut: number[] = []; +const firefoxNotSupportOffsetXY = env.browser.firefox + // use offsetX/offsetY for Firefox >= 39 + // PENDING: consider Firefox for Android and Firefox OS? >= 43 + && +(env.browser.version as string).split('.')[0] < 39; type FirefoxMouseEvent = { layerX: number @@ -63,10 +67,7 @@ export function clientToLocal( // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d. // // BTW3, In ff, offsetX/offsetY is always 0. - else if (env.browser.firefox - // use offsetX/offsetY for Firefox >= 39 - // PENDING: consider Firefox for Android and Firefox OS? >= 43 - && env.browser.version < '39' + else if (firefoxNotSupportOffsetXY && (e as FirefoxMouseEvent).layerX != null && (e as FirefoxMouseEvent).layerX !== (e as MouseEvent).offsetX ) { From aa6765006928e3223753e4e4d87ef62a2c999240 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 14 Oct 2021 13:41:00 +0800 Subject: [PATCH 057/148] fix(svg): put style on the svg dom instead of container --- src/svg/Painter.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 3b86b3834..ac29a2669 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -21,7 +21,7 @@ import { createBrushScope } from './core'; import { normalizeColor } from './helper'; -import { defaults, disableUserSelect, extend, keys, logError, map } from '../core/util'; +import { defaults, extend, keys, logError, map } from '../core/util'; import Path from '../graphic/Path'; import patch from './patch'; import { getSize } from '../canvas/helper'; @@ -66,9 +66,6 @@ class SVGPainter implements PainterBase { // A unique id for generating svg ids. this._id = 'zr' + svgId++; - if (root && root.style) { - disableUserSelect(root); - } if (root && !opts.ssr) { const viewport = this._viewport = document.createElement('div'); @@ -107,6 +104,8 @@ class SVGPainter implements PainterBase { const vnode = this.renderToVNode({ willUpdate: true }); + // Disable user selection. + vnode.attrs.style = 'user-select:none;position:absolute;left:0;top:0;'; patch(this._oldVNode || this._svgDom, vnode); this._oldVNode = vnode; } From ca72646f3462ea6de1420053209009358745ba49 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 14 Oct 2021 13:41:19 +0800 Subject: [PATCH 058/148] style: simpler prop name --- src/graphic/Path.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/graphic/Path.ts b/src/graphic/Path.ts index f76da8de1..08559c040 100644 --- a/src/graphic/Path.ts +++ b/src/graphic/Path.ts @@ -149,7 +149,7 @@ class Path extends Displayable { */ autoBatch: boolean - private _rectWithStroke: BoundingRect + private _rectStroke: BoundingRect protected _normalState: PathState @@ -359,9 +359,9 @@ class Path extends Displayable { // Needs update rect with stroke lineWidth when // 1. Element changes scale or lineWidth // 2. Shape is changed - const rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone()); + const rectStroke = this._rectStroke || (this._rectStroke = rect.clone()); if (this.__dirty || needsUpdateRect) { - rectWithStroke.copy(rect); + rectStroke.copy(rect); // PENDING, Min line width is needed when line is horizontal or vertical const lineScale = style.strokeNoScale ? this.getLineScale() : 1; // FIXME Must after updateTransform @@ -375,15 +375,15 @@ class Path extends Displayable { // Consider line width // Line scale can't be 0; if (lineScale > 1e-10) { - rectWithStroke.width += w / lineScale; - rectWithStroke.height += w / lineScale; - rectWithStroke.x -= w / lineScale / 2; - rectWithStroke.y -= w / lineScale / 2; + rectStroke.width += w / lineScale; + rectStroke.height += w / lineScale; + rectStroke.x -= w / lineScale / 2; + rectStroke.y -= w / lineScale / 2; } } // Return rect with stroke - return rectWithStroke; + return rectStroke; } return rect; From ac84ffee73fc0ef21aefb65d13f163dcd75b1c53 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 14 Oct 2021 13:41:36 +0800 Subject: [PATCH 059/148] refact: remove IncrementalDisplayable from dependencies --- src/canvas/Painter.ts | 4 ++-- src/canvas/graphic.ts | 7 ++++--- src/graphic/IncrementalDisplayable.ts | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/canvas/Painter.ts b/src/canvas/Painter.ts index 1f6e98a14..b125739d8 100644 --- a/src/canvas/Painter.ts +++ b/src/canvas/Painter.ts @@ -12,10 +12,10 @@ import Storage from '../Storage'; import { brush, BrushScope, brushSingle } from './graphic'; import { PainterBase } from '../PainterBase'; import BoundingRect from '../core/BoundingRect'; -import IncrementalDisplayable from '../graphic/IncrementalDisplayable'; import Path from '../graphic/Path'; import { REDRAW_BIT } from '../graphic/constants'; import { getSize } from './helper'; +import type IncrementalDisplayable from '../graphic/IncrementalDisplayable'; const HOVER_LAYER_ZLEVEL = 1e5; const CANVAS_ZLEVEL = 314159; @@ -396,7 +396,7 @@ export default class CanvasPainter implements PainterBase { const clearColor = layer.zlevel === this._zlevelList[0] ? this._backgroundColor : null; - // All elements in this layer are cleared. + // All elements in this layer are removed. if (layer.__startIndex === layer.__endIndex) { layer.clear(false, clearColor, repaintRects); } diff --git a/src/canvas/graphic.ts b/src/canvas/graphic.ts index f2eb3527d..5cc75a53b 100644 --- a/src/canvas/graphic.ts +++ b/src/canvas/graphic.ts @@ -14,8 +14,8 @@ import { DEFAULT_FONT } from '../contain/text'; import { MatrixArray } from '../core/matrix'; import { map, RADIAN_TO_DEGREE } from '../core/util'; import { normalizeLineDash } from '../graphic/helper/dashStyle'; -import IncrementalDisplayable from '../graphic/IncrementalDisplayable'; import { REDRAW_BIT, SHAPE_CHANGED_BIT } from '../graphic/constants'; +import type IncrementalDisplayable from '../graphic/IncrementalDisplayable'; const pathProxyForDraw = new PathProxy(true); @@ -765,13 +765,14 @@ export function brush( bindImageStyle(ctx, el as ZRImage, prevEl as ZRImage, forceSetStyle, scope); brushImage(ctx, el as ZRImage, style); } - else if (el instanceof IncrementalDisplayable) { + // Assume it's a IncrementalDisplayable + else if ((el as IncrementalDisplayable).getTemporalDisplayables) { if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) { forceSetStyle = true; scope.lastDrawType = DRAW_TYPE_INCREMENTAL; } - brushIncremental(ctx, el, scope); + brushIncremental(ctx, el as IncrementalDisplayable, scope); } } diff --git a/src/graphic/IncrementalDisplayable.ts b/src/graphic/IncrementalDisplayable.ts index ada4f437d..c1fd4dbc7 100644 --- a/src/graphic/IncrementalDisplayable.ts +++ b/src/graphic/IncrementalDisplayable.ts @@ -38,6 +38,7 @@ export default class IncrementalDisplayable extends Displayble { // PENDING this.style = {}; } + // getCurrentCursor / updateCursorAfterBrush // is used in graphic.ts. It's not provided for developers getCursor() { From 2d4e1d1877dcb42c7c1d1b07e74e392d1db17812 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 14 Oct 2021 17:24:21 +0800 Subject: [PATCH 060/148] feat: add setPlatformAPI --- src/canvas/Layer.ts | 3 +- src/canvas/graphic.ts | 2 +- src/contain/text.ts | 62 ++---------------------- src/contain/textWidthMap.ts | 36 -------------- src/core/platform.ts | 94 +++++++++++++++++++++++++++++++++++++ src/core/util.ts | 16 ------- src/export.ts | 4 +- src/graphic/TSpan.ts | 3 +- src/graphic/Text.ts | 3 +- src/svg-legacy/graphic.ts | 3 +- src/svg/graphic.ts | 4 +- 11 files changed, 112 insertions(+), 118 deletions(-) delete mode 100644 src/contain/textWidthMap.ts create mode 100644 src/core/platform.ts diff --git a/src/canvas/Layer.ts b/src/canvas/Layer.ts index 6900db7a0..100ea80b9 100644 --- a/src/canvas/Layer.ts +++ b/src/canvas/Layer.ts @@ -11,9 +11,10 @@ import { createCanvasPattern } from './graphic'; import Displayable from '../graphic/Displayable'; import BoundingRect from '../core/BoundingRect'; import { REDRAW_BIT } from '../graphic/constants'; +import { platformApi } from '../core/platform'; function createDom(id: string, painter: CanvasPainter, dpr: number) { - const newDom = util.createCanvas(); + const newDom = platformApi.createCanvas(); const width = painter.getWidth(); const height = painter.getHeight(); diff --git a/src/canvas/graphic.ts b/src/canvas/graphic.ts index 5cc75a53b..3e96ec4e9 100644 --- a/src/canvas/graphic.ts +++ b/src/canvas/graphic.ts @@ -10,12 +10,12 @@ import { getCanvasGradient, isClipPathChanged } from './helper'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; import TSpan, {TSpanStyleProps} from '../graphic/TSpan'; -import { DEFAULT_FONT } from '../contain/text'; import { MatrixArray } from '../core/matrix'; import { map, RADIAN_TO_DEGREE } from '../core/util'; import { normalizeLineDash } from '../graphic/helper/dashStyle'; import { REDRAW_BIT, SHAPE_CHANGED_BIT } from '../graphic/constants'; import type IncrementalDisplayable from '../graphic/IncrementalDisplayable'; +import { DEFAULT_FONT } from '../core/platform'; const pathProxyForDraw = new PathProxy(true); diff --git a/src/contain/text.ts b/src/contain/text.ts index c113320c3..c799f67fc 100644 --- a/src/contain/text.ts +++ b/src/contain/text.ts @@ -1,64 +1,10 @@ import BoundingRect, { RectLike } from '../core/BoundingRect'; -import { createCanvas, retrieve2 } from '../core/util'; -import { Dictionary, PropType, TextAlign, TextVerticalAlign, BuiltinTextPosition } from '../core/types'; +import { Dictionary, TextAlign, TextVerticalAlign, BuiltinTextPosition } from '../core/types'; import LRU from '../core/LRU'; -import { DEFAULT_TEXT_WIDTH_MAP } from './textWidthMap'; +import { DEFAULT_FONT, platformApi } from '../core/platform'; let textWidthCache: Dictionary> = {}; -export const DEFAULT_FONT_SIZE = 12; -export const DEFAULT_FONT_FAMILY = 'sans-serif'; -export const DEFAULT_FONT = `${DEFAULT_FONT_SIZE}px ${DEFAULT_FONT_FAMILY}`; - -let _ctx: CanvasRenderingContext2D; -let _cachedFont: string; - -function defaultMeasureText(text: string, font?: string): { width: number } { - if (!_ctx) { - const canvas = createCanvas(); - _ctx = canvas && canvas.getContext('2d'); - } - if (_ctx) { - if (_cachedFont !== font) { - _cachedFont = _ctx.font = font || DEFAULT_FONT; - } - return _ctx.measureText(text); - } - else { - text = text || ''; - font = font || DEFAULT_FONT; - // Use font size if there is no other method can be used. - const res = /^([0-9]*?)px$/.exec(font); - const fontSize = +(res && res[1]) || DEFAULT_FONT_SIZE; - let width = 0; - if (font.indexOf('mono') >= 0) { // is monospace - width = fontSize * text.length; - } - else { - for (let i = 0; i < text.length; i++) { - width += retrieve2(DEFAULT_TEXT_WIDTH_MAP[text[i]], 1) * fontSize; - } - } - return { width }; - } -} - -let methods: { - measureText: (text: string, font?: string) => { width: number } -} = { - measureText: defaultMeasureText -}; - -export function $override( - name: keyof typeof methods, - fn: PropType -) { - methods[name] = fn; -} - -// let cacheMissCount = 0; -// let totalCount = 0; - export function getWidth(text: string, font: string): number { font = font || DEFAULT_FONT; let cacheOfFont = textWidthCache[font]; @@ -67,7 +13,7 @@ export function getWidth(text: string, font: string): number { } let width = cacheOfFont.get(text); if (width == null) { - width = methods.measureText(text, font).width; + width = platformApi.measureText(text, font).width; cacheOfFont.put(text, width); } @@ -152,7 +98,7 @@ export function getLineHeight(font?: string): number { export function measureText(text: string, font?: string): { width: number } { - return methods.measureText(text, font); + return platformApi.measureText(text, font); } diff --git a/src/contain/textWidthMap.ts b/src/contain/textWidthMap.ts deleted file mode 100644 index 857d44eee..000000000 --- a/src/contain/textWidthMap.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Text width map used for environment there is no canvas -// Only common ascii is used for size concern. - -// Generated from following code -// -// ctx.font = '12px sans-serif'; -// const asciiRange = [32, 126]; -// let mapStr = ''; -// for (let i = asciiRange[0]; i <= asciiRange[1]; i++) { -// const char = String.fromCharCode(i); -// const width = ctx.measureText(char).width; -// const ratio = Math.round(width / 12 * 100); -// mapStr += String.fromCharCode(ratio + 20)) -// } -// mapStr.replace(/\\/g, '\\\\'); - -export const OFFSET = 20; -export const SCALE = 100; -// TODO other basic fonts? -// eslint-disable-next-line -const defaultWidthMapStr = `007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N`; - -function getMap(mapStr: string): Record { - const map: Record = {}; - if (typeof JSON === 'undefined') { - return map; - } - for (let i = 0; i < mapStr.length; i++) { - const char = String.fromCharCode(i + 32); - const size = (mapStr.charCodeAt(i) - OFFSET) / SCALE; - map[char] = size; - } - return map; -} - -export const DEFAULT_TEXT_WIDTH_MAP = getMap(defaultWidthMapStr); \ No newline at end of file diff --git a/src/core/platform.ts b/src/core/platform.ts new file mode 100644 index 000000000..a9cf03ff1 --- /dev/null +++ b/src/core/platform.ts @@ -0,0 +1,94 @@ +import { extend, retrieve2 } from './util'; + +export const DEFAULT_FONT_SIZE = 12; +export const DEFAULT_FONT_FAMILY = 'sans-serif'; +export const DEFAULT_FONT = `${DEFAULT_FONT_SIZE}px ${DEFAULT_FONT_FAMILY}`; + +interface Platform { + // TODO CanvasLike? + createCanvas(): HTMLCanvasElement + measureText(text: string, font?: string): { width: number } +} + +// Text width map used for environment there is no canvas +// Only common ascii is used for size concern. + +// Generated from following code +// +// ctx.font = '12px sans-serif'; +// const asciiRange = [32, 126]; +// let mapStr = ''; +// for (let i = asciiRange[0]; i <= asciiRange[1]; i++) { +// const char = String.fromCharCode(i); +// const width = ctx.measureText(char).width; +// const ratio = Math.round(width / 12 * 100); +// mapStr += String.fromCharCode(ratio + 20)) +// } +// mapStr.replace(/\\/g, '\\\\'); +const OFFSET = 20; +const SCALE = 100; +// TODO other basic fonts? +// eslint-disable-next-line +const defaultWidthMapStr = `007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N`; + +function getTextWidthMap(mapStr: string): Record { + const map: Record = {}; + if (typeof JSON === 'undefined') { + return map; + } + for (let i = 0; i < mapStr.length; i++) { + const char = String.fromCharCode(i + 32); + const size = (mapStr.charCodeAt(i) - OFFSET) / SCALE; + map[char] = size; + } + return map; +} + +export const DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr); + +export const platformApi: Platform = { + // Export methods + createCanvas() { + return typeof document !== 'undefined' + && document.createElement('canvas'); + }, + + measureText: (function () { + + let _ctx: CanvasRenderingContext2D; + let _cachedFont: string; + return (text: string, font?: string) => { + if (!_ctx) { + const canvas = platformApi.createCanvas(); + _ctx = canvas && canvas.getContext('2d'); + } + if (_ctx) { + if (_cachedFont !== font) { + _cachedFont = _ctx.font = font || DEFAULT_FONT; + } + return _ctx.measureText(text); + } + else { + text = text || ''; + font = font || DEFAULT_FONT; + // Use font size if there is no other method can be used. + const res = /^([0-9]*?)px$/.exec(font); + const fontSize = +(res && res[1]) || DEFAULT_FONT_SIZE; + let width = 0; + if (font.indexOf('mono') >= 0) { // is monospace + width = fontSize * text.length; + } + else { + for (let i = 0; i < text.length; i++) { + width += retrieve2(DEFAULT_TEXT_WIDTH_MAP[text[i]], 1) * fontSize; + } + } + return { width }; + } + }; + })() +}; + +export function setPlatformAPI(platformApis?: Partial) { + extend(platformApi, platformApis); +} \ No newline at end of file diff --git a/src/core/util.ts b/src/core/util.ts index 2fa3c08cd..c753de870 100644 --- a/src/core/util.ts +++ b/src/core/util.ts @@ -41,12 +41,6 @@ const ctorFunction = function () {}.constructor; const protoFunction = ctorFunction ? ctorFunction.prototype : null; const protoKey = '__proto__'; -// Avoid assign to an exported constiable, for transforming to cjs. -const methods: {[key: string]: Function} = {}; - -export function $override(name: string, fn: Function) { - methods[name] = fn; -} let idStart = 0x0907; @@ -211,16 +205,6 @@ export function defaults< } return target as T & S; } - -export const createCanvas = function (): HTMLCanvasElement { - return methods.createCanvas(); -}; - -methods.createCanvas = function (): HTMLCanvasElement { - return typeof document !== 'undefined' - && document.createElement('canvas'); -}; - /** * 查询数组中元素的index */ diff --git a/src/export.ts b/src/export.ts index 7889e74a9..05fea0459 100644 --- a/src/export.ts +++ b/src/export.ts @@ -71,4 +71,6 @@ export {morphPathTool as morph}; export {parseSVG}; -export {default as showDebugDirtyRect} from './debug/showDebugDirtyRect'; \ No newline at end of file +export {default as showDebugDirtyRect} from './debug/showDebugDirtyRect'; + +export {setPlatformAPI} from './core/platform'; \ No newline at end of file diff --git a/src/graphic/TSpan.ts b/src/graphic/TSpan.ts index 33fe682a9..4b5012901 100644 --- a/src/graphic/TSpan.ts +++ b/src/graphic/TSpan.ts @@ -1,9 +1,10 @@ import Displayable, { DisplayableProps, DisplayableStatePropNames } from './Displayable'; -import { getBoundingRect, DEFAULT_FONT } from '../contain/text'; +import { getBoundingRect } from '../contain/text'; import BoundingRect from '../core/BoundingRect'; import { PathStyleProps, DEFAULT_PATH_STYLE } from './Path'; import { createObject, defaults } from '../core/util'; import { FontStyle, FontWeight, TextAlign, TextVerticalAlign } from '../core/types'; +import { DEFAULT_FONT } from '../core/platform'; export interface TSpanStyleProps extends PathStyleProps { diff --git a/src/graphic/Text.ts b/src/graphic/Text.ts index eb4e65720..c3e312e26 100644 --- a/src/graphic/Text.ts +++ b/src/graphic/Text.ts @@ -6,7 +6,7 @@ import { TextAlign, TextVerticalAlign, ImageLike, Dictionary, MapToType, FontWei import { parseRichText, parsePlainText } from './helper/parseText'; import TSpan, { TSpanStyleProps } from './TSpan'; import { retrieve2, each, normalizeCssArray, trim, retrieve3, extend, keys, defaults } from '../core/util'; -import { DEFAULT_FONT, adjustTextX, adjustTextY, DEFAULT_FONT_SIZE } from '../contain/text'; +import { adjustTextX, adjustTextY } from '../contain/text'; import ZRImage from './Image'; import Rect from './shape/Rect'; import BoundingRect from '../core/BoundingRect'; @@ -21,6 +21,7 @@ import Animator from '../animation/Animator'; import Transformable from '../core/Transformable'; import { ElementCommonState } from '../Element'; import { GroupLike } from './Group'; +import { DEFAULT_FONT, DEFAULT_FONT_SIZE } from '../core/platform'; type TextContentBlock = ReturnType type TextLine = TextContentBlock['lines'][0] diff --git a/src/svg-legacy/graphic.ts b/src/svg-legacy/graphic.ts index 5c42cfa62..f48b20d25 100644 --- a/src/svg-legacy/graphic.ts +++ b/src/svg-legacy/graphic.ts @@ -7,10 +7,11 @@ import { getMatrixStr, TEXT_ALIGN_TO_ANCHOR, adjustTextY } from '../svg/helper'; import * as matrix from '../core/matrix'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; -import { DEFAULT_FONT, getLineHeight } from '../contain/text'; +import { getLineHeight } from '../contain/text'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import SVGPathRebuilder from '../svg/SVGPathRebuilder'; import mapStyleToAttrs from '../svg/mapStyleToAttrs'; +import { DEFAULT_FONT } from '../core/platform'; export interface SVGProxy { brush(el: T): void diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 009e52948..df63f1b10 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -17,13 +17,12 @@ import { isPattern, isRadialGradient, normalizeColor, - round1, round4, TEXT_ALIGN_TO_ANCHOR } from './helper'; import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; -import { DEFAULT_FONT, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, getLineHeight } from '../contain/text'; +import { getLineHeight } from '../contain/text'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import SVGPathRebuilder from './SVGPathRebuilder'; import mapStyleToAttrs from './mapStyleToAttrs'; @@ -39,6 +38,7 @@ import { createOrUpdateImage } from '../graphic/helper/image'; import { ImageLike } from '../core/types'; import { createCSSAnimation } from './cssAnimation'; import { hasSeparateFont, parseFontSize } from '../graphic/Text'; +import { DEFAULT_FONT, DEFAULT_FONT_FAMILY } from '../core/platform'; const round = Math.round; From beab675876ea4fbb9e1853637069d17b4f69784c Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 15 Oct 2021 10:33:21 +0800 Subject: [PATCH 061/148] Expose createCanvas in util for compatibility Cleaner code in util --- src/core/platform.ts | 14 +++++++---- src/core/util.ts | 58 ++++++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/core/platform.ts b/src/core/platform.ts index a9cf03ff1..50b407712 100644 --- a/src/core/platform.ts +++ b/src/core/platform.ts @@ -1,5 +1,3 @@ -import { extend, retrieve2 } from './util'; - export const DEFAULT_FONT_SIZE = 12; export const DEFAULT_FONT_FAMILY = 'sans-serif'; export const DEFAULT_FONT = `${DEFAULT_FONT_SIZE}px ${DEFAULT_FONT_FAMILY}`; @@ -80,7 +78,8 @@ export const platformApi: Platform = { } else { for (let i = 0; i < text.length; i++) { - width += retrieve2(DEFAULT_TEXT_WIDTH_MAP[text[i]], 1) * fontSize; + const preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]]; + width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize); } } return { width }; @@ -89,6 +88,11 @@ export const platformApi: Platform = { })() }; -export function setPlatformAPI(platformApis?: Partial) { - extend(platformApi, platformApis); +export function setPlatformAPI(newPlatformApis: Partial) { + for (let key in platformApi) { + // Don't assign unkown methods. + if ((newPlatformApis as any)[key]) { + (platformApi as any)[key] = (newPlatformApis as any)[key]; + } + } } \ No newline at end of file diff --git a/src/core/util.ts b/src/core/util.ts index c753de870..2fc571dc0 100644 --- a/src/core/util.ts +++ b/src/core/util.ts @@ -2,32 +2,38 @@ import { Dictionary, ArrayLike, KeyOfDistributive } from './types'; import { GradientObject } from '../graphic/Gradient'; import { ImagePatternObject } from '../graphic/Pattern'; - +import { platformApi } from './platform'; // 用于处理merge时无法遍历Date等对象的问题 -const BUILTIN_OBJECT: {[key: string]: boolean} = { - '[object Function]': true, - '[object RegExp]': true, - '[object Date]': true, - '[object Error]': true, - '[object CanvasGradient]': true, - '[object CanvasPattern]': true, +const BUILTIN_OBJECT: Record = reduce([ + 'Function', + 'RegExp', + 'Date', + 'Error', + 'CanvasGradient', + 'CanvasPattern', // For node-canvas - '[object Image]': true, - '[object Canvas]': true -}; - -const TYPED_ARRAY: {[key: string]: boolean} = { - '[object Int8Array]': true, - '[object Uint8Array]': true, - '[object Uint8ClampedArray]': true, - '[object Int16Array]': true, - '[object Uint16Array]': true, - '[object Int32Array]': true, - '[object Uint32Array]': true, - '[object Float32Array]': true, - '[object Float64Array]': true -}; + 'Image', + 'Canvas' +], (obj, val) => { + obj['[object ' + val + ']'] = true; + return obj; +}, {} as Record); + +const TYPED_ARRAY: Record = reduce([ + 'Int8', + 'Uint8', + 'Uint8Clamped', + 'Int16', + 'Uint16', + 'Int32', + 'Uint32', + 'Float32', + 'Float64' +], (obj, val) => { + obj['[object ' + val + 'Array]'] = true; + return obj; +}, {} as Record); const objToString = Object.prototype.toString; @@ -95,7 +101,7 @@ export function clone(source: T): T { else { result = new Ctor((source as Float32Array).length); for (let i = 0, len = (source as Float32Array).length; i < len; i++) { - result[i] = clone((source as Float32Array)[i]); + result[i] = (source as Float32Array)[i]; } } } @@ -205,6 +211,10 @@ export function defaults< } return target as T & S; } + +// Expose createCanvas in util for compatibility +export const createCanvas = platformApi.createCanvas; + /** * 查询数组中元素的index */ From 7a58527d707271eea2f7202b2669bc25341bde4f Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 15 Oct 2021 10:54:15 +0800 Subject: [PATCH 062/148] test: update unit test --- package-lock.json | 4975 +++++++++-------- package.json | 6 +- .../spec/animation/ElementAnimation.test.ts | 2 +- test/ut/spec/core/platform.test.ts | 36 + 4 files changed, 2577 insertions(+), 2442 deletions(-) create mode 100644 test/ut/spec/core/platform.test.ts diff --git a/package-lock.json b/package-lock.json index 58889833d..efe1775e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,46 +13,52 @@ "@babel/highlight": "^7.8.0" } }, + "@babel/compat-data": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", + "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "dev": true + }, "@babel/core": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz", - "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.4", - "@babel/helpers": "^7.8.4", - "@babel/parser": "^7.8.4", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.4", - "@babel/types": "^7.8.3", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", + "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.15.8", + "@babel/generator": "^7.15.8", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.8", + "@babel/helpers": "^7.15.4", + "@babel/parser": "^7.15.8", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", "source-map": "^0.5.0" }, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.14.5" } }, "@babel/highlight": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", - "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, @@ -67,12 +73,6 @@ "supports-color": "^5.3.0" } }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -91,14 +91,13 @@ } }, "@babel/generator": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", - "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.8.3", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" }, "dependencies": { @@ -110,50 +109,147 @@ } } }, + "@babel/helper-compilation-targets": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.15.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + } + }, "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-module-imports": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-module-transforms": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.15.4" } }, "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", "dev": true }, + "@babel/helper-replace-supers": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" + } + }, + "@babel/helper-simple-access": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + } + }, "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.15.4" } }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, "@babel/helpers": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", - "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.4", - "@babel/types": "^7.8.3" + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" } }, "@babel/highlight": { @@ -190,11 +286,20 @@ } }, "@babel/parser": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", - "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, "@babel/plugin-syntax-bigint": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", @@ -204,6 +309,60 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, "@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", @@ -213,34 +372,70 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/template": { + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", - "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz", + "integrity": "sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/template": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" }, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.14.5" } }, "@babel/highlight": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", - "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, @@ -267,39 +462,39 @@ } }, "@babel/traverse": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", - "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.4", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.8.4", - "@babel/types": "^7.8.3", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" + "globals": "^11.1.0" }, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.14.5" } }, "@babel/highlight": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", - "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, @@ -326,13 +521,12 @@ } }, "@babel/types": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", - "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } }, @@ -342,32 +536,15 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, "@istanbuljs/load-nyc-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", - "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "requires": { "camelcase": "^5.3.1", "find-up": "^4.1.0", + "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, @@ -381,72 +558,166 @@ } }, "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.2.5.tgz", + "integrity": "sha512-smtlRF9vNKorRMCUtJ+yllIoiY8oFmfFG7xlzsAE76nKEwXNhjPOJIsc7Dv+AUitVt76t+KjIpUP9m98Crn2LQ==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", - "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "@jest/types": "^27.2.5", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.2.5", + "jest-util": "^27.2.5", "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } } }, "@jest/core": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.1.0.tgz", - "integrity": "sha512-iz05+NmwCmZRzMXvMo6KFipW7nzhbpEawrKrkkdJzgytavPse0biEnCNr2wRlyCsp3SmKaEY+SGv7YWYQnIdig==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.2.5.tgz", + "integrity": "sha512-VR7mQ+jykHN4WO3OvusRJMk4xCa2MFLipMS+43fpcRGaYrN1KwMATfVEXif7ccgFKYGy5D1TVXTNE4mGq/KMMA==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/reporters": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^27.2.5", + "@jest/reporters": "^27.2.5", + "@jest/test-result": "^27.2.5", + "@jest/transform": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", + "chalk": "^4.0.0", + "emittery": "^0.8.1", "exit": "^0.1.2", - "graceful-fs": "^4.2.3", - "jest-changed-files": "^25.1.0", - "jest-config": "^25.1.0", - "jest-haste-map": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-resolve-dependencies": "^25.1.0", - "jest-runner": "^25.1.0", - "jest-runtime": "^25.1.0", - "jest-snapshot": "^25.1.0", - "jest-util": "^25.1.0", - "jest-validate": "^25.1.0", - "jest-watcher": "^25.1.0", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "realpath-native": "^1.1.0", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^27.2.5", + "jest-config": "^27.2.5", + "jest-haste-map": "^27.2.5", + "jest-message-util": "^27.2.5", + "jest-regex-util": "^27.0.6", + "jest-resolve": "^27.2.5", + "jest-resolve-dependencies": "^27.2.5", + "jest-runner": "^27.2.5", + "jest-runtime": "^27.2.5", + "jest-snapshot": "^27.2.5", + "jest-util": "^27.2.5", + "jest-validate": "^27.2.5", + "jest-watcher": "^27.2.5", + "micromatch": "^4.0.4", "rimraf": "^3.0.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, "dependencies": { "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.21.3" } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "rimraf": { @@ -459,156 +730,335 @@ } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } } } }, "@jest/environment": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", - "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.2.5.tgz", + "integrity": "sha512-XvUW3q6OUF+54SYFCgbbfCd/BKTwm5b2MGLoc2jINXQLKQDTCS2P2IrpPOtQ08WWZDGzbhAzVhOYta3J2arubg==", "dev": true, "requires": { - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0" + "@jest/fake-timers": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", + "jest-mock": "^27.2.5" } }, "@jest/fake-timers": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", - "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.2.5.tgz", + "integrity": "sha512-ZGUb6jg7BgwY+nmO0TW10bc7z7Hl2G/UTAvmxEyZ/GgNFoa31tY9/cgXmqcxnnZ7o5Xs7RAOz3G1SKIj8IVDlg==", + "dev": true, + "requires": { + "@jest/types": "^27.2.5", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.2.5", + "jest-mock": "^27.2.5", + "jest-util": "^27.2.5" + } + }, + "@jest/globals": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.2.5.tgz", + "integrity": "sha512-naRI537GM+enFVJQs6DcwGYPn/0vgJNb06zGVbzXfDfe/epDPV73hP1vqO37PqSKDeOXM2KInr6ymYbL1HTP7g==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", - "lolex": "^5.0.0" + "@jest/environment": "^27.2.5", + "@jest/types": "^27.2.5", + "expect": "^27.2.5" } }, "@jest/reporters": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.1.0.tgz", - "integrity": "sha512-ORLT7hq2acJQa8N+NKfs68ZtHFnJPxsGqmofxW7v7urVhzJvpKZG9M7FAcgh9Ee1ZbCteMrirHA3m5JfBtAaDg==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.2.5.tgz", + "integrity": "sha512-zYuR9fap3Q3mxQ454VWF8I6jYHErh368NwcKHWO2uy2fwByqBzRHkf9j2ekMDM7PaSTWcLBSZyd7NNxR1iHxzQ==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^25.1.0", - "@jest/environment": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", + "@jest/console": "^27.2.5", + "@jest/test-result": "^27.2.5", + "@jest/transform": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", + "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.2", + "graceful-fs": "^4.2.4", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-instrument": "^4.0.3", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.0", - "jest-haste-map": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-runtime": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", - "node-notifier": "^6.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^27.2.5", + "jest-resolve": "^27.2.5", + "jest-util": "^27.2.5", + "jest-worker": "^27.2.5", "slash": "^3.0.0", "source-map": "^0.6.0", - "string-length": "^3.1.0", + "string-length": "^4.0.1", "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.0.1" + "v8-to-istanbul": "^8.1.0" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.5.tgz", + "integrity": "sha512-HTjEPZtcNKZ4LnhSp02NEH4vE+5OpJ0EsOWYvGQpHgUMLngydESAAMH5Wd/asPf29+XUDQZszxpLg1BkIIA2aw==", "dev": true, "requires": { + "@types/node": "*", "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - } - } - }, - "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + } + } + }, + "@jest/source-map": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.0.6.tgz", + "integrity": "sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g==", "dev": true, "requires": { "callsites": "^3.0.0", - "graceful-fs": "^4.2.3", + "graceful-fs": "^4.2.4", "source-map": "^0.6.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + } } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.2.5.tgz", + "integrity": "sha512-ub7j3BrddxZ0BdSnM5JCF6cRZJ/7j3wgdX0+Dtwhw2Po+HKsELCiXUTvh+mgS4/89mpnU1CPhZxe2mTvuLPJJg==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^27.2.5", + "@jest/types": "^27.2.5", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.1.0.tgz", - "integrity": "sha512-WgZLRgVr2b4l/7ED1J1RJQBOharxS11EFhmwDqknpknE0Pm87HLZVS2Asuuw+HQdfQvm2aXL2FvvBLxOD1D0iw==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.2.5.tgz", + "integrity": "sha512-8j8fHZRfnjbbdMitMAGFKaBZ6YqvFRFJlMJzcy3v75edTOqc7RY65S9JpMY6wT260zAcL2sTQRga/P4PglCu3Q==", "dev": true, "requires": { - "@jest/test-result": "^25.1.0", - "jest-haste-map": "^25.1.0", - "jest-runner": "^25.1.0", - "jest-runtime": "^25.1.0" + "@jest/test-result": "^27.2.5", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.2.5", + "jest-runtime": "^27.2.5" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + } } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.2.5.tgz", + "integrity": "sha512-29lRtAHHYGALbZOx343v0zKmdOg4Sb0rsA1uSv0818bvwRhs3TyElOmTVXlrw0v1ZTqXJCAH/cmoDXimBhQOJQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^27.2.5", "babel-plugin-istanbul": "^6.0.0", - "chalk": "^3.0.0", + "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", - "micromatch": "^4.0.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.2.5", + "jest-regex-util": "^27.0.6", + "jest-util": "^27.2.5", + "micromatch": "^4.0.4", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + } } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.2.5.tgz", + "integrity": "sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } } }, "@microsoft/api-extractor": { @@ -780,14 +1230,29 @@ } }, "@sinonjs/commons": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.0.tgz", - "integrity": "sha512-qbk9AP+cZUsKdW1GJsBpxPKFmCJ0T8swwzVje3qFd+AkQb74Q/tiuzrdfFg8AD2g5HH/XbE/I8Uc1KYHVYWfhg==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", "dev": true, "requires": { "type-detect": "4.0.8" } }, + "@sinonjs/fake-timers": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.0.1.tgz", + "integrity": "sha512-AU7kwFxreVd6OAXcAFlKSmZquiRUU0FvYm44k1Y1QbK7Co4m0aqfGMhjykIeQp/H6rcl+nFmj0zfdUcGVs9Dew==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, "@types/argparse": { "version": "1.0.33", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.33.tgz", @@ -795,9 +1260,9 @@ "dev": true }, "@types/babel__core": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.4.tgz", - "integrity": "sha512-c/5MuRz5HM4aizqL5ViYfW4iEnmfPcfbH4Xa6GgLT21dMc1NGeNnuS6egHheOmP+kCJ9CAzC4pv4SDCWTnRkbg==", + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz", + "integrity": "sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -808,18 +1273,18 @@ } }, "@types/babel__generator": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", - "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", + "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", "dev": true, "requires": { "@babel/types": "^7.0.0" } }, "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -827,9 +1292,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.8.tgz", - "integrity": "sha512-yGeB2dHEdvxjP0y4UbRtQaSkXJ9649fYCmIdRoul5kfAoGCwxuCbMhag0k3RPfnuh9kPGm8x89btcfDEXdVWGw==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -847,10 +1312,19 @@ "integrity": "sha512-K1DPVvnBCPxzD+G51/cxVIoc2X8uUVl1zpJeE6iKcgHMj4+tbat5Xu4TjV7v2QSDbIeAfLi2hIk+u2+s0MlpUQ==", "dev": true }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", "dev": true }, "@types/istanbul-lib-report": { @@ -863,23 +1337,22 @@ } }, "@types/istanbul-reports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", - "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "*", "@types/istanbul-lib-report": "*" } }, "@types/jest": { - "version": "25.1.2", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.2.tgz", - "integrity": "sha512-EsPIgEsonlXmYV7GzUqcvORsSS9Gqxw/OvkGwHfAdpjduNRxMlhsav0O5Kb0zijc/eXSO/uW6SJt9nwull8AUQ==", + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.2.tgz", + "integrity": "sha512-4dRxkS/AFX0c5XW6IPMNOydLn2tEhNhJV7DnYK+0bjoJZ+QTmfucBlihX7aoEsh/ocYtkLC73UbnBXBXIxsULA==", "dev": true, "requires": { - "jest-diff": "^25.1.0", - "pretty-format": "^25.1.0" + "jest-diff": "^27.0.0", + "pretty-format": "^27.0.0" } }, "@types/json-schema": { @@ -894,25 +1367,31 @@ "integrity": "sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==", "dev": true }, + "@types/prettier": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw==", + "dev": true + }, "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, "@types/yargs": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.3.tgz", - "integrity": "sha512-XCMQRK6kfpNBixHLyHUsGmXrpEmFFxzMrcnSXFMziHd8CoNJo8l16FkHyQq4x+xbM7E2XL83/O78OD8u+iZTdQ==", + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", "dev": true }, "@typescript-eslint/eslint-plugin": { @@ -1142,6 +1621,15 @@ "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", "dev": true }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, "ajv": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", @@ -1176,9 +1664,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -1194,24 +1682,6 @@ "sprintf-js": "~1.0.2" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", @@ -1224,12 +1694,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -1245,12 +1709,6 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -1263,12 +1721,6 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1282,18 +1734,61 @@ "dev": true }, "babel-jest": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.1.0.tgz", - "integrity": "sha512-tz0VxUhhOE2y+g8R2oFrO/2VtVjA1lkJeavlhExuRBg3LdNJY9gwQ+Vcvqt9+cqy71MCTJhewvTB7Qtnnr9SWg==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.2.5.tgz", + "integrity": "sha512-GC9pWCcitBhSuF7H3zl0mftoKizlswaF0E3qi+rPL417wKkCB0d+Sjjb0OfXvxj7gWiBf497ldgRMii68Xz+2g==", "dev": true, "requires": { - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", - "@types/babel__core": "^7.1.0", + "@jest/transform": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^25.1.0", - "chalk": "^3.0.0", + "babel-preset-jest": "^27.2.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + } } }, "babel-plugin-istanbul": { @@ -1310,23 +1805,45 @@ } }, "babel-plugin-jest-hoist": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.1.0.tgz", - "integrity": "sha512-oIsopO41vW4YFZ9yNYoLQATnnN46lp+MZ6H4VvPKFkcc2/fkl3CfE/NZZSmnEIEsJRmJAgkVEK0R7Zbl50CpTw==", + "version": "27.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.2.0.tgz", + "integrity": "sha512-TOux9khNKdi64mW+0OIhcmbAn75tTlzKhxmiNXevQaPbrBYK7YKjP1jl6NHTJ6XR5UgUrJbCnWlKVnJn29dfjw==", "dev": true, "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", "@types/babel__traverse": "^7.0.6" } }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, "babel-preset-jest": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.1.0.tgz", - "integrity": "sha512-eCGn64olaqwUMaugXsTtGAM2I0QTahjEtnRu0ql8Ie+gDWAc1N6wqN0k2NilnyTunM69Pad7gJY7LOtwLimoFQ==", + "version": "27.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.2.0.tgz", + "integrity": "sha512-z7MgQ3peBwN5L5aCqBKnF6iqdlvZvFUQynEhu0J+X9nHLU72jO3iY331lcYrg+AssJ8q7xsv5/3AICzVmJ/wvg==", "dev": true, "requires": { - "@babel/plugin-syntax-bigint": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^25.1.0" + "babel-plugin-jest-hoist": "^27.2.0", + "babel-preset-current-node-syntax": "^1.0.0" } }, "balanced-match": { @@ -1335,61 +1852,6 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -1424,21 +1886,17 @@ "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", "dev": true }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "browserslist": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.4.tgz", + "integrity": "sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==", "dev": true, "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } + "caniuse-lite": "^1.0.30001265", + "electron-to-chromium": "^1.3.867", + "escalade": "^3.1.1", + "node-releases": "^2.0.0", + "picocolors": "^1.0.0" } }, "bs-logger": { @@ -1460,28 +1918,11 @@ } }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1494,14 +1935,11 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } + "caniuse-lite": { + "version": "1.0.30001267", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001267.tgz", + "integrity": "sha512-r1mjTzAuJ9W8cPBGbbus8E0SKcUP7gn03R14Wk8FlAlqhH9hroy9nLqmpuXlfKEw/oILW+FGz47ipXV2O7x8lg==", + "dev": true }, "caseless": { "version": "0.12.0", @@ -1546,6 +1984,12 @@ } } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -1553,33 +1997,16 @@ "dev": true }, "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", + "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", "dev": true }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true }, "cli-cursor": { "version": "2.1.0", @@ -1597,20 +2024,20 @@ "dev": true }, "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "wrap-ansi": "^7.0.0" }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "emoji-regex": { @@ -1626,23 +2053,23 @@ "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } } } @@ -1654,21 +2081,11 @@ "dev": true }, "collect-v8-coverage": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz", - "integrity": "sha512-VKIhJgvk8E1W28m5avZ2Gv2Ruv5YiF56ug2oclvaG9md69BuZImMG2sk9g7QNKLUbtYAKQjXjYxbYZVUlMMKmQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", "dev": true }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1711,12 +2128,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1724,9 +2135,9 @@ "dev": true }, "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -1740,12 +2151,6 @@ } } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1825,22 +2230,16 @@ "ms": "^2.1.1" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, "decimal.js": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", "dev": true }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, "deep-is": { @@ -1849,55 +2248,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true }, "delayed-stream": { "version": "1.0.0", @@ -1912,9 +2267,9 @@ "dev": true }, "diff-sequences": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz", - "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==", + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz", + "integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==", "dev": true }, "dir-glob": { @@ -1954,50 +2309,29 @@ "safer-buffer": "^2.1.0" } }, + "electron-to-chromium": { + "version": "1.3.870", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.870.tgz", + "integrity": "sha512-PiJMshfq6PL+i1V+nKLwhHbCKeD8eAz8rvO9Cwk/7cChOHJBtufmjajLyYLsSRHguRFiOCVx3XzJLeZsIAYfSA==", + "dev": true + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true }, "escape-string-regexp": { "version": "1.0.5", @@ -2177,120 +2511,111 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", - "dev": true - }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "ms": "2.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "mimic-fn": "^2.1.0" } }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "shebang-regex": "^3.0.0" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, "expect": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-25.1.0.tgz", - "integrity": "sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.2.5.tgz", + "integrity": "sha512-ZrO0w7bo8BgGoP/bLz+HDCI+0Hfei9jUSZs5yI/Wyn9VkG9w8oJ7rHRgYj+MA7yqqFa0IwHA3flJzZtYugShJA==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "ansi-styles": "^4.0.0", - "jest-get-type": "^25.1.0", - "jest-matcher-utils": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-regex-util": "^25.1.0" + "@jest/types": "^27.2.5", + "ansi-styles": "^5.0.0", + "jest-get-type": "^27.0.6", + "jest-matcher-utils": "^27.2.5", + "jest-message-util": "^27.2.5", + "jest-regex-util": "^27.0.6" }, "dependencies": { "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true } } @@ -2301,27 +2626,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -2333,71 +2637,6 @@ "tmp": "^0.0.33" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -2519,12 +2758,6 @@ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -2542,15 +2775,6 @@ "mime-types": "^2.1.12" } }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, "fs-extra": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz", @@ -2569,9 +2793,9 @@ "dev": true }, "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -2588,9 +2812,9 @@ "dev": true }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -2599,19 +2823,16 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, "getpass": { @@ -2680,13 +2901,6 @@ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true, - "optional": true - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -2718,64 +2932,6 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "html-encoding-sniffer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.0.tgz", @@ -2786,11 +2942,22 @@ } }, "html-escaper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", - "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -2802,10 +2969,20 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, "iconv-lite": { @@ -2834,9 +3011,9 @@ } }, "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", "dev": true, "requires": { "pkg-dir": "^4.2.0", @@ -2914,98 +3091,24 @@ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "dev": true }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "is-ci": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", + "integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "ci-info": "^3.1.1" } }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "requires": { - "ci-info": "^2.0.0" + "has": "^1.0.3" } }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3039,14 +3142,11 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true }, "is-promise": { "version": "2.1.0", @@ -3054,67 +3154,24 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", - "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -3122,21 +3179,18 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.2.tgz", + "integrity": "sha512-o5+eTUYzCJ11/+JhW5/FUCdfsdoYVdQ/8I/OveE2XsjehYn5DdeSnNQAbjYaO8gQ6hvGTN6GM6ddQqpTVG5j8g==", "dev": true }, "istanbul-lib-instrument": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", - "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { "@babel/core": "^7.7.5", - "@babel/parser": "^7.7.5", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" @@ -3154,9 +3208,9 @@ } }, "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "requires": { "debug": "^4.1.1", @@ -3165,9 +3219,9 @@ } }, "istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.5.tgz", + "integrity": "sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -3175,659 +3229,1469 @@ } }, "jest": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-25.1.0.tgz", - "integrity": "sha512-FV6jEruneBhokkt9MQk0WUFoNTwnF76CLXtwNMfsc0um0TlB/LG2yxUd0KqaFjEJ9laQmVWQWS0sG/t2GsuI0w==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.2.5.tgz", + "integrity": "sha512-vDMzXcpQN4Ycaqu+vO7LX8pZwNNoKMhc+gSp6q1D8S6ftRk8gNW8cni3YFxknP95jxzQo23Lul0BI2FrWgnwYQ==", "dev": true, "requires": { - "@jest/core": "^25.1.0", + "@jest/core": "^27.2.5", "import-local": "^3.0.2", - "jest-cli": "^25.1.0" - }, - "dependencies": { - "jest-cli": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-25.1.0.tgz", - "integrity": "sha512-p+aOfczzzKdo3AsLJlhs8J5EW6ffVidfSZZxXedJ0mHPBOln1DccqFmGCoO8JWd4xRycfmwy1eoQkMsF8oekPg==", - "dev": true, - "requires": { - "@jest/core": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^25.1.0", - "jest-util": "^25.1.0", - "jest-validate": "^25.1.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^15.0.0" - } - } - } - }, - "jest-changed-files": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.1.0.tgz", - "integrity": "sha512-bdL1aHjIVy3HaBO3eEQeemGttsq1BDlHgWcOjEOIAcga7OOEGWHD2WSu8HhL7I1F0mFFyci8VKU4tRNk+qtwDA==", - "dev": true, - "requires": { - "@jest/types": "^25.1.0", - "execa": "^3.2.0", - "throat": "^5.0.0" + "jest-cli": "^27.2.5" }, "dependencies": { - "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "color-convert": "^2.0.1" } }, - "execa": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", - "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "p-finally": "^2.0.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "pump": "^3.0.0" + "color-name": "~1.1.4" } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "dev": true }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "jest-cli": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.2.5.tgz", + "integrity": "sha512-XzfcOXi5WQrXqFYsDxq5RDOKY4FNIgBgvgf3ZBz4e/j5/aWep5KnsAYH5OFPMdX/TP/LFsYQMRH7kzJUMh6JKg==", "dev": true, "requires": { - "path-key": "^3.0.0" + "@jest/core": "^27.2.5", + "@jest/test-result": "^27.2.5", + "@jest/types": "^27.2.5", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "jest-config": "^27.2.5", + "jest-util": "^27.2.5", + "jest-validate": "^27.2.5", + "prompts": "^2.0.1", + "yargs": "^16.2.0" } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + } + } + }, + "jest-changed-files": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.2.5.tgz", + "integrity": "sha512-jfnNJzF89csUKRPKJ4MwZ1SH27wTmX2xiAIHUHrsb/OYd9Jbo4/SXxJ17/nnx6RIifpthk3Y+LEeOk+/dDeGdw==", + "dev": true, + "requires": { + "@jest/types": "^27.2.5", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.2.5.tgz", + "integrity": "sha512-eyL9IcrAxm3Saq3rmajFCwpaxaRMGJ1KJs+7hlTDinXpJmeR3P02bheM3CYohE7UfwOBmrFMJHjgo/WPcLTM+Q==", + "dev": true, + "requires": { + "@jest/environment": "^27.2.5", + "@jest/test-result": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.2.5", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.2.5", + "jest-matcher-utils": "^27.2.5", + "jest-message-util": "^27.2.5", + "jest-runtime": "^27.2.5", + "jest-snapshot": "^27.2.5", + "jest-util": "^27.2.5", + "pretty-format": "^27.2.5", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "mimic-fn": "^2.1.0" + "color-convert": "^2.0.1" } }, - "p-finally": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", - "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "shebang-regex": "^3.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "isexe": "^2.0.0" + "color-name": "~1.1.4" } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true } } }, "jest-config": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.1.0.tgz", - "integrity": "sha512-tLmsg4SZ5H7tuhBC5bOja0HEblM0coS3Wy5LTCb2C8ZV6eWLewHyK+3qSq9Bi29zmWQ7ojdCd3pxpx4l4d2uGw==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.2.5.tgz", + "integrity": "sha512-QdENtn9b5rIIYGlbDNEcgY9LDL5kcokJnXrp7x8AGjHob/XFqw1Z6p+gjfna2sUulQsQ3ce2Fvntnv+7fKYDhQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^25.1.0", - "@jest/types": "^25.1.0", - "babel-jest": "^25.1.0", - "chalk": "^3.0.0", + "@jest/test-sequencer": "^27.2.5", + "@jest/types": "^27.2.5", + "babel-jest": "^27.2.5", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", "glob": "^7.1.1", - "jest-environment-jsdom": "^25.1.0", - "jest-environment-node": "^25.1.0", - "jest-get-type": "^25.1.0", - "jest-jasmine2": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-util": "^25.1.0", - "jest-validate": "^25.1.0", - "micromatch": "^4.0.2", - "pretty-format": "^25.1.0", - "realpath-native": "^1.1.0" - } - }, - "jest-diff": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz", - "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "diff-sequences": "^25.1.0", - "jest-get-type": "^25.1.0", - "pretty-format": "^25.1.0" - } - }, - "jest-docblock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.1.0.tgz", - "integrity": "sha512-370P/mh1wzoef6hUKiaMcsPtIapY25suP6JqM70V9RJvdKLrV4GaGbfUseUVk4FZJw4oTZ1qSCJNdrClKt5JQA==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.1.0.tgz", - "integrity": "sha512-R9EL8xWzoPySJ5wa0DXFTj7NrzKpRD40Jy+zQDp3Qr/2QmevJgkN9GqioCGtAJ2bW9P/MQRznQHQQhoeAyra7A==", - "dev": true, - "requires": { - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", - "jest-get-type": "^25.1.0", - "jest-util": "^25.1.0", - "pretty-format": "^25.1.0" - } - }, - "jest-environment-jsdom": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.1.0.tgz", - "integrity": "sha512-ILb4wdrwPAOHX6W82GGDUiaXSSOE274ciuov0lztOIymTChKFtC02ddyicRRCdZlB5YSrv3vzr1Z5xjpEe1OHQ==", - "dev": true, - "requires": { - "@jest/environment": "^25.1.0", - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", - "jsdom": "^15.1.1" + "graceful-fs": "^4.2.4", + "is-ci": "^3.0.0", + "jest-circus": "^27.2.5", + "jest-environment-jsdom": "^27.2.5", + "jest-environment-node": "^27.2.5", + "jest-get-type": "^27.0.6", + "jest-jasmine2": "^27.2.5", + "jest-regex-util": "^27.0.6", + "jest-resolve": "^27.2.5", + "jest-runner": "^27.2.5", + "jest-util": "^27.2.5", + "jest-validate": "^27.2.5", + "micromatch": "^4.0.4", + "pretty-format": "^27.2.5" }, "dependencies": { - "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" + "color-convert": "^2.0.1" } }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "webidl-conversions": "^4.0.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "whatwg-encoding": "^1.0.1" + "color-name": "~1.1.4" } }, - "jsdom": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", - "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^7.1.0", - "acorn-globals": "^4.3.2", - "array-equal": "^1.0.0", - "cssom": "^0.4.1", - "cssstyle": "^2.0.0", - "data-urls": "^1.1.0", - "domexception": "^1.0.1", - "escodegen": "^1.11.1", - "html-encoding-sniffer": "^1.0.2", - "nwsapi": "^2.2.0", - "parse5": "5.1.0", - "pn": "^1.1.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.7", - "saxes": "^3.1.9", - "symbol-tree": "^3.2.2", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.1", - "w3c-xmlserializer": "^1.1.2", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^7.0.0", - "ws": "^7.0.0", - "xml-name-validator": "^3.0.0" - } + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "parse5": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", - "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "dev": true }, - "saxes": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", - "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { - "xmlchars": "^2.1.1" + "braces": "^3.0.1", + "picomatch": "^2.2.3" } }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + } + } + }, + "jest-diff": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.2.5.tgz", + "integrity": "sha512-7gfwwyYkeslOOVQY4tVq5TaQa92mWfC9COsVYMNVYyJTOYAqbIkoD3twi5A+h+tAPtAelRxkqY6/xu+jwTr0dA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.2.5" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "punycode": "^2.1.0" + "color-convert": "^2.0.1" } }, - "w3c-xmlserializer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", - "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "domexception": "^1.0.1", - "webidl-conversions": "^4.0.2", - "xml-name-validator": "^3.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "color-name": "~1.1.4" } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true } } }, - "jest-environment-node": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.1.0.tgz", - "integrity": "sha512-U9kFWTtAPvhgYY5upnH9rq8qZkj6mYLup5l1caAjjx9uNnkLHN2xgZy5mo4SyLdmrh/EtB9UPpKFShvfQHD0Iw==", + "jest-docblock": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.0.6.tgz", + "integrity": "sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA==", "dev": true, "requires": { - "@jest/environment": "^25.1.0", - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0" + "detect-newline": "^3.0.0" } }, - "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", - "dev": true - }, - "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "jest-each": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.2.5.tgz", + "integrity": "sha512-HUPWIbJT0bXarRwKu/m7lYzqxR4GM5EhKOsu0z3t0SKtbFN6skQhpAUADM4qFShBXb9zoOuag5lcrR1x/WM+Ag==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", - "micromatch": "^4.0.2", - "sane": "^4.0.3", + "@jest/types": "^27.2.5", + "chalk": "^4.0.0", + "jest-get-type": "^27.0.6", + "jest-util": "^27.2.5", + "pretty-format": "^27.2.5" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "jest-environment-jsdom": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.2.5.tgz", + "integrity": "sha512-QtRpOh/RQKuXniaWcoFE2ElwP6tQcyxHu0hlk32880g0KczdonCs5P1sk5+weu/OVzh5V4Bt1rXuQthI01mBLg==", + "dev": true, + "requires": { + "@jest/environment": "^27.2.5", + "@jest/fake-timers": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", + "jest-mock": "^27.2.5", + "jest-util": "^27.2.5", + "jsdom": "^16.6.0" + }, + "dependencies": { + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "acorn": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "dev": true + } + } + }, + "jest-environment-node": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.2.5.tgz", + "integrity": "sha512-0o1LT4grm7iwrS8fIoLtwJxb/hoa3GsH7pP10P02Jpj7Mi4BXy65u46m89vEM2WfD1uFJQ2+dfDiWZNA2e6bJg==", + "dev": true, + "requires": { + "@jest/environment": "^27.2.5", + "@jest/fake-timers": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", + "jest-mock": "^27.2.5", + "jest-util": "^27.2.5" + } + }, + "jest-get-type": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz", + "integrity": "sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==", + "dev": true + }, + "jest-haste-map": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.2.5.tgz", + "integrity": "sha512-pzO+Gw2WLponaSi0ilpzYBE0kuVJstoXBX8YWyUebR8VaXuX4tzzn0Zp23c/WaETo7XYTGv2e8KdnpiskAFMhQ==", + "dev": true, + "requires": { + "@jest/types": "^27.2.5", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^27.0.6", + "jest-serializer": "^27.0.6", + "jest-util": "^27.2.5", + "jest-worker": "^27.2.5", + "micromatch": "^4.0.4", "walker": "^1.0.7" }, "dependencies": { + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.5.tgz", + "integrity": "sha512-HTjEPZtcNKZ4LnhSp02NEH4vE+5OpJ0EsOWYvGQpHgUMLngydESAAMH5Wd/asPf29+XUDQZszxpLg1BkIIA2aw==", "dev": true, "requires": { + "@types/node": "*", "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" + "supports-color": "^8.0.0" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } } } }, "jest-jasmine2": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.1.0.tgz", - "integrity": "sha512-GdncRq7jJ7sNIQ+dnXvpKO2MyP6j3naNK41DTTjEAhLEdpImaDA9zSAZwDhijjSF/D7cf4O5fdyUApGBZleaEg==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.2.5.tgz", + "integrity": "sha512-hdxY9Cm/CjLqu2tXeAoQHPgA4vcqlweVXYOg1+S9FeFdznB9Rti+eEBKDDkmOy9iqr4Xfbq95OkC4NFbXXPCAQ==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^25.1.0", - "@jest/source-map": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", + "@jest/environment": "^27.2.5", + "@jest/source-map": "^27.0.6", + "@jest/test-result": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", + "chalk": "^4.0.0", "co": "^4.6.0", - "expect": "^25.1.0", + "expect": "^27.2.5", "is-generator-fn": "^2.0.0", - "jest-each": "^25.1.0", - "jest-matcher-utils": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-runtime": "^25.1.0", - "jest-snapshot": "^25.1.0", - "jest-util": "^25.1.0", - "pretty-format": "^25.1.0", - "throat": "^5.0.0" + "jest-each": "^27.2.5", + "jest-matcher-utils": "^27.2.5", + "jest-message-util": "^27.2.5", + "jest-runtime": "^27.2.5", + "jest-snapshot": "^27.2.5", + "jest-util": "^27.2.5", + "pretty-format": "^27.2.5", + "throat": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } } }, "jest-leak-detector": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.1.0.tgz", - "integrity": "sha512-3xRI264dnhGaMHRvkFyEKpDeaRzcEBhyNrOG5oT8xPxOyUAblIAQnpiR3QXu4wDor47MDTiHbiFcbypdLcLW5w==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.2.5.tgz", + "integrity": "sha512-HYsi3GUR72bYhOGB5C5saF9sPdxGzSjX7soSQS+BqDRysc7sPeBwPbhbuT8DnOpijnKjgwWQ8JqvbmReYnt3aQ==", "dev": true, "requires": { - "jest-get-type": "^25.1.0", - "pretty-format": "^25.1.0" + "jest-get-type": "^27.0.6", + "pretty-format": "^27.2.5" } }, "jest-matcher-utils": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz", - "integrity": "sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.2.5.tgz", + "integrity": "sha512-qNR/kh6bz0Dyv3m68Ck2g1fLW5KlSOUNcFQh87VXHZwWc/gY6XwnKofx76Qytz3x5LDWT09/2+yXndTkaG4aWg==", "dev": true, "requires": { - "chalk": "^3.0.0", - "jest-diff": "^25.1.0", - "jest-get-type": "^25.1.0", - "pretty-format": "^25.1.0" + "chalk": "^4.0.0", + "jest-diff": "^27.2.5", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.2.5" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "micromatch": "^4.0.2", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.2.5.tgz", + "integrity": "sha512-ggXSLoPfIYcbmZ8glgEJZ8b+e0Msw/iddRmgkoO7lDAr9SmI65IIfv7VnvTnV4FGnIIUIjzM+fHRHO5RBvyAbQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.2.5", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.4", + "pretty-format": "^27.2.5", "slash": "^3.0.0", - "stack-utils": "^1.0.1" + "stack-utils": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "jest-mock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", - "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.2.5.tgz", + "integrity": "sha512-HiMB3LqE9RzmeMzZARi2Bz3NoymxyP0gCid4y42ca1djffNtYFKgI220aC1VP1mUZ8rbpqZbHZOJ15093bZV/Q==", "dev": true, "requires": { - "@jest/types": "^25.1.0" + "@jest/types": "^27.2.5", + "@types/node": "*" } }, "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", "dev": true }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.0.6.tgz", + "integrity": "sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ==", "dev": true }, "jest-resolve": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", - "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", - "dev": true, - "requires": { - "@jest/types": "^25.1.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.2.5.tgz", + "integrity": "sha512-q5irwS3oS73SKy3+FM/HL2T7WJftrk9BRzrXF92f7net5HMlS7lJMg/ZwxLB4YohKqjSsdksEw7n/jvMxV7EKg==", + "dev": true, + "requires": { + "@jest/types": "^27.2.5", + "chalk": "^4.0.0", + "escalade": "^3.1.1", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.2.5", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.2.5", + "jest-validate": "^27.2.5", + "resolve": "^1.20.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } } }, "jest-resolve-dependencies": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.1.0.tgz", - "integrity": "sha512-Cu/Je38GSsccNy4I2vL12ZnBlD170x2Oh1devzuM9TLH5rrnLW1x51lN8kpZLYTvzx9j+77Y5pqBaTqfdzVzrw==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.2.5.tgz", + "integrity": "sha512-BSjefped31bcvvCh++/pN9ueqqN1n0+p8/58yScuWfklLm2tbPbS9d251vJhAy0ZI2pL/0IaGhOTJrs9Y4FJlg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-snapshot": "^25.1.0" + "@jest/types": "^27.2.5", + "jest-regex-util": "^27.0.6", + "jest-snapshot": "^27.2.5" } }, "jest-runner": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.1.0.tgz", - "integrity": "sha512-su3O5fy0ehwgt+e8Wy7A8CaxxAOCMzL4gUBftSs0Ip32S0epxyZPDov9Znvkl1nhVOJNf4UwAsnqfc3plfQH9w==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.2.5.tgz", + "integrity": "sha512-n41vw9RLg5TKAnEeJK9d6pGOsBOpwE89XBniK+AD1k26oIIy3V7ogM1scbDjSheji8MUPC9pNgCrZ/FHLVDNgg==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/environment": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", + "@jest/console": "^27.2.5", + "@jest/environment": "^27.2.5", + "@jest/test-result": "^27.2.5", + "@jest/transform": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", "exit": "^0.1.2", - "graceful-fs": "^4.2.3", - "jest-config": "^25.1.0", - "jest-docblock": "^25.1.0", - "jest-haste-map": "^25.1.0", - "jest-jasmine2": "^25.1.0", - "jest-leak-detector": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-runtime": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "graceful-fs": "^4.2.4", + "jest-docblock": "^27.0.6", + "jest-environment-jsdom": "^27.2.5", + "jest-environment-node": "^27.2.5", + "jest-haste-map": "^27.2.5", + "jest-leak-detector": "^27.2.5", + "jest-message-util": "^27.2.5", + "jest-resolve": "^27.2.5", + "jest-runtime": "^27.2.5", + "jest-util": "^27.2.5", + "jest-worker": "^27.2.5", "source-map-support": "^0.5.6", - "throat": "^5.0.0" + "throat": "^6.0.1" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.5.tgz", + "integrity": "sha512-HTjEPZtcNKZ4LnhSp02NEH4vE+5OpJ0EsOWYvGQpHgUMLngydESAAMH5Wd/asPf29+XUDQZszxpLg1BkIIA2aw==", "dev": true, "requires": { + "@types/node": "*", "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } } } }, "jest-runtime": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.1.0.tgz", - "integrity": "sha512-mpPYYEdbExKBIBB16ryF6FLZTc1Rbk9Nx0ryIpIMiDDkOeGa0jQOKVI/QeGvVGlunKKm62ywcioeFVzIbK03bA==", - "dev": true, - "requires": { - "@jest/console": "^25.1.0", - "@jest/environment": "^25.1.0", - "@jest/source-map": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.2.5.tgz", + "integrity": "sha512-N0WRZ3QszKyZ3Dm27HTBbBuestsSd3Ud5ooVho47XZJ8aSKO/X1Ag8M1dNx9XzfGVRNdB/xCA3lz8MJwIzPLLA==", + "dev": true, + "requires": { + "@jest/console": "^27.2.5", + "@jest/environment": "^27.2.5", + "@jest/fake-timers": "^27.2.5", + "@jest/globals": "^27.2.5", + "@jest/source-map": "^27.0.6", + "@jest/test-result": "^27.2.5", + "@jest/transform": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", "exit": "^0.1.2", "glob": "^7.1.3", - "graceful-fs": "^4.2.3", - "jest-config": "^25.1.0", - "jest-haste-map": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-snapshot": "^25.1.0", - "jest-util": "^25.1.0", - "jest-validate": "^25.1.0", - "realpath-native": "^1.1.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.2.5", + "jest-message-util": "^27.2.5", + "jest-mock": "^27.2.5", + "jest-regex-util": "^27.0.6", + "jest-resolve": "^27.2.5", + "jest-snapshot": "^27.2.5", + "jest-util": "^27.2.5", + "jest-validate": "^27.2.5", "slash": "^3.0.0", "strip-bom": "^4.0.0", - "yargs": "^15.0.0" + "yargs": "^16.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + } } }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", - "dev": true + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.0.6.tgz", + "integrity": "sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + } + } }, "jest-snapshot": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.1.0.tgz", - "integrity": "sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.2.5.tgz", + "integrity": "sha512-2/Jkn+VN6Abwz0llBltZaiJMnL8b1j5Bp/gRIxe9YR3FCEh9qp0TXVV0dcpTGZ8AcJV1SZGQkczewkI9LP5yGw==", "dev": true, "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/parser": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", "@babel/types": "^7.0.0", - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", - "expect": "^25.1.0", - "jest-diff": "^25.1.0", - "jest-get-type": "^25.1.0", - "jest-matcher-utils": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "mkdirp": "^0.5.1", + "@jest/transform": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.2.5", + "graceful-fs": "^4.2.4", + "jest-diff": "^27.2.5", + "jest-get-type": "^27.0.6", + "jest-haste-map": "^27.2.5", + "jest-matcher-utils": "^27.2.5", + "jest-message-util": "^27.2.5", + "jest-resolve": "^27.2.5", + "jest-util": "^27.2.5", "natural-compare": "^1.4.0", - "pretty-format": "^25.1.0", - "semver": "^7.1.1" + "pretty-format": "^27.2.5", + "semver": "^7.3.2" }, "dependencies": { - "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.2.5.tgz", + "integrity": "sha512-QRhDC6XxISntMzFRd/OQ6TGsjbzA5ONO0tlAj2ElHs155x1aEr0rkYJBEysG6H/gZVH3oGFzCdAB/GA8leh8NQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "@jest/types": "^27.2.5", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^3.0.0", + "picomatch": "^2.2.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + } } }, "jest-validate": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.1.0.tgz", - "integrity": "sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.2.5.tgz", + "integrity": "sha512-XgYtjS89nhVe+UfkbLgcm+GgXKWgL80t9nTcNeejyO3t0Sj/yHE8BtIJqjZu9NXQksYbGImoQRXmQ1gP+Guffw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "jest-get-type": "^25.1.0", + "@jest/types": "^27.2.5", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.0.6", "leven": "^3.1.0", - "pretty-format": "^25.1.0" + "pretty-format": "^27.2.5" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } } }, "jest-watcher": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.1.0.tgz", - "integrity": "sha512-Q9eZ7pyaIr6xfU24OeTg4z1fUqBF/4MP6J801lyQfg7CsnZ/TCzAPvCfckKdL5dlBBEKBeHV0AdyjFZ5eWj4ig==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.2.5.tgz", + "integrity": "sha512-umV4qGozg2Dn6DTTtqAh9puPw+DGLK9AQas7+mWjiK8t0fWMpxKg8ZXReZw7L4C88DqorsGUiDgwHNZ+jkVrkQ==", "dev": true, "requires": { - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^27.2.5", + "@jest/types": "^27.2.5", + "@types/node": "*", "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "jest-util": "^25.1.0", - "string-length": "^3.1.0" + "chalk": "^4.0.0", + "jest-util": "^27.2.5", + "string-length": "^4.0.1" }, "dependencies": { "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "color-name": "~1.1.4" } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true } } }, @@ -3946,18 +4810,18 @@ "dev": true }, "json5": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" }, "dependencies": { "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true } } @@ -3983,12 +4847,6 @@ "verror": "1.10.0" } }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4050,15 +4908,6 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, - "lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4087,9 +4936,9 @@ } }, "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "makeerror": { @@ -4101,21 +4950,6 @@ "tmpl": "1.0.x" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4174,27 +5008,6 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -4216,25 +5029,6 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4259,19 +5053,11 @@ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", "dev": true }, - "node-notifier": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz", - "integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==", - "dev": true, - "optional": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^2.1.1", - "semver": "^6.3.0", - "shellwords": "^0.1.1", - "which": "^1.3.1" - } + "node-releases": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.0.tgz", + "integrity": "sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA==", + "dev": true }, "normalize-path": { "version": "3.0.0", @@ -4280,12 +5066,20 @@ "dev": true }, "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + } } }, "nwsapi": { @@ -4300,89 +5094,6 @@ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4421,18 +5132,6 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, "p-limit": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", @@ -4472,12 +5171,6 @@ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "dev": true }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4514,6 +5207,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", @@ -4544,12 +5243,6 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -4557,46 +5250,27 @@ "dev": true }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.2.5.tgz", + "integrity": "sha512-+nYn2z9GgicO9JiqmY25Xtq8SYfZ/5VCpEU3pppHHNAhd1y+ZXxmNPd1evmNcAd6Hz4iBV2kf0UpGth5A/VJ7g==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "@jest/types": "^27.2.5", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true } } @@ -4608,13 +5282,13 @@ "dev": true }, "prompts": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.1.tgz", - "integrity": "sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "requires": { "kleur": "^3.0.3", - "sisteransi": "^1.0.4" + "sisteransi": "^1.0.5" } }, "psl": { @@ -4623,16 +5297,6 @@ "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -4652,54 +5316,17 @@ "dev": true }, "react-is": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", - "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", - "dev": true, - "requires": { - "util.promisify": "^1.0.0" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -4784,12 +5411,6 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "resolve": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", @@ -4822,12 +5443,6 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -4838,12 +5453,6 @@ "signal-exit": "^3.0.2" } }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4923,12 +5532,6 @@ "estree-walker": "^0.6.1" } }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true - }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", @@ -4957,181 +5560,25 @@ }, "dependencies": { "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } } } }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "saxes": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/saxes/-/saxes-4.0.2.tgz", @@ -5153,35 +5600,6 @@ "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "dev": true }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -5197,13 +5615,6 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true, - "optional": true - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -5211,9 +5622,9 @@ "dev": true }, "sisteransi": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz", - "integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, "slash": { @@ -5233,184 +5644,28 @@ "is-fullwidth-code-point": "^2.0.0" } }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, "sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "dev": true }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5435,29 +5690,19 @@ } }, "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "escape-string-regexp": "^2.0.0" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true } } }, @@ -5468,13 +5713,30 @@ "dev": true }, "string-length": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", - "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^5.2.0" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } } }, "string-width": { @@ -5498,26 +5760,6 @@ } } }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -5541,12 +5783,6 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -5577,9 +5813,9 @@ } }, "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", "dev": true, "requires": { "has-flag": "^4.0.0", @@ -5636,12 +5872,12 @@ }, "dependencies": { "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.21.3" } } } @@ -5664,9 +5900,9 @@ "dev": true }, "throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", "dev": true }, "through": { @@ -5691,9 +5927,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-fast-properties": { @@ -5702,38 +5938,6 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5764,42 +5968,28 @@ } }, "ts-jest": { - "version": "25.2.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.2.0.tgz", - "integrity": "sha512-VaRdb0da46eorLfuHEFf0G3d+jeREcV+Wb/SvW71S4y9Oe8SHWU+m1WY/3RaMknrBsnvmVH0/rRjT8dkgeffNQ==", + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.6.tgz", + "integrity": "sha512-XWkEBbrkyUWJdK9FwiCVdBZ7ZmT7sxcKtyVEZNmo7u8eQw6NHmtYEM2WpkX9VfnRI0DjSr6INfEHC9vCFhsFnQ==", "dev": true, "requires": { "bs-logger": "0.x", - "buffer-from": "1.x", "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", "json5": "2.x", "lodash.memoize": "4.x", "make-error": "1.x", - "mkdirp": "0.x", - "resolve": "1.x", - "semver": "^5.5", - "yargs-parser": "10.x" + "semver": "7.x", + "yargs-parser": "20.x" }, "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "lru-cache": "^6.0.0" } } } @@ -5857,9 +6047,9 @@ "dev": true }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "typedarray-to-buffer": { @@ -5883,64 +6073,12 @@ "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==", "dev": true }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -5950,30 +6088,6 @@ "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, "uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", @@ -5987,9 +6101,9 @@ "dev": true }, "v8-to-istanbul": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.2.tgz", - "integrity": "sha512-G9R+Hpw0ITAmPSr47lSlc5A1uekSYzXxTMlFxso2xoffwo4jQnzbv1p9yXIinO8UMZKfAFewaCHwWvnH4Jb4Ug==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz", + "integrity": "sha512-/PRhfd8aTNp9Ggr62HPzXg2XasNFGy5PBt0Rp04du7/8GNNSgxFL6WBTkgMKSL9bFjH+8kKEG3f37FmxiTqUUA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.1", @@ -6090,12 +6204,6 @@ "isexe": "^2.0.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -6103,9 +6211,9 @@ "dev": true }, "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", @@ -6114,18 +6222,17 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -6157,23 +6264,23 @@ "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } } } @@ -6194,9 +6301,9 @@ } }, "write-file-atomic": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", - "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { "imurmurhash": "^0.1.4", @@ -6224,9 +6331,9 @@ "dev": true }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { @@ -6236,28 +6343,24 @@ "dev": true }, "yargs": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz", - "integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^16.1.0" + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "emoji-regex": { @@ -6273,36 +6376,32 @@ "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } } } }, "yargs-parser": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz", - "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true }, "z-schema": { "version": "3.18.4", diff --git a/package.json b/package.json index e36941dcc..648f00ed6 100644 --- a/package.json +++ b/package.json @@ -37,19 +37,19 @@ "devDependencies": { "@microsoft/api-extractor": "^7.7.2", "@rollup/plugin-replace": "^3.0.0", - "@types/jest": "^25.1.2", + "@types/jest": "^27.0.2", "@typescript-eslint/eslint-plugin": "^4.9.1", "@typescript-eslint/parser": "^4.9.1", "chalk": "^3.0.0", "commander": "2.11.0", "eslint": "6.3.0", "fs-extra": "4.0.2", - "jest": "^25.1.0", + "jest": "^27.2.5", "jsdom": "^16.0.0", "rollup": "^1.28.0", "rollup-plugin-typescript2": "^0.25.3", "rollup-plugin-uglify": "^6.0.4", - "ts-jest": "^25.2.0", + "ts-jest": "^27.0.6", "typescript": "^4.4.3", "uglify-js": "^3.10.0" } diff --git a/test/ut/spec/animation/ElementAnimation.test.ts b/test/ut/spec/animation/ElementAnimation.test.ts index 39548c48e..88b21ffd0 100644 --- a/test/ut/spec/animation/ElementAnimation.test.ts +++ b/test/ut/spec/animation/ElementAnimation.test.ts @@ -56,7 +56,7 @@ describe('ElementAnimation', function () { duration: 100, done: () => { animation.stop(); - resolve(); + resolve(undefined); } }); for (let i = 0; i < rect.animators.length; i++) { diff --git a/test/ut/spec/core/platform.test.ts b/test/ut/spec/core/platform.test.ts new file mode 100644 index 000000000..5f307fe88 --- /dev/null +++ b/test/ut/spec/core/platform.test.ts @@ -0,0 +1,36 @@ +import * as platform from '../../../../src/core/platform'; + +describe('platform', function() { + + it('Default font should be correct', function () { + expect(platform.DEFAULT_FONT_SIZE).toBe(12); + expect(platform.DEFAULT_FONT_FAMILY).toBe('sans-serif'); + expect(platform.DEFAULT_FONT).toBe('12px sans-serif'); + }); + + it('setPlatformAPI can override methods', function () { + function createCanvas() { + return { + width: 1 + }as HTMLCanvasElement; + } + function measureText() { + return { width: 16.5 }; + } + + const oldCreateCanvas = platform.platformApi.createCanvas; + const oldMeasureText = platform.platformApi.measureText; + platform.setPlatformAPI({ + createCanvas, + measureText + }); + expect(platform.platformApi.createCanvas().width).toBe(1); + expect(platform.platformApi.measureText('a', '12px sans-serif').width).toBe(16.5); + + // Restore + platform.setPlatformAPI({ + createCanvas: oldCreateCanvas, + measureText: oldMeasureText + }); + }) +}); \ No newline at end of file From 38102ff6b415b0143eab239683dd9cb90ffc0aff Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 15 Oct 2021 11:17:44 +0800 Subject: [PATCH 063/148] feat(platform): add loadImage in platform api --- src/core/platform.ts | 15 ++++++++++++++- src/graphic/helper/image.ts | 9 +++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/core/platform.ts b/src/core/platform.ts index 50b407712..1a1fb0d4e 100644 --- a/src/core/platform.ts +++ b/src/core/platform.ts @@ -6,6 +6,11 @@ interface Platform { // TODO CanvasLike? createCanvas(): HTMLCanvasElement measureText(text: string, font?: string): { width: number } + loadImage( + src: string, + onload: () => void, + onerrror: () => void + ): HTMLImageElement } // Text width map used for environment there is no canvas @@ -85,7 +90,15 @@ export const platformApi: Platform = { return { width }; } }; - })() + })(), + + loadImage(src, onload, onerror) { + const image = new Image(); + image.onload = onload; + image.onerror = onerror; + image.src = src; + return image; + } }; export function setPlatformAPI(newPlatformApis: Partial) { diff --git a/src/graphic/helper/image.ts b/src/graphic/helper/image.ts index f99e096cb..aa2bd37ba 100644 --- a/src/graphic/helper/image.ts +++ b/src/graphic/helper/image.ts @@ -1,5 +1,6 @@ import LRU from '../../core/LRU'; +import { platformApi } from '../../core/platform'; import { ImageLike } from '../../core/types'; const globalImageCache = new LRU(50); @@ -64,8 +65,10 @@ export function createOrUpdateImage( !isImageReady(image) && cachedImgObj.pending.push(pendingWrap); } else { - image = new Image(); - image.onload = image.onerror = imageOnLoad; + const image = platformApi.loadImage( + newImageOrSrc, imageOnLoad, imageOnLoad + ); + (image as any).__zrImageSrc = newImageOrSrc; globalImageCache.put( newImageOrSrc, @@ -74,8 +77,6 @@ export function createOrUpdateImage( pending: [pendingWrap] } ); - - image.src = (image as any).__zrImageSrc = newImageOrSrc; } return image; From 9468300b17dd9ce7cc8adb2ff3a169967162d809 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 15 Oct 2021 12:39:40 +0800 Subject: [PATCH 064/148] fix(platform): wider loadImage callback type --- src/core/platform.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/platform.ts b/src/core/platform.ts index 1a1fb0d4e..7df2364cb 100644 --- a/src/core/platform.ts +++ b/src/core/platform.ts @@ -8,8 +8,8 @@ interface Platform { measureText(text: string, font?: string): { width: number } loadImage( src: string, - onload: () => void, - onerrror: () => void + onload: () => void | HTMLImageElement['onload'], + onerror: () => void | HTMLImageElement['onerror'] ): HTMLImageElement } From a49e7e403c49c057c31c6bddcd74ff8da1760113 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 15 Oct 2021 14:37:17 +0800 Subject: [PATCH 065/148] fix type error --- src/PainterBase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PainterBase.ts b/src/PainterBase.ts index ead6db3ee..c41fbd178 100644 --- a/src/PainterBase.ts +++ b/src/PainterBase.ts @@ -26,7 +26,7 @@ export interface PainterBase { clear(): void // must be given if ssr is true. - renderToString?(): void; + renderToString?(): string; getType: () => string From cd756a9edc6e47035859b58d624d6e2688472f73 Mon Sep 17 00:00:00 2001 From: pissang Date: Sun, 21 Nov 2021 15:42:01 +0800 Subject: [PATCH 066/148] refact: remove spline implentation to reduce code size a bit --- src/animation/Animation.ts | 2 +- src/animation/Animator.ts | 193 ++++++---------------------------- src/animation/easing.ts | 2 +- src/graphic/helper/poly.ts | 9 +- src/graphic/shape/Polygon.ts | 2 +- src/graphic/shape/Polyline.ts | 2 +- test/orbit.html | 98 ----------------- test/orbit2.html | 53 ---------- test/poly.html | 18 ---- 9 files changed, 41 insertions(+), 338 deletions(-) delete mode 100644 test/orbit.html delete mode 100644 test/orbit2.html diff --git a/src/animation/Animation.ts b/src/animation/Animation.ts index a1c31850e..453814658 100644 --- a/src/animation/Animation.ts +++ b/src/animation/Animation.ts @@ -37,7 +37,7 @@ interface AnimationOption { * x: 100, * y: 100 * }) - * .start('spline'); + * .start(); */ export default class Animation extends Eventful { diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 6632d83d2..62c17a23d 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -145,69 +145,6 @@ function is1DArraySame(arr0: NumberArray, arr1: NumberArray) { return true; } - -/** - * Catmull Rom interpolate number - */ -function catmullRomInterpolate( - p0: number, p1: number, p2: number, p3: number, t: number, t2: number, t3: number -) { - const v0 = (p2 - p0) * 0.5; - const v1 = (p3 - p1) * 0.5; - return (2 * (p1 - p2) + v0 + v1) * t3 - + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 - + v0 * t + p1; -} -/** - * Catmull Rom interpolate 1D array - */ -function catmullRomInterpolate1DArray( - out: NumberArray, - p0: NumberArray, - p1: NumberArray, - p2: NumberArray, - p3: NumberArray, - t: number, - t2: number, - t3: number -) { - const len = p0.length; - for (let i = 0; i < len; i++) { - out[i] = catmullRomInterpolate( - p0[i], p1[i], p2[i], p3[i], t, t2, t3 - ); - } -} - -/** - * Catmull Rom interpolate 2D array - */ -function catmullRomInterpolate2DArray( - out: NumberArray[], - p0: NumberArray[], - p1: NumberArray[], - p2: NumberArray[], - p3: NumberArray[], - t: number, - t2: number, - t3: number -) { - const len = p0.length; - const len2 = p0[0].length; - for (let i = 0; i < len; i++) { - if (!out[i]) { - out[1] = []; - } - for (let j = 0; j < len2; j++) { - out[i][j] = catmullRomInterpolate( - p0[i][j], p1[i][j], p2[i][j], p3[i][j], - t, t2, t3 - ); - } - } -} - - export function cloneValue(value: InterpolatableType) { if (isArrayLike(value)) { const len = value.length; @@ -253,11 +190,6 @@ class Track { propName: string - /** - * If use spline interpolate - */ - useSpline: boolean - // Larger than 0 if value is array arrDim: number = 0 isValueColor: boolean @@ -513,7 +445,6 @@ class Track { this._lastFrame = frameIdx; this._lastFramePercent = percent; - const range = (nextFrame.percent - frame.percent); if (range === 0) { return; @@ -527,101 +458,47 @@ class Track { if ((arrDim > 0 || isValueColor) && !targetArr) { targetArr = this._additiveValue = []; } - if (this.useSpline) { - const p1 = keyframes[frameIdx][valueKey]; - const p0 = keyframes[frameIdx === 0 ? frameIdx : frameIdx - 1][valueKey]; - const p2 = keyframes[frameIdx > kfsNum - 2 ? kfsNum - 1 : frameIdx + 1][valueKey]; - const p3 = keyframes[frameIdx > kfsNum - 3 ? kfsNum - 1 : frameIdx + 2][valueKey]; - - if (arrDim > 0) { - arrDim === 1 - ? catmullRomInterpolate1DArray( - targetArr as NumberArray, - p0 as NumberArray, - p1 as NumberArray, - p2 as NumberArray, - p3 as NumberArray, - w, w * w, w * w * w - ) - : catmullRomInterpolate2DArray( - targetArr as NumberArray[], - p0 as NumberArray[], p1 as NumberArray[], p2 as NumberArray[], p3 as NumberArray[], - w, w * w, w * w * w - ); - } - else if (isValueColor) { - catmullRomInterpolate1DArray( - targetArr, - p0 as NumberArray, p1 as NumberArray, p2 as NumberArray, p3 as NumberArray, - w, w * w, w * w * w + + if (arrDim > 0) { + arrDim === 1 + ? interpolate1DArray( + targetArr as NumberArray, + frame[valueKey] as NumberArray, + nextFrame[valueKey] as NumberArray, + w + ) + : interpolate2DArray( + targetArr as NumberArray[], + frame[valueKey] as NumberArray[], + nextFrame[valueKey] as NumberArray[], + w ); - if (!isAdditive) { // Convert to string later:) - target[propName] = rgba2String(targetArr); - } - } - else { - let value; - if (!this.interpolable) { - // String is step(0.5) - // value = step(p1, p2, w); - value = p2; - } - else { - value = catmullRomInterpolate( - p0 as number, p1 as number, p2 as number, p3 as number, - w, w * w, w * w * w - ); - } - if (isAdditive) { - this._additiveValue = value; - } - else { - target[propName] = value; - } + } + else if (isValueColor) { + interpolate1DArray( + targetArr, + frame[valueKey] as NumberArray, + nextFrame[valueKey] as NumberArray, + w + ); + if (!isAdditive) { // Convert to string later:) + target[propName] = rgba2String(targetArr); } } else { - if (arrDim > 0) { - arrDim === 1 - ? interpolate1DArray( - targetArr as NumberArray, - frame[valueKey] as NumberArray, - nextFrame[valueKey] as NumberArray, - w - ) - : interpolate2DArray( - targetArr as NumberArray[], - frame[valueKey] as NumberArray[], - nextFrame[valueKey] as NumberArray[], - w - ); + let value; + if (!this.interpolable) { + // String is step(0.5) + value = step(frame[valueKey], nextFrame[valueKey], w); } - else if (isValueColor) { - interpolate1DArray( - targetArr, - frame[valueKey] as NumberArray, - nextFrame[valueKey] as NumberArray, - w - ); - if (!isAdditive) { // Convert to string later:) - target[propName] = rgba2String(targetArr); - } + else { + value = interpolateNumber(frame[valueKey] as number, nextFrame[valueKey] as number, w); + } + if (isAdditive) { + this._additiveValue = value; } else { - let value; - if (!this.interpolable) { - // String is step(0.5) - value = step(frame[valueKey], nextFrame[valueKey], w); - } - else { - value = interpolateNumber(frame[valueKey] as number, nextFrame[valueKey] as number, w); - } - if (isAdditive) { - this._additiveValue = value; - } - else { - target[propName] = value; - } + target[propName] = value; } } @@ -934,7 +811,7 @@ export default class Animator { this.animation.addClip(clip); } - if (easing && easing !== 'spline') { + if (easing) { clip.easing = easing; } } diff --git a/src/animation/easing.ts b/src/animation/easing.ts index b6e75f7ee..3003fe58b 100644 --- a/src/animation/easing.ts +++ b/src/animation/easing.ts @@ -6,7 +6,7 @@ type easingFunc = (percent: number) => number; -export type AnimationEasing = keyof typeof easing | easingFunc | 'spline'; +export type AnimationEasing = keyof typeof easing | easingFunc; const easing = { /** diff --git a/src/graphic/helper/poly.ts b/src/graphic/helper/poly.ts index 1950f77a1..316838d0b 100644 --- a/src/graphic/helper/poly.ts +++ b/src/graphic/helper/poly.ts @@ -1,5 +1,4 @@ -import smoothSpline from './smoothSpline'; import smoothBezier from './smoothBezier'; import { VectorArray } from '../../core/vector'; import PathProxy from '../../core/PathProxy'; @@ -8,7 +7,7 @@ export function buildPath( ctx: CanvasRenderingContext2D | PathProxy, shape: { points: VectorArray[], - smooth?: number | 'spline' + smooth?: number smoothConstraint?: VectorArray[] }, closePath: boolean @@ -16,7 +15,7 @@ export function buildPath( const smooth = shape.smooth; let points = shape.points; if (points && points.length >= 2) { - if (smooth && smooth !== 'spline') { + if (smooth) { const controlPoints = smoothBezier( points, smooth, closePath, shape.smoothConstraint ); @@ -33,10 +32,6 @@ export function buildPath( } } else { - if (smooth === 'spline') { - points = smoothSpline(points, closePath); - } - ctx.moveTo(points[0][0], points[0][1]); for (let i = 1, l = points.length; i < l; i++) { ctx.lineTo(points[i][0], points[i][1]); diff --git a/src/graphic/shape/Polygon.ts b/src/graphic/shape/Polygon.ts index cf24961d1..b7c9f75ca 100644 --- a/src/graphic/shape/Polygon.ts +++ b/src/graphic/shape/Polygon.ts @@ -9,7 +9,7 @@ import { VectorArray } from '../../core/vector'; export class PolygonShape { points: VectorArray[] = null - smooth?: number | 'spline' = 0 + smooth?: number = 0 smoothConstraint?: VectorArray[] = null } diff --git a/src/graphic/shape/Polyline.ts b/src/graphic/shape/Polyline.ts index f93caa98e..2221eb260 100644 --- a/src/graphic/shape/Polyline.ts +++ b/src/graphic/shape/Polyline.ts @@ -10,7 +10,7 @@ export class PolylineShape { points: VectorArray[] = null // Percent of displayed polyline. For animating purpose percent?: number = 1 - smooth?: number | 'spline' = 0 + smooth?: number = 0 smoothConstraint?: VectorArray[] = null } diff --git a/test/orbit.html b/test/orbit.html deleted file mode 100644 index c2574ab72..000000000 --- a/test/orbit.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - Animation - - - - -
- - - \ No newline at end of file diff --git a/test/orbit2.html b/test/orbit2.html deleted file mode 100644 index 0345a60fc..000000000 --- a/test/orbit2.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Animation - - - -
- - - \ No newline at end of file diff --git a/test/poly.html b/test/poly.html index 1b0d248f8..909109e47 100644 --- a/test/poly.html +++ b/test/poly.html @@ -25,19 +25,6 @@ points.push([Math.random() * 300 + 100, Math.random() * 300 + 100]); } - var splineSmoothedPolyline = new zrender.Polyline({ - x: 100, - y: 100, - style : { - stroke: 'rgba(220, 20, 60, 0.8)', - lineWidth: 2 - }, - shape: { - smooth: 'spline', - points: points - } - }); - var polygon = new zrender.Polygon({ x: 100, y: 100, @@ -61,7 +48,6 @@ }); zr.add(polygon); zr.add(polygonOutline); - zr.add(splineSmoothedPolyline); function update() { polygon.setShape({ @@ -73,10 +59,6 @@ polygonOutline.setStyle({ strokePercent: config.percent }); - - splineSmoothedPolyline.setStyle({ - strokePercent: config.percent - }); } const config = { From c1de8815a16b4fadb7bb988f0060c491dc44ffff Mon Sep 17 00:00:00 2001 From: pissang Date: Sun, 21 Nov 2021 16:55:36 +0800 Subject: [PATCH 067/148] feat(animation): support different easing in each keyframe --- src/animation/Animator.ts | 29 ++++++++++------ src/animation/easing.ts | 12 +++---- test/animation-keyframe-easing.html | 50 +++++++++++++++++++++++++++ test/animation2.html | 53 ----------------------------- 4 files changed, 75 insertions(+), 69 deletions(-) create mode 100644 test/animation-keyframe-easing.html delete mode 100644 test/animation2.html diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 62c17a23d..32484c8a0 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -6,7 +6,7 @@ import Clip from './Clip'; import * as color from '../tool/color'; import {isArrayLike, keys, logError, map} from '../core/util'; import {ArrayLike, Dictionary} from '../core/types'; -import { AnimationEasing } from './easing'; +import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; type NumberArray = ArrayLike @@ -179,6 +179,7 @@ type Keyframe = { value: unknown percent: number + easing?: (percent: number) => number additiveValue?: unknown } @@ -238,7 +239,7 @@ class Track { return this._additiveTrack; } - addKeyframe(time: number, value: unknown) { + addKeyframe(time: number, value: unknown, easing?: Keyframe['easing']) { if (time >= this.maxTime) { this.maxTime = time; } @@ -317,7 +318,8 @@ class Track { const kf = { time, value, - percent: 0 + percent: 0, + easing }; // Not check if value equal here. this.keyframes.push(kf); @@ -449,7 +451,12 @@ class Track { if (range === 0) { return; } - const w = (percent - frame.percent) / range; + let w = (percent - frame.percent) / range; + // Apply different easing of each keyframe. + // Use easing specified in target frame. + if (nextFrame.easing) { + w = nextFrame.easing(w); + } // If value is arr let targetArr = isAdditive ? this._additiveValue @@ -615,14 +622,17 @@ export default class Animator { * @param time 关键帧时间,单位是ms * @param props 关键帧的属性值,key-value表示 */ - when(time: number, props: Dictionary) { - return this.whenWithKeys(time, props, keys(props) as string[]); + when(time: number, props: Dictionary, easing?: AnimationEasing) { + return this.whenWithKeys(time, props, keys(props) as string[], easing); } // Fast path for add keyframes of aniamteTo - whenWithKeys(time: number, props: Dictionary, propNames: string[]) { + whenWithKeys(time: number, props: Dictionary, propNames: string[], easing?: AnimationEasing) { const tracks = this._tracks; + if (typeof easing === 'string') { + easing = easingFuncs[easing]; + } for (let i = 0; i < propNames.length; i++) { const propName = propNames[i]; @@ -654,13 +664,12 @@ export default class Animator { // Else // Initialize value from current prop value if (time !== 0) { - track.addKeyframe(0, cloneValue(initialValue)); + track.addKeyframe(0, cloneValue(initialValue), easing); } this._trackKeys.push(propName); } - // PENDING - track.addKeyframe(time, cloneValue(props[propName])); + track.addKeyframe(time, cloneValue(props[propName]), easing); } this._maxTime = Math.max(this._maxTime, time); return this; diff --git a/src/animation/easing.ts b/src/animation/easing.ts index 3003fe58b..e7602c0f5 100644 --- a/src/animation/easing.ts +++ b/src/animation/easing.ts @@ -6,9 +6,9 @@ type easingFunc = (percent: number) => number; -export type AnimationEasing = keyof typeof easing | easingFunc; +export type AnimationEasing = keyof typeof easingFuncs | easingFunc; -const easing = { +const easingFuncs = { /** * @param {number} k * @return {number} @@ -315,7 +315,7 @@ const easing = { * @return {number} */ bounceIn(k: number) { - return 1 - easing.bounceOut(1 - k); + return 1 - easingFuncs.bounceOut(1 - k); }, /** * @param {number} k @@ -341,10 +341,10 @@ const easing = { */ bounceInOut(k: number) { if (k < 0.5) { - return easing.bounceIn(k * 2) * 0.5; + return easingFuncs.bounceIn(k * 2) * 0.5; } - return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5; + return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5; } }; -export default easing; \ No newline at end of file +export default easingFuncs; \ No newline at end of file diff --git a/test/animation-keyframe-easing.html b/test/animation-keyframe-easing.html new file mode 100644 index 000000000..d692b966b --- /dev/null +++ b/test/animation-keyframe-easing.html @@ -0,0 +1,50 @@ + + + + + Animation Keyframe Easing + + + + + +
+ + + \ No newline at end of file diff --git a/test/animation2.html b/test/animation2.html deleted file mode 100644 index a30ce2ac3..000000000 --- a/test/animation2.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Animation - - - - - - -
- - \ No newline at end of file From 731cd9da43a00feba244265e6a49671edc1b0885 Mon Sep 17 00:00:00 2001 From: pissang Date: Mon, 22 Nov 2021 13:49:39 +0800 Subject: [PATCH 068/148] feat: support per kf easing in css animation --- src/animation/Animator.ts | 24 +++++++++++++----------- src/svg/cssAnimation.ts | 34 +++++++++++++++++++++++++++++++--- test/svg-ssr.html | 27 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 32484c8a0..0a49dd60a 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -4,7 +4,7 @@ import Clip from './Clip'; import * as color from '../tool/color'; -import {isArrayLike, keys, logError, map} from '../core/util'; +import {isArrayLike, isFunction, isString, keys, logError, map} from '../core/util'; import {ArrayLike, Dictionary} from '../core/types'; import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; @@ -179,7 +179,8 @@ type Keyframe = { value: unknown percent: number - easing?: (percent: number) => number + easing?: AnimationEasing // Raw easing + easingFunc?: (percent: number) => number additiveValue?: unknown } @@ -239,7 +240,7 @@ class Track { return this._additiveTrack; } - addKeyframe(time: number, value: unknown, easing?: Keyframe['easing']) { + addKeyframe(time: number, value: unknown, easing?: AnimationEasing) { if (time >= this.maxTime) { this.maxTime = time; } @@ -315,12 +316,16 @@ class Track { } } - const kf = { + const kf: Keyframe = { time, value, - percent: 0, - easing + percent: 0 }; + if (easing) { + // Save the raw easing name to be used in css animation output + kf.easing = easing; + kf.easingFunc = isFunction(easing) ? easing : easingFuncs[easing]; + } // Not check if value equal here. this.keyframes.push(kf); return kf; @@ -454,8 +459,8 @@ class Track { let w = (percent - frame.percent) / range; // Apply different easing of each keyframe. // Use easing specified in target frame. - if (nextFrame.easing) { - w = nextFrame.easing(w); + if (nextFrame.easingFunc) { + w = nextFrame.easingFunc(w); } // If value is arr @@ -630,9 +635,6 @@ export default class Animator { // Fast path for add keyframes of aniamteTo whenWithKeys(time: number, props: Dictionary, propNames: string[], easing?: AnimationEasing) { const tracks = this._tracks; - if (typeof easing === 'string') { - easing = easingFuncs[easing]; - } for (let i = 0; i < propNames.length; i++) { const propName = propNames[i]; diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 1f852e012..a0f4a194c 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -8,6 +8,7 @@ import { getPathPrecision, getSRTTransformString } from './helper'; import { each, extend, filter, isString, keys } from '../core/util'; import Animator from '../animation/Animator'; import { CompoundPath } from '../export'; +import { AnimationEasing } from '../animation/easing'; export const EASING_MAP: Record = { // From https://easings.net/ @@ -141,6 +142,10 @@ function createCompoundPathCSSAnimation( return cssAnimationCfg.replace(cssAnimationName, animationName); } +function getEasingFunc(easing: AnimationEasing) { + return (isString(easing) && EASING_MAP[easing]) ? `cubic-bezier(${EASING_MAP[easing]})` : ''; +} + export function createCSSAnimation( el: Displayable, attrs: SVGVNodeAttrs, @@ -169,11 +174,11 @@ export function createCSSAnimation( for (let i = 0; i < len; i++) { const animator = animators[i]; const cfgArr: (string | number)[] = [animator.getMaxTime() / 1000 + 's']; - const easing = animator.getClip().easing; + const easing = getEasingFunc(animator.getClip().easing); const delay = animator.getDelay(); - if (isString(easing) && EASING_MAP[easing]) { - cfgArr.push(`cubic-bezier(${EASING_MAP[easing]})`); + if (easing) { + cfgArr.push(easing); } else { cfgArr.push('linear'); @@ -199,6 +204,8 @@ export function createCSSAnimation( const finalKfs: Record = {}; + const animationTimingFunctionAttrName = 'animation-timing-function'; + function saveAnimatorTrackToCssKfs( animator: Animator, cssKfs: Record, @@ -216,9 +223,16 @@ export function createCSSAnimation( for (let i = 0; i < kfs.length; i++) { const kf = kfs[i]; const percent = Math.round(kf.time / maxTime * 100) + '%'; + const kfEasing = getEasingFunc(kf.easing); + cssKfs[percent] = cssKfs[percent] || {}; cssKfs[percent][attrName] = track.isValueColor ? col2str(kf.value as any) : kf.value; + + if (kfEasing) { + // TODO. If different property have different easings. + cssKfs[percent][animationTimingFunctionAttrName] = kfEasing; + } } } } @@ -244,11 +258,17 @@ export function createCSSAnimation( copyTransform(transform, el); extend(transform, transformKfs[percent]); const str = getSRTTransformString(transform); + const timingFunction = transformKfs[percent][animationTimingFunctionAttrName]; finalKfs[percent] = str ? { transform: str } : {}; // TODO set transform origin in element? setTransformOrigin(finalKfs[percent], transform); + + // Save timing function + if (timingFunction) { + finalKfs[percent][animationTimingFunctionAttrName] = timingFunction; + } }; @@ -257,7 +277,10 @@ export function createCSSAnimation( // eslint-disable-next-line for (let percent in shapeKfs) { finalKfs[percent] = finalKfs[percent] || {}; + const isFirst = !path; + const timingFunction = shapeKfs[percent][animationTimingFunctionAttrName]; + if (isFirst) { path = new PathProxy(); } @@ -270,6 +293,11 @@ export function createCSSAnimation( canAnimateShape = false; break; } + + // Save timing function + if (timingFunction) { + finalKfs[percent][animationTimingFunctionAttrName] = timingFunction; + } }; if (!canAnimateShape) { // eslint-disable-next-line diff --git a/test/svg-ssr.html b/test/svg-ssr.html index 655a561bb..625e534a7 100644 --- a/test/svg-ssr.html +++ b/test/svg-ssr.html @@ -261,6 +261,33 @@ zr.add(el); } + // Perframe easing animation + for (let i = 0; i < 10; i++) { + const el = new zrender.Circle({ + x: i * cellSize + cellSize / 2, + y: cellSize * 11 + cellSize / 2, + shape: { + cx: 0, + cy: 0, + r: elSize / 2 + }, + style: { + fill: 'red' + } + }); + el.animate(null, true) + .when(1000, { + scaleX: 0.2, + scaleY: 0.2 + }, 'sinusoidalInOut') + .when(2000, { + scaleX: 1, + scaleY: 1 + }, 'sinusoidalInOut') + .start() + zr.add(el); + } + console.time('render'); const svg = zr.painter.renderToString(); From 7d3fe39826913b2df8b9fc12ff19ab8e016d5de2 Mon Sep 17 00:00:00 2001 From: pissang Date: Mon, 22 Nov 2021 18:03:39 +0800 Subject: [PATCH 069/148] test: optimize ssr case --- test/svg-ssr.html | 40 ++++++---------------------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/test/svg-ssr.html b/test/svg-ssr.html index 625e534a7..cac180bb9 100644 --- a/test/svg-ssr.html +++ b/test/svg-ssr.html @@ -168,7 +168,7 @@ zr.add(group); } - // Transform animation with multiple keyframe + // Transform animation with multiple keyframe and easing for (let i = 0; i < 10; i++) { const el = new zrender.Circle({ x: i * cellSize + cellSize / 2, @@ -184,14 +184,14 @@ }); el.animate(null, true) .when(1000, { - scaleX: 1.2, - scaleY: 1.2 - }) + scaleX: 0.2, + scaleY: 0.2 + }, 'sinusoidalInOut') .when(2000, { scaleX: 1, scaleY: 1 - }) - .start(); + }, 'sinusoidalInOut') + .start() zr.add(el); } @@ -261,34 +261,6 @@ zr.add(el); } - // Perframe easing animation - for (let i = 0; i < 10; i++) { - const el = new zrender.Circle({ - x: i * cellSize + cellSize / 2, - y: cellSize * 11 + cellSize / 2, - shape: { - cx: 0, - cy: 0, - r: elSize / 2 - }, - style: { - fill: 'red' - } - }); - el.animate(null, true) - .when(1000, { - scaleX: 0.2, - scaleY: 0.2 - }, 'sinusoidalInOut') - .when(2000, { - scaleX: 1, - scaleY: 1 - }, 'sinusoidalInOut') - .start() - zr.add(el); - } - - console.time('render'); const svg = zr.painter.renderToString(); document.getElementById('main').innerHTML = svg; From c3ba264a74c2451baf332a3234545e7b9ccca886 Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 23 Nov 2021 09:24:19 +0800 Subject: [PATCH 070/148] tweak --- src/animation/Animator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 0a49dd60a..4a4fef154 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -920,7 +920,7 @@ export default class Animator { for (let i = 0; i < propNames.length; i++) { const track = tracks[propNames[i]]; - if (track) { + if (track && !track.isFinished()) { if (forwardToLast) { track.step(this._target, 1); } From 9fb1577705ac21e15deb4fc8562a5ca907398cb4 Mon Sep 17 00:00:00 2001 From: "Zhongxiang.Wang" Date: Fri, 26 Nov 2021 10:58:19 +0800 Subject: [PATCH 071/148] fix(svg): update `_svgDom` when vnode changed. --- src/svg/Painter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index ac29a2669..445b0b06a 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -108,6 +108,7 @@ class SVGPainter implements PainterBase { vnode.attrs.style = 'user-select:none;position:absolute;left:0;top:0;'; patch(this._oldVNode || this._svgDom, vnode); this._oldVNode = vnode; + this._svgDom = vnode.elm as SVGElement; } } @@ -372,4 +373,4 @@ function createMethodNotSupport(method: string): any { } -export default SVGPainter; \ No newline at end of file +export default SVGPainter; From de3282973da85605829d180da53efb4b7119921c Mon Sep 17 00:00:00 2001 From: pissang Date: Mon, 29 Nov 2021 11:06:07 +0800 Subject: [PATCH 072/148] refact: simplify codes, remove unused animation.onframe --- src/animation/Animation.ts | 56 +++++++++++++++++--------------------- src/animation/Animator.ts | 34 ++++++++++++----------- src/animation/Clip.ts | 46 ++++++++++++++----------------- src/svg/cssAnimation.ts | 14 ++-------- src/zrender.ts | 6 ++-- 5 files changed, 68 insertions(+), 88 deletions(-) diff --git a/src/animation/Animation.ts b/src/animation/Animation.ts index 453814658..239c9948e 100644 --- a/src/animation/Animation.ts +++ b/src/animation/Animation.ts @@ -11,15 +11,16 @@ import requestAnimationFrame from './requestAnimationFrame'; import Animator from './Animator'; import Clip from './Clip'; +export function getTime() { + return new Date().getTime(); +} interface Stage { update?: () => void } -type OnframeCallback = (deltaTime: number) => void interface AnimationOption { stage?: Stage - onframe?: OnframeCallback } /** * @example @@ -44,17 +45,15 @@ export default class Animation extends Eventful { stage: Stage - onframe: OnframeCallback - // Use linked list to store clip - private _clipsHead: Clip - private _clipsTail: Clip + private _head: Clip + private _tail: Clip - private _running: boolean = false + private _running = false - private _time: number = 0 - private _pausedTime: number = 0 - private _pauseStart: number = 0 + private _time = 0 + private _pausedTime = 0 + private _pauseStart = 0 private _paused = false; @@ -64,8 +63,6 @@ export default class Animation extends Eventful { opts = opts || {}; this.stage = opts.stage || {}; - - this.onframe = opts.onframe || function () {}; } /** @@ -77,14 +74,14 @@ export default class Animation extends Eventful { this.removeClip(clip); } - if (!this._clipsHead) { - this._clipsHead = this._clipsTail = clip; + if (!this._head) { + this._head = this._tail = clip; } else { - this._clipsTail.next = clip; - clip.prev = this._clipsTail; + this._tail.next = clip; + clip.prev = this._tail; clip.next = null; - this._clipsTail = clip; + this._tail = clip; } clip.animation = this; } @@ -112,14 +109,14 @@ export default class Animation extends Eventful { } else { // Is head - this._clipsHead = next; + this._head = next; } if (next) { next.prev = prev; } else { // Is tail - this._clipsTail = prev; + this._tail = prev; } clip.next = clip.prev = clip.animation = null; } @@ -136,9 +133,9 @@ export default class Animation extends Eventful { } update(notTriggerFrameAndStageUpdate?: boolean) { - const time = new Date().getTime() - this._pausedTime; + const time = getTime() - this._pausedTime; const delta = time - this._time; - let clip = this._clipsHead; + let clip = this._head; while (clip) { // Save the nextClip before step. @@ -146,7 +143,7 @@ export default class Animation extends Eventful { const nextClip = clip.next; let finished = clip.step(time, delta); if (finished) { - clip.ondestroy && clip.ondestroy(); + clip.ondestroy(); this.removeClip(clip); clip = nextClip; } @@ -158,7 +155,6 @@ export default class Animation extends Eventful { this._time = time; if (!notTriggerFrameAndStageUpdate) { - this.onframe(delta); // 'frame' should be triggered before stage, because upper application // depends on the sequence (e.g., echarts-stream and finish @@ -176,9 +172,7 @@ export default class Animation extends Eventful { function step() { if (self._running) { - requestAnimationFrame(step); - !self._paused && self.update(); } } @@ -194,7 +188,7 @@ export default class Animation extends Eventful { return; } - this._time = new Date().getTime(); + this._time = getTime(); this._pausedTime = 0; this._startLoop(); @@ -212,7 +206,7 @@ export default class Animation extends Eventful { */ pause() { if (!this._paused) { - this._pauseStart = new Date().getTime(); + this._pauseStart = getTime(); this._paused = true; } } @@ -222,7 +216,7 @@ export default class Animation extends Eventful { */ resume() { if (this._paused) { - this._pausedTime += (new Date().getTime()) - this._pauseStart; + this._pausedTime += getTime() - this._pauseStart; this._paused = false; } } @@ -231,7 +225,7 @@ export default class Animation extends Eventful { * Clear animation. */ clear() { - let clip = this._clipsHead; + let clip = this._head; while (clip) { let nextClip = clip.next; @@ -239,14 +233,14 @@ export default class Animation extends Eventful { clip = nextClip; } - this._clipsHead = this._clipsTail = null; + this._head = this._tail = null; } /** * Whether animation finished. */ isFinished() { - return this._clipsHead == null; + return this._head == null; } /** diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 4a4fef154..c9fd3015e 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -194,7 +194,7 @@ class Track { // Larger than 0 if value is array arrDim: number = 0 - isValueColor: boolean + isColor: boolean interpolable: boolean = true @@ -249,7 +249,6 @@ class Track { } let keyframes = this.keyframes; - let len = keyframes.length; if (this.interpolable) { @@ -293,7 +292,7 @@ class Track { const colorArray = color.parse(value); if (colorArray) { value = colorArray; - this.isValueColor = true; + this.isColor = true; } else { this.interpolable = false; @@ -306,7 +305,7 @@ class Track { if (this._isAllValueEqual && len > 0) { let lastFrame = keyframes[len - 1]; - if (this.isValueColor && !is1DArraySame(lastFrame.value as number[], value as number[])) { + if (this.isColor && !is1DArraySame(lastFrame.value as number[], value as number[])) { this._isAllValueEqual = false; } else if (lastFrame.value !== value) { @@ -327,7 +326,7 @@ class Track { kf.easingFunc = isFunction(easing) ? easing : easingFuncs[easing]; } // Not check if value equal here. - this.keyframes.push(kf); + keyframes.push(kf); return kf; } @@ -359,7 +358,7 @@ class Track { && this.needsAnimate() && additiveTrack.needsAnimate() && arrDim === additiveTrack.arrDim - && this.isValueColor === additiveTrack.isValueColor + && this.isColor === additiveTrack.isColor && !additiveTrack._finished ) { this._additiveTrack = additiveTrack; @@ -368,7 +367,7 @@ class Track { // Calculate difference for (let i = 0; i < kfsLen; i++) { if (arrDim === 0) { - if (this.isValueColor) { + if (this.isColor) { kfs[i].additiveValue = add1DArray([], kfs[i].value as NumberArray, startValue as NumberArray, -1); } @@ -409,14 +408,16 @@ class Track { const valueKey = isAdditive ? 'additiveValue' : 'value'; const keyframes = this.keyframes; - const kfsNum = this.keyframes.length; + const kfsNum = keyframes.length; const propName = this.propName; const arrDim = this.arrDim; - const isValueColor = this.isValueColor; + const isValueColor = this.isColor; // Find the range keyframes // kf1-----kf2---------current--------kf3 // find kf2 and kf3 and do interpolation let frameIdx; + const lastFrame = this._lastFrame; + const min = Math.min; // In the easing function like elasticOut, percent may less than 0 if (percent < 0) { frameIdx = 0; @@ -424,23 +425,24 @@ class Track { else if (percent < this._lastFramePercent) { // Start from next key // PENDING start from lastFrame ? - const start = Math.min(this._lastFrame + 1, kfsNum - 1); + const start = min(lastFrame + 1, kfsNum - 1); for (frameIdx = start; frameIdx >= 0; frameIdx--) { if (keyframes[frameIdx].percent <= percent) { break; } } // PENDING really need to do this ? - frameIdx = Math.min(frameIdx, kfsNum - 2); + frameIdx = min(frameIdx, kfsNum - 2); } else { - for (frameIdx = this._lastFrame; frameIdx < kfsNum; frameIdx++) { + for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) { if (keyframes[frameIdx].percent > percent) { break; } } - frameIdx = Math.min(frameIdx - 1, kfsNum - 2); + frameIdx = min(frameIdx - 1, kfsNum - 2); } + let nextFrame = keyframes[frameIdx + 1]; let frame = keyframes[frameIdx]; @@ -526,7 +528,7 @@ class Track { const additiveValue = this._additiveValue; if (arrDim === 0) { - if (this.isValueColor) { + if (this.isColor) { // TODO reduce unnecessary parse color.parse(target[propName], tmpRgba); add1DArray(tmpRgba, tmpRgba, additiveValue as NumberArray, 1); @@ -648,7 +650,7 @@ export default class Animator { const lastFinalKf = additiveTrack.keyframes[additiveTrack.keyframes.length - 1]; // Use the last state of additived animator. initialValue = lastFinalKf && lastFinalKf.value; - if (additiveTrack.isValueColor && initialValue) { + if (additiveTrack.isColor && initialValue) { // Convert to rgba string initialValue = rgba2String(initialValue as number[]); } @@ -982,7 +984,7 @@ export default class Animator { if (kf) { // TODO CLONE? let val: unknown = cloneValue(kf.value as any); - if (track.isValueColor) { + if (track.isColor) { val = rgba2String(val as number[]); } diff --git a/src/animation/Clip.ts b/src/animation/Clip.ts index caddaf561..ed8c3040b 100644 --- a/src/animation/Clip.ts +++ b/src/animation/Clip.ts @@ -15,6 +15,7 @@ import easingFuncs, {AnimationEasing} from './easing'; import type Animation from './Animation'; +import { isFunction, isString, noop } from '../core/util'; type OnframeCallback = (percent: number) => void; type ondestroyCallback = () => void @@ -37,13 +38,10 @@ export interface ClipProps { export default class Clip { - // 生命周期 private _life: number - // 延时 private _delay: number - private _initialized: boolean = false - // 开始时间 + private _inited: boolean = false private _startTime = 0 // 开始时间单位毫秒 private _pausedTime = 0 @@ -66,29 +64,25 @@ export default class Clip { constructor(opts: ClipProps) { this._life = opts.life || 1000; - this._delay = opts.delay || 0; - // this._startTime = new Date().getTime() + this._delay; - - // 是否循环 - this.loop = opts.loop == null ? false : opts.loop; + this.loop = opts.loop || false; this.gap = opts.gap || 0; this.easing = opts.easing || 'linear'; - this.onframe = opts.onframe; - this.ondestroy = opts.ondestroy; - this.onrestart = opts.onrestart; + this.onframe = opts.onframe || noop; + this.ondestroy = opts.ondestroy || noop; + this.onrestart = opts.onrestart || noop; } step(globalTime: number, deltaTime: number): boolean { // Set startTime on first step, or _startTime may has milleseconds different between clips // PENDING - if (!this._initialized) { + if (!this._inited) { this._startTime = globalTime + this._delay; - this._initialized = true; + this._inited = true; } if (this._paused) { @@ -96,7 +90,9 @@ export default class Clip { return; } - let percent = (globalTime - this._startTime - this._pausedTime) / this._life; + const life = this._life; + let elapsedTime = globalTime - this._startTime - this._pausedTime; + let percent = elapsedTime / life; // PENDING: Not begin yet. Still run the loop. // In the case callback needs to be invoked. @@ -109,19 +105,23 @@ export default class Clip { percent = Math.min(percent, 1); const easing = this.easing; - const easingFunc = typeof easing === 'string' + const easingFunc = isString(easing) ? easingFuncs[easing as keyof typeof easingFuncs] : easing; - const schedule = typeof easingFunc === 'function' + const schedule = isFunction(easingFunc) ? easingFunc(percent) : percent; - this.onframe && this.onframe(schedule); + this.onframe(schedule); // 结束 if (percent === 1) { if (this.loop) { - this._restart(globalTime); - this.onrestart && this.onrestart(); + // Restart + const remainder = elapsedTime % life; + this._startTime = globalTime - remainder + this.gap; + this._pausedTime = 0; + + this.onrestart(); } else { return true; @@ -131,12 +131,6 @@ export default class Clip { return false; } - private _restart(globalTime: number) { - const remainder = (globalTime - this._startTime - this._pausedTime) % this._life; - this._startTime = globalTime - remainder + this.gap; - this._pausedTime = 0; - } - pause() { this._paused = true; } diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index a0f4a194c..f75f64959 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -1,4 +1,4 @@ -import Transformable, { copyTransform, TRANSFORMABLE_PROPS } from '../core/Transformable'; +import Transformable, { copyTransform } from '../core/Transformable'; import Displayable from '../graphic/Displayable'; import { SVGVNodeAttrs, BrushScope, createBrushScope} from './core'; import Path from '../graphic/Path'; @@ -38,16 +38,6 @@ export const EASING_MAP: Record = { const transformOriginKey = 'transform-origin'; -function sameTransform(a: any, b: any) { - for (let i = 0; i < TRANSFORMABLE_PROPS.length; i++) { - const prop = TRANSFORMABLE_PROPS[i]; - if (a[prop] !== b[prop]) { - return false; - } - } - return true; -} - function buildPathString(el: Path, kfShape: Path['shape'], path: PathProxy) { const shape = extend({}, el.shape); extend(shape, kfShape); @@ -227,7 +217,7 @@ export function createCSSAnimation( cssKfs[percent] = cssKfs[percent] || {}; cssKfs[percent][attrName] = - track.isValueColor ? col2str(kf.value as any) : kf.value; + track.isColor ? col2str(kf.value as any) : kf.value; if (kfEasing) { // TODO. If different property have different easings. diff --git a/src/zrender.ts b/src/zrender.ts index c32cecbee..23aa6b130 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -13,7 +13,7 @@ import * as zrUtil from './core/util'; import Handler from './Handler'; import Storage from './Storage'; import {PainterBase} from './PainterBase'; -import Animation from './animation/Animation'; +import Animation, {getTime} from './animation/Animation'; import HandlerProxy from './dom/HandlerProxy'; import Element, { ElementEventCallback } from './Element'; import { Dictionary, ElementEventName, RenderedEvent, WithThisType } from './core/types'; @@ -239,7 +239,7 @@ class ZRender { private _flush(fromInside?: boolean) { let triggerRendered; - const start = new Date().getTime(); + const start = getTime(); if (this._needsRefresh) { triggerRendered = true; this.refreshImmediately(fromInside); @@ -249,7 +249,7 @@ class ZRender { triggerRendered = true; this.refreshHoverImmediately(); } - const end = new Date().getTime(); + const end = getTime(); if (triggerRendered) { this._stillFrameAccum = 0; From 9997ff9ace3ff5af8044587d4be0e7ab1db1e056 Mon Sep 17 00:00:00 2001 From: pissang Date: Mon, 29 Nov 2021 11:07:17 +0800 Subject: [PATCH 073/148] fix: not remove original attributes when css animation is used. To compatible with the environment that not support css animation --- src/svg/cssAnimation.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index f75f64959..7e207746c 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -336,11 +336,11 @@ export function createCSSAnimation( ).length) { const animationName = addAnimation(finalKfs, scope); // eslint-disable-next-line - for (const attrName in finalKfs[percents[0]]) { - // Remove the attrs in the element because it will be set by animation. - // Reduce the size. - attrs[attrName] = false; - } + // for (const attrName in finalKfs[percents[0]]) { + // // Remove the attrs in the element because it will be set by animation. + // // Reduce the size. + // attrs[attrName] = false; + // } // animationName {duration easing delay loop} fillMode return `${animationName} ${groupAnimator[0]} both`; } From 369b25a58c32d1161134bb990ccef65093feeac1 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 30 Nov 2021 18:17:57 +0800 Subject: [PATCH 074/148] fix(svg): pre-create svg vnode when initializing. --- src/svg/Painter.ts | 37 ++++++++++++------------------------- src/svg/core.ts | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 445b0b06a..5e6786c88 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -9,16 +9,15 @@ import Displayable from '../graphic/Displayable'; import Storage from '../Storage'; import { PainterBase } from '../PainterBase'; import { + createElement, createVNode, vNodeToString, SVGVNodeAttrs, SVGVNode, - createElement, - SVGNS, - XLINKNS, getCssString, BrushScope, - createBrushScope + createBrushScope, + createSVGVNode } from './core'; import { normalizeColor } from './helper'; import { defaults, extend, keys, logError, map } from '../core/util'; @@ -66,13 +65,14 @@ class SVGPainter implements PainterBase { // A unique id for generating svg ids. this._id = 'zr' + svgId++; + this._oldVNode = createSVGVNode(opts.width, opts.height); if (root && !opts.ssr) { const viewport = this._viewport = document.createElement('div'); - viewport.style.cssText = 'overflow:hidden;position:relative'; - const svgDom = this._svgDom = createElement('svg'); - root.appendChild(viewport); + viewport.style.cssText = 'position:relative;overflow:hidden'; + const svgDom = this._svgDom = this._oldVNode.elm = createElement('svg'); viewport.appendChild(svgDom); + root.appendChild(viewport); } this.resize(opts.width, opts.height); @@ -105,10 +105,9 @@ class SVGPainter implements PainterBase { willUpdate: true }); // Disable user selection. - vnode.attrs.style = 'user-select:none;position:absolute;left:0;top:0;'; - patch(this._oldVNode || this._svgDom, vnode); + vnode.attrs.style = 'position:absolute;left:0;top:0;user-select:none'; + patch(this._oldVNode, vnode); this._oldVNode = vnode; - this._svgDom = vnode.elm as SVGElement; } } @@ -126,8 +125,8 @@ class SVGPainter implements PainterBase { const list = this.storage.getDisplayList(true); const bgColor = this._backgroundColor; - const width = this._width + ''; - const height = this._height + ''; + const width = this._width; + const height = this._height; const scope = createBrushScope(this._id); scope.animation = opts.animation; @@ -176,19 +175,7 @@ class SVGPainter implements PainterBase { } } - return createVNode( - 'svg', - 'root', - { - 'width': width, - 'height': height, - 'xmlns': SVGNS, - 'xmlns:xlink': XLINKNS, - 'version': '1.1', - 'baseProfile': 'full' - }, - children - ); + return createSVGVNode(width, height, children); } renderToString(opts?: { diff --git a/src/svg/core.ts b/src/svg/core.ts index 194528750..bec673278 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -171,4 +171,20 @@ export function createBrushScope(zrId: string): BrushScope { patternIdx: 0, clipPathIdx: 0 }; +} + +export function createSVGVNode(width?: number | string, height?: number | string, children?: SVGVNode[]) { + return createVNode( + 'svg', + 'root', + { + 'width': width, + 'height': height, + 'xmlns': SVGNS, + 'xmlns:xlink': XLINKNS, + 'version': '1.1', + 'baseProfile': 'full' + }, + children + ); } \ No newline at end of file From 511f9ef1b4d3e29aaf949a7bb60cc19dad34aed5 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 30 Nov 2021 18:27:48 +0800 Subject: [PATCH 075/148] chore(svg): remove some magic strings. --- src/svg/core.ts | 1 + src/svg/patch.ts | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/svg/core.ts b/src/svg/core.ts index bec673278..511b65a13 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -6,6 +6,7 @@ export type CSSAnimationVNode = Record> export const SVGNS = 'http://www.w3.org/2000/svg'; export const XLINKNS = 'http://www.w3.org/1999/xlink'; export const XMLNS = 'http://www.w3.org/2000/xmlns/'; +export const XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace'; export function createElement(name: string) { return document.createElementNS(SVGNS, name); diff --git a/src/svg/patch.ts b/src/svg/patch.ts index 34f5d92c5..c76fcb432 100644 --- a/src/svg/patch.ts +++ b/src/svg/patch.ts @@ -9,11 +9,9 @@ */ import { isArray, isObject } from '../core/util'; -import { createElement, createVNode, SVGVNode } from './core'; +import { createElement, createVNode, SVGVNode, XMLNS, XML_NAMESPACE, XLINKNS } from './core'; import * as api from './domapi'; -const xlinkNS = 'http://www.w3.org/1999/xlink'; -const xmlNS = 'http://www.w3.org/XML/1998/namespace'; const colonChar = 58; const xChar = 120; const emptyNode = createVNode('', '') as SVGVNode; @@ -169,15 +167,15 @@ function updateAttrs(oldVnode: SVGVNode, vnode: SVGVNode): void { } // TODO else if (key === 'xmlns:xlink' || key === 'xmlns') { - elm.setAttributeNS('http://www.w3.org/2000/xmlns/', key, cur as any); + elm.setAttributeNS(XMLNS, key, cur as any); } else if (key.charCodeAt(3) === colonChar) { // Assume xml namespace - elm.setAttributeNS(xmlNS, key, cur as any); + elm.setAttributeNS(XML_NAMESPACE, key, cur as any); } else if (key.charCodeAt(5) === colonChar) { // Assume xlink namespace - elm.setAttributeNS(xlinkNS, key, cur as any); + elm.setAttributeNS(XLINKNS, key, cur as any); } else { elm.setAttribute(key, cur as any); From 33a9cfb7812344217c41cd22f0f8f8c69e094249 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 30 Nov 2021 20:28:50 +0800 Subject: [PATCH 076/148] fix(svg): remove unused `emptyNodeAt` & `isVnode` and unnecessary typescript `as`. --- src/svg/patch.ts | 45 +++++++++++---------------------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/src/svg/patch.ts b/src/svg/patch.ts index c76fcb432..3038dab03 100644 --- a/src/svg/patch.ts +++ b/src/svg/patch.ts @@ -41,7 +41,7 @@ function createKeyToOldIdx( console.error(`Duplicate key ${key}`); } } - map[key as string] = i; + map[key] = i; } } return map; @@ -54,25 +54,8 @@ function sameVnode(vnode1: SVGVNode, vnode2: SVGVNode): boolean { return isSameTag && isSameKey; } -function isVnode(vnode: any): vnode is SVGVNode { - return vnode.tag !== undefined; -} - type KeyToIndexMap = { [key: string]: number }; -function emptyNodeAt(elm: Element): SVGVNode { - const id = elm.id ? '#' + elm.id : ''; - - // elm.className doesn't return a string when elm is an SVG element inside a shadowRoot. - // https://stackoverflow.com/questions/29454340/detecting-classname-of-svganimatedstring - const classes = elm.getAttribute('class'); - - const c = classes ? '.' + classes.split(' ').join('.') : ''; - const vnode = createVNode(api.tagName(elm).toLowerCase() + id + c, '') as SVGVNode; - vnode.elm = elm; - return vnode; -} - function createElm(vnode: SVGVNode, insertedVnodeQueue: VNodeQueue): Node { let i: any; const children = vnode.children; @@ -93,7 +76,7 @@ function createElm(vnode: SVGVNode, insertedVnodeQueue: VNodeQueue): Node { for (i = 0; i < children.length; ++i) { const ch = children[i]; if (ch != null) { - api.appendChild(elm, createElm(ch as SVGVNode, insertedVnodeQueue)); + api.appendChild(elm, createElm(ch, insertedVnodeQueue)); } } } @@ -128,7 +111,7 @@ function removeVnodes(parentElm: Node, vnodes: SVGVNode[], startIdx: number, end const ch = vnodes[startIdx]; if (ch != null) { if (isDef(ch.tag)) { - const parent = api.parentNode(ch.elm) as Node; + const parent = api.parentNode(ch.elm); api.removeChild(parent, ch.elm); } else { @@ -141,9 +124,9 @@ function removeVnodes(parentElm: Node, vnodes: SVGVNode[], startIdx: number, end function updateAttrs(oldVnode: SVGVNode, vnode: SVGVNode): void { let key: string; - const elm: Element = vnode.elm as Element; - let oldAttrs = oldVnode.attrs || {}; - let attrs = vnode.attrs || {}; + const elm = vnode.elm as Element; + const oldAttrs = oldVnode.attrs || {}; + const attrs = vnode.attrs || {}; if (oldAttrs === attrs) { return; @@ -284,8 +267,8 @@ function updateChildren( function patchVnode(oldVnode: SVGVNode, vnode: SVGVNode, insertedVnodeQueue: VNodeQueue) { const elm = (vnode.elm = oldVnode.elm)!; - const oldCh = oldVnode.children as SVGVNode[]; - const ch = vnode.children as SVGVNode[]; + const oldCh = oldVnode.children; + const ch = vnode.children; if (oldVnode === vnode) { return; } @@ -319,21 +302,15 @@ function patchVnode(oldVnode: SVGVNode, vnode: SVGVNode, insertedVnodeQueue: VNo } } -export default function patch(oldVnode: SVGVNode | Element, vnode: SVGVNode): SVGVNode { - let elm: Node; - let parent: Node; +export default function patch(oldVnode: SVGVNode, vnode: SVGVNode): SVGVNode { const insertedVnodeQueue: VNodeQueue = []; - if (!isVnode(oldVnode)) { - oldVnode = emptyNodeAt(oldVnode); - } - if (sameVnode(oldVnode, vnode)) { patchVnode(oldVnode, vnode, insertedVnodeQueue); } else { - elm = oldVnode.elm!; - parent = api.parentNode(elm) as Node; + const elm = oldVnode.elm!; + const parent = api.parentNode(elm); createElm(vnode, insertedVnodeQueue); From d063e33a1613f0d8253f935b50d3b1e4490c00bc Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 30 Nov 2021 20:38:05 +0800 Subject: [PATCH 077/148] fix(svg): remove unused `insertedVnodeQueue`. --- src/svg/patch.ts | 50 +++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/svg/patch.ts b/src/svg/patch.ts index 3038dab03..32fbfb69b 100644 --- a/src/svg/patch.ts +++ b/src/svg/patch.ts @@ -14,19 +14,18 @@ import * as api from './domapi'; const colonChar = 58; const xChar = 120; -const emptyNode = createVNode('', '') as SVGVNode; +const emptyNode = createVNode('', ''); type NonUndefined = T extends undefined ? never : T; function isUndef(s: any): boolean { return s === undefined; } + function isDef
(s: A): s is NonUndefined { return s !== undefined; } -type VNodeQueue = SVGVNode[]; - function createKeyToOldIdx( children: SVGVNode[], beginIdx: number, @@ -56,7 +55,7 @@ function sameVnode(vnode1: SVGVNode, vnode2: SVGVNode): boolean { type KeyToIndexMap = { [key: string]: number }; -function createElm(vnode: SVGVNode, insertedVnodeQueue: VNodeQueue): Node { +function createElm(vnode: SVGVNode): Node { let i: any; const children = vnode.children; const tag = vnode.tag; @@ -76,7 +75,7 @@ function createElm(vnode: SVGVNode, insertedVnodeQueue: VNodeQueue): Node { for (i = 0; i < children.length; ++i) { const ch = children[i]; if (ch != null) { - api.appendChild(elm, createElm(ch, insertedVnodeQueue)); + api.appendChild(elm, createElm(ch)); } } } @@ -95,13 +94,12 @@ function addVnodes( before: Node | null, vnodes: SVGVNode[], startIdx: number, - endIdx: number, - insertedVnodeQueue: VNodeQueue + endIdx: number ) { for (; startIdx <= endIdx; ++startIdx) { const ch = vnodes[startIdx]; if (ch != null) { - api.insertBefore(parentElm, createElm(ch, insertedVnodeQueue), before); + api.insertBefore(parentElm, createElm(ch), before); } } } @@ -178,9 +176,7 @@ function updateAttrs(oldVnode: SVGVNode, vnode: SVGVNode): void { } -function updateChildren( - parentElm: Node, oldCh: SVGVNode[], newCh: SVGVNode[], insertedVnodeQueue: VNodeQueue -) { +function updateChildren(parentElm: Node, oldCh: SVGVNode[], newCh: SVGVNode[]) { let oldStartIdx = 0; let newStartIdx = 0; let oldEndIdx = oldCh.length - 1; @@ -208,25 +204,25 @@ function updateChildren( newEndVnode = newCh[--newEndIdx]; } else if (sameVnode(oldStartVnode, newStartVnode)) { - patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue); + patchVnode(oldStartVnode, newStartVnode); oldStartVnode = oldCh[++oldStartIdx]; newStartVnode = newCh[++newStartIdx]; } else if (sameVnode(oldEndVnode, newEndVnode)) { - patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); + patchVnode(oldEndVnode, newEndVnode); oldEndVnode = oldCh[--oldEndIdx]; newEndVnode = newCh[--newEndIdx]; } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right - patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); + patchVnode(oldStartVnode, newEndVnode); api.insertBefore(parentElm, oldStartVnode.elm!, api.nextSibling(oldEndVnode.elm!)); oldStartVnode = oldCh[++oldStartIdx]; newEndVnode = newCh[--newEndIdx]; } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left - patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue); + patchVnode(oldEndVnode, newStartVnode); api.insertBefore(parentElm, oldEndVnode.elm!, oldStartVnode.elm!); oldEndVnode = oldCh[--oldEndIdx]; newStartVnode = newCh[++newStartIdx]; @@ -235,19 +231,19 @@ function updateChildren( if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); } - idxInOld = oldKeyToIdx[newStartVnode.key as string]; + idxInOld = oldKeyToIdx[newStartVnode.key]; if (isUndef(idxInOld)) { // New element - api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm!); + api.insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm!); } else { elmToMove = oldCh[idxInOld]; if (elmToMove.tag !== newStartVnode.tag) { - api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm!); + api.insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm!); } else { - patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); - oldCh[idxInOld] = undefined as any; + patchVnode(elmToMove, newStartVnode); + oldCh[idxInOld] = undefined; api.insertBefore(parentElm, elmToMove.elm!, oldStartVnode.elm!); } } @@ -257,7 +253,7 @@ function updateChildren( if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) { if (oldStartIdx > oldEndIdx) { before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm; - addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue); + addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx); } else { removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); @@ -265,7 +261,7 @@ function updateChildren( } } -function patchVnode(oldVnode: SVGVNode, vnode: SVGVNode, insertedVnodeQueue: VNodeQueue) { +function patchVnode(oldVnode: SVGVNode, vnode: SVGVNode) { const elm = (vnode.elm = oldVnode.elm)!; const oldCh = oldVnode.children; const ch = vnode.children; @@ -278,14 +274,14 @@ function patchVnode(oldVnode: SVGVNode, vnode: SVGVNode, insertedVnodeQueue: VNo if (isUndef(vnode.text)) { if (isDef(oldCh) && isDef(ch)) { if (oldCh !== ch) { - updateChildren(elm, oldCh, ch, insertedVnodeQueue); + updateChildren(elm, oldCh, ch); } } else if (isDef(ch)) { if (isDef(oldVnode.text)) { api.setTextContent(elm, ''); } - addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); + addVnodes(elm, null, ch, 0, ch.length - 1); } else if (isDef(oldCh)) { removeVnodes(elm, oldCh, 0, oldCh.length - 1); @@ -303,16 +299,14 @@ function patchVnode(oldVnode: SVGVNode, vnode: SVGVNode, insertedVnodeQueue: VNo } export default function patch(oldVnode: SVGVNode, vnode: SVGVNode): SVGVNode { - const insertedVnodeQueue: VNodeQueue = []; - if (sameVnode(oldVnode, vnode)) { - patchVnode(oldVnode, vnode, insertedVnodeQueue); + patchVnode(oldVnode, vnode); } else { const elm = oldVnode.elm!; const parent = api.parentNode(elm); - createElm(vnode, insertedVnodeQueue); + createElm(vnode); if (parent !== null) { api.insertBefore(parent, vnode.elm!, api.nextSibling(elm)); From 1a64a4e5c87d22b24de8858350480d24b6c69e50 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 1 Dec 2021 10:17:34 +0800 Subject: [PATCH 078/148] refact: optimize code of line dash --- build/build.js | 1 + src/canvas/dashStyle.ts | 33 +++++++++++++++ src/canvas/graphic.ts | 75 ++++++++++++--------------------- src/core/PathProxy.ts | 1 - src/graphic/Text.ts | 1 + src/graphic/helper/dashStyle.ts | 14 ------ src/svg/mapStyleToAttrs.ts | 31 ++++++-------- 7 files changed, 74 insertions(+), 82 deletions(-) create mode 100644 src/canvas/dashStyle.ts delete mode 100644 src/graphic/helper/dashStyle.ts diff --git a/build/build.js b/build/build.js index a0b189809..6f80edf22 100644 --- a/build/build.js +++ b/build/build.js @@ -30,6 +30,7 @@ function createInputOption(env, isWatch) { } }), replace({ + preventAssignment: true, 'process.env.NODE_ENV': JSON.stringify(env) }), progress({ diff --git a/src/canvas/dashStyle.ts b/src/canvas/dashStyle.ts new file mode 100644 index 000000000..e57ca85bc --- /dev/null +++ b/src/canvas/dashStyle.ts @@ -0,0 +1,33 @@ +import { isArray, isNumber, map } from '../core/util'; +import Path from '../graphic/Path'; +import TSpan from '../graphic/TSpan'; + +export function normalizeLineDash(lineType: any, lineWidth?: number): number[] | false { + if (!lineType || lineType === 'solid' || !(lineWidth > 0)) { + return null; + } + lineWidth = lineWidth || 1; + return lineType === 'dashed' + ? [4 * lineWidth, 2 * lineWidth] + : lineType === 'dotted' + ? [lineWidth] + : isNumber(lineType) + ? [lineType] : isArray(lineType) ? lineType : null; +} +export function getLineDash(el: Path | TSpan): [number[] | false, number] { + const style = el.style; + + let lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth); + let lineDashOffset = style.lineDashOffset; + + if (lineDash) { + const lineScale = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1; + if (lineScale && lineScale !== 1) { + lineDash = map(lineDash, function (rawVal) { + return rawVal / lineScale; + }); + lineDashOffset /= lineScale; + } + } + return [lineDash, lineDashOffset]; +} \ No newline at end of file diff --git a/src/canvas/graphic.ts b/src/canvas/graphic.ts index 6ed916ce2..a6effc80f 100644 --- a/src/canvas/graphic.ts +++ b/src/canvas/graphic.ts @@ -11,8 +11,8 @@ import Path, { PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; import TSpan, {TSpanStyleProps} from '../graphic/TSpan'; import { MatrixArray } from '../core/matrix'; -import { map, RADIAN_TO_DEGREE } from '../core/util'; -import { normalizeLineDash } from '../graphic/helper/dashStyle'; +import { RADIAN_TO_DEGREE } from '../core/util'; +import { getLineDash } from './dashStyle'; import { REDRAW_BIT, SHAPE_CHANGED_BIT } from '../graphic/constants'; import type IncrementalDisplayable from '../graphic/IncrementalDisplayable'; import { DEFAULT_FONT } from '../core/platform'; @@ -79,7 +79,7 @@ export function createCanvasPattern( ) { const matrix = new DOMMatrix(); matrix.translateSelf((pattern.x || 0), (pattern.y || 0)); - matrix.rotateSelf(0, 0, (pattern.rotation || 0) / Math.PI * 180); + matrix.rotateSelf(0, 0, (pattern.rotation || 0) * RADIAN_TO_DEGREE); matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1)); canvasPattern.setTransform(matrix); } @@ -105,6 +105,7 @@ function brushPath(ctx: CanvasRenderingContext2D, el: Path, style: PathStyleProp } const path = el.path || pathProxyForDraw; + const dirtyFlag = el.__dirty; if (!inBatch) { const fill = style.fill; @@ -123,9 +124,10 @@ function brushPath(ctx: CanvasRenderingContext2D, el: Path, style: PathStyleProp if (hasFillGradient || hasStrokeGradient) { rect = el.getBoundingRect(); } + // Update gradient because bounding rect may changed if (hasFillGradient) { - fillGradient = el.__dirty + fillGradient = dirtyFlag ? getCanvasGradient(ctx, fill as (LinearGradientObject | RadialGradientObject), rect) : el.__canvasFillGradient; // No need to clear cache when fill is not gradient. @@ -133,21 +135,21 @@ function brushPath(ctx: CanvasRenderingContext2D, el: Path, style: PathStyleProp el.__canvasFillGradient = fillGradient; } if (hasStrokeGradient) { - strokeGradient = el.__dirty + strokeGradient = dirtyFlag ? getCanvasGradient(ctx, stroke as (LinearGradientObject | RadialGradientObject), rect) : el.__canvasStrokeGradient; el.__canvasStrokeGradient = strokeGradient; } if (hasFillPattern) { // Pattern might be null if image not ready (even created from dataURI) - fillPattern = (el.__dirty || !el.__canvasFillPattern) + fillPattern = (dirtyFlag || !el.__canvasFillPattern) ? createCanvasPattern(ctx, fill as ImagePatternObject, el) : el.__canvasFillPattern; el.__canvasFillPattern = fillPattern; } if (hasStrokePattern) { // Pattern might be null if image not ready (even created from dataURI) - strokePattern = (el.__dirty || !el.__canvasStrokePattern) + strokePattern = (dirtyFlag || !el.__canvasStrokePattern) ? createCanvasPattern(ctx, stroke as ImagePatternObject, el) : el.__canvasStrokePattern; el.__canvasStrokePattern = fillPattern; @@ -180,34 +182,19 @@ function brushPath(ctx: CanvasRenderingContext2D, el: Path, style: PathStyleProp } } - let lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth); - let lineDashOffset = style.lineDashOffset; - - const ctxLineDash = !!ctx.setLineDash; - // Update path sx, sy const scale = el.getGlobalScale(); path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold); - if (lineDash) { - const lineScale = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1; - if (lineScale && lineScale !== 1) { - lineDash = map(lineDash, function (rawVal) { - return rawVal / lineScale; - }); - lineDashOffset /= lineScale; - } + let lineDash; + let lineDashOffset; + if (ctx.setLineDash && style.lineDash) { + [lineDash, lineDashOffset] = getLineDash(el); } let needsRebuild = true; - // Proxy context - // Rebuild path in following 2 cases - // 1. Path is dirty - // 2. Path needs javascript implemented lineDash stroking. - // In this case, lineDash information will not be saved in PathProxy - if (firstDraw || (el.__dirty & SHAPE_CHANGED_BIT) - || (lineDash && !ctxLineDash && hasStroke) - ) { + + if (firstDraw || (dirtyFlag & SHAPE_CHANGED_BIT)) { path.setDPR((ctx as any).dpr); if (strokePart) { // Use rebuildPath for percent stroke, so no context. @@ -231,7 +218,7 @@ function brushPath(ctx: CanvasRenderingContext2D, el: Path, style: PathStyleProp path.rebuildPath(ctx, strokePart ? strokePercent : 1); } - if (lineDash && ctxLineDash) { + if (lineDash) { ctx.setLineDash(lineDash); ctx.lineDashOffset = lineDashOffset; } @@ -255,7 +242,7 @@ function brushPath(ctx: CanvasRenderingContext2D, el: Path, style: PathStyleProp } } - if (lineDash && ctxLineDash) { + if (lineDash) { // PENDING // Remove lineDash ctx.setLineDash([]); @@ -329,23 +316,15 @@ function brushText(ctx: CanvasRenderingContext2D, el: TSpan, style: TSpanStylePr ctx.textAlign = style.textAlign; ctx.textBaseline = style.textBaseline; - let hasLineDash; - if (ctx.setLineDash) { - let lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth); - let lineDashOffset = style.lineDashOffset; - if (lineDash) { - const lineScale = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1; - if (lineScale && lineScale !== 1) { - lineDash = map(lineDash, function (rawVal) { - return rawVal / lineScale; - }); - lineDashOffset /= lineScale; - } - ctx.setLineDash(lineDash); - ctx.lineDashOffset = lineDashOffset; - - hasLineDash = true; - } + let lineDash; + let lineDashOffset; + if (ctx.setLineDash && style.lineDash) { + [lineDash, lineDashOffset] = getLineDash(el); + } + + if (lineDash) { + ctx.setLineDash(lineDash); + ctx.lineDashOffset = lineDashOffset; } if (style.strokeFirst) { @@ -365,7 +344,7 @@ function brushText(ctx: CanvasRenderingContext2D, el: TSpan, style: TSpanStylePr } } - if (hasLineDash) { + if (lineDash) { // Remove lineDash ctx.setLineDash([]); } diff --git a/src/core/PathProxy.ts b/src/core/PathProxy.ts index 620b7353d..fd35e3ade 100644 --- a/src/core/PathProxy.ts +++ b/src/core/PathProxy.ts @@ -49,7 +49,6 @@ const mathMin = Math.min; const mathMax = Math.max; const mathCos = Math.cos; const mathSin = Math.sin; -const mathSqrt = Math.sqrt; const mathAbs = Math.abs; const PI = Math.PI; diff --git a/src/graphic/Text.ts b/src/graphic/Text.ts index c3e312e26..6b5e89fc7 100644 --- a/src/graphic/Text.ts +++ b/src/graphic/Text.ts @@ -34,6 +34,7 @@ export interface TextStylePropsPart { fill?: string stroke?: string + strokeNoScale?: boolean opacity?: number fillOpacity?: number diff --git a/src/graphic/helper/dashStyle.ts b/src/graphic/helper/dashStyle.ts deleted file mode 100644 index 732ad45c5..000000000 --- a/src/graphic/helper/dashStyle.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { isArray, isNumber } from '../../core/util'; - -export function normalizeLineDash(lineType: any, lineWidth?: number): number[] | false { - if (!lineType || lineType === 'solid' || !(lineWidth > 0)) { - return null; - } - lineWidth = lineWidth || 1; - return lineType === 'dashed' - ? [4 * lineWidth, 2 * lineWidth] - : lineType === 'dotted' - ? [lineWidth] - : isNumber(lineType) - ? [lineType] : isArray(lineType) ? lineType : null; -} \ No newline at end of file diff --git a/src/svg/mapStyleToAttrs.ts b/src/svg/mapStyleToAttrs.ts index 4b53646a4..486f3bda7 100644 --- a/src/svg/mapStyleToAttrs.ts +++ b/src/svg/mapStyleToAttrs.ts @@ -2,7 +2,7 @@ import Path, { DEFAULT_PATH_STYLE, PathStyleProps } from '../graphic/Path'; import ZRImage, { ImageStyleProps } from '../graphic/Image'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; -import { normalizeLineDash } from '../graphic/helper/dashStyle'; +import { getLineDash } from '../canvas/dashStyle'; import { map } from '../core/util'; import { normalizeColor } from './helper'; @@ -38,7 +38,7 @@ export default function mapStyleToAttrs( // only set opacity. stroke and fill cannot be applied to svg image if (el instanceof ZRImage) { - updateAttr('opacity', opacity + ''); + updateAttr('opacity', opacity); return; } @@ -49,7 +49,7 @@ export default function mapStyleToAttrs( ? style.fillOpacity * fill.opacity * opacity : fill.opacity * opacity; if (forceUpdate || fillOpacity < 1) { - updateAttr('fill-opacity', fillOpacity + ''); + updateAttr('fill-opacity', fillOpacity); } } else { @@ -69,32 +69,25 @@ export default function mapStyleToAttrs( const strokeFirst = style.strokeFirst; if (forceUpdate || strokeWidth !== 1) { - updateAttr('stroke-width', strokeWidth + ''); + updateAttr('stroke-width', strokeWidth); } // stroke then fill for text; fill then stroke for others if (forceUpdate || strokeFirst) { updateAttr('paint-order', strokeFirst ? 'stroke' : 'fill'); } if (forceUpdate || strokeOpacity < 1) { - updateAttr('stroke-opacity', strokeOpacity + ''); + updateAttr('stroke-opacity', strokeOpacity); } - let lineDash = style.lineDash && strokeWidth > 0 && normalizeLineDash(style.lineDash, strokeWidth); - if (lineDash) { - let lineDashOffset = style.lineDashOffset; - if (strokeScale && strokeScale !== 1) { - lineDash = map(lineDash, function (rawVal) { - return rawVal / strokeScale; - }); - if (lineDashOffset) { - lineDashOffset /= strokeScale; - lineDashOffset = mathRound(lineDashOffset); + if (style.lineDash) { + let [lineDash, lineDashOffset] = getLineDash(el); + if (lineDash) { + lineDashOffset = mathRound(lineDashOffset || 0); + updateAttr('stroke-dasharray', lineDash.join(',')); + if (lineDashOffset || forceUpdate) { + updateAttr('stroke-dashoffset', lineDashOffset); } } - updateAttr('stroke-dasharray', lineDash.join(',')); - if (lineDashOffset || forceUpdate) { - updateAttr('stroke-dashoffset', (lineDashOffset || 0) + ''); - } } else if (forceUpdate) { // Reset if force update. From c85a5ded37be6d5410b2a66e9a98bf1d801bd6a5 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 1 Dec 2021 11:19:31 +0800 Subject: [PATCH 079/148] fix(animation): use max time of animator in each keyframe --- src/animation/Animator.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index c9fd3015e..1c551ba65 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -188,7 +188,6 @@ let tmpRgba: number[] = [0, 0, 0, 0]; class Track { keyframes: Keyframe[] = [] - maxTime: number = 0 propName: string @@ -232,8 +231,7 @@ class Track { needsAnimate() { return !this._isAllValueEqual && this.keyframes.length >= 2 - && this.interpolable - && this.maxTime > 0; + && this.interpolable; } getAdditiveTrack() { @@ -241,12 +239,7 @@ class Track { } addKeyframe(time: number, value: unknown, easing?: AnimationEasing) { - if (time >= this.maxTime) { - this.maxTime = time; - } - else { - this._needsSort = true; - } + this._needsSort = true; let keyframes = this.keyframes; let len = keyframes.length; @@ -330,7 +323,7 @@ class Track { return kf; } - prepare(additiveTrack?: Track) { + prepare(maxTime: number, additiveTrack?: Track) { let kfs = this.keyframes; if (this._needsSort) { // Sort keyframe as ascending @@ -344,7 +337,7 @@ class Track { const lastKf = kfs[kfsLen - 1]; for (let i = 0; i < kfsLen; i++) { - kfs[i].percent = kfs[i].time / this.maxTime; + kfs[i].percent = kfs[i].time / maxTime; if (arrDim > 0 && i !== kfsLen - 1) { // Align array with target frame. @@ -766,7 +759,7 @@ export default class Animator { const track = this._tracks[propName]; const additiveTrack = this._getAdditiveTrack(propName); const kfs = track.keyframes; - track.prepare(additiveTrack); + track.prepare(this._maxTime, additiveTrack); if (track.needsAnimate()) { tracks.push(track); } @@ -1013,7 +1006,7 @@ export default class Animator { track.addKeyframe(lastKf.time, finalProps[propName]); // Prepare again. - track.prepare(track.getAdditiveTrack()); + track.prepare(this._maxTime, track.getAdditiveTrack()); } } } From 975663b08eba8a5b512c392791e2d9decc71f7e1 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 1 Dec 2021 13:52:10 +0800 Subject: [PATCH 080/148] fix(text): fix fill 'none' not work --- src/graphic/Text.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/graphic/Text.ts b/src/graphic/Text.ts index 6b5e89fc7..254360790 100644 --- a/src/graphic/Text.ts +++ b/src/graphic/Text.ts @@ -586,15 +586,15 @@ class ZRText extends Displayable implements GroupLike { subElStyle.shadowOffsetY = style.textShadowOffsetY || 0; } + // Always override default fill and stroke value. + subElStyle.stroke = textStroke as string; + subElStyle.fill = textFill as string; + if (textStroke) { - subElStyle.stroke = textStroke as string; subElStyle.lineWidth = style.lineWidth || defaultLineWidth; subElStyle.lineDash = style.lineDash; subElStyle.lineDashOffset = style.lineDashOffset || 0; } - if (textFill) { - subElStyle.fill = textFill as string; - } subElStyle.font = textFont; setSeparateFont(subElStyle, style); From 07483bfdfc26398ca34a7377cc86aee00414b0c5 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 1 Dec 2021 22:47:09 +0800 Subject: [PATCH 081/148] feat(animation): support cubic bezier easing expression --- src/animation/Animator.ts | 9 ++-- src/animation/Clip.ts | 24 ++++++---- src/animation/cubicEasing.ts | 27 +++++++++++ src/animation/easing.ts | 1 + src/svg/cssAnimation.ts | 7 ++- test/animation-cubic-easing.html | 81 ++++++++++++++++++++++++++++++++ 6 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 src/animation/cubicEasing.ts create mode 100644 test/animation-cubic-easing.html diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 1c551ba65..2784753a0 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -4,10 +4,11 @@ import Clip from './Clip'; import * as color from '../tool/color'; -import {isArrayLike, isFunction, isString, keys, logError, map} from '../core/util'; +import {isArrayLike, isFunction, keys, logError, map} from '../core/util'; import {ArrayLike, Dictionary} from '../core/types'; import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; +import { createCubicEasingFunc } from './cubicEasing'; type NumberArray = ArrayLike type InterpolatableType = string | number | NumberArray | NumberArray[]; @@ -316,7 +317,9 @@ class Track { if (easing) { // Save the raw easing name to be used in css animation output kf.easing = easing; - kf.easingFunc = isFunction(easing) ? easing : easingFuncs[easing]; + kf.easingFunc = isFunction(easing) + ? easing + : easingFuncs[easing] || createCubicEasingFunc(easing); } // Not check if value equal here. keyframes.push(kf); @@ -818,7 +821,7 @@ export default class Animator { } if (easing) { - clip.easing = easing; + clip.setEasing(easing); } } else { diff --git a/src/animation/Clip.ts b/src/animation/Clip.ts index ed8c3040b..7429db192 100644 --- a/src/animation/Clip.ts +++ b/src/animation/Clip.ts @@ -15,7 +15,8 @@ import easingFuncs, {AnimationEasing} from './easing'; import type Animation from './Animation'; -import { isFunction, isString, noop } from '../core/util'; +import { isFunction, noop } from '../core/util'; +import { createCubicEasingFunc } from './cubicEasing'; type OnframeCallback = (percent: number) => void; type ondestroyCallback = () => void @@ -51,7 +52,9 @@ export default class Clip { loop: boolean gap: number + easing: AnimationEasing + easingFunc: (p: number) => number // For linked list. Readonly next: Clip @@ -70,11 +73,11 @@ export default class Clip { this.gap = opts.gap || 0; - this.easing = opts.easing || 'linear'; - this.onframe = opts.onframe || noop; this.ondestroy = opts.ondestroy || noop; this.onrestart = opts.onrestart || noop; + + opts.easing && this.setEasing(opts.easing); } step(globalTime: number, deltaTime: number): boolean { @@ -104,12 +107,8 @@ export default class Clip { percent = Math.min(percent, 1); - const easing = this.easing; - const easingFunc = isString(easing) - ? easingFuncs[easing as keyof typeof easingFuncs] : easing; - const schedule = isFunction(easingFunc) - ? easingFunc(percent) - : percent; + const easingFunc = this.easingFunc; + const schedule = easingFunc ? easingFunc(percent) : percent; this.onframe(schedule); @@ -138,4 +137,11 @@ export default class Clip { resume() { this._paused = false; } + + setEasing(easing: AnimationEasing) { + this.easing = easing; + this.easingFunc = isFunction(easing) + ? easing + : easingFuncs[easing] || createCubicEasingFunc(easing); + } } \ No newline at end of file diff --git a/src/animation/cubicEasing.ts b/src/animation/cubicEasing.ts new file mode 100644 index 000000000..bc5d10e2a --- /dev/null +++ b/src/animation/cubicEasing.ts @@ -0,0 +1,27 @@ +import { cubicAt, cubicRootAt } from '../core/curve'; +import { trim } from '../core/util'; + +const regexp = /cubic-bezier\(([0-9,\.e ]+)\)/; + +export function createCubicEasingFunc(cubicEasingStr: string) { + const cubic = cubicEasingStr && regexp.exec(cubicEasingStr); + if (cubic) { + const points = cubic[1].split(','); + const a = +trim(points[0]); + const b = +trim(points[1]); + const c = +trim(points[2]); + const d = +trim(points[3]); + + if (isNaN(a + b + c + d)) { + return; + } + + const roots: number[] = []; + return (p: number) => { + return p <= 0 + ? 0 : p >= 1 + ? 1 + : cubicRootAt(0, a, c, 1, p, roots) && cubicAt(0, b, d, 1, roots[0]); + }; + } +} \ No newline at end of file diff --git a/src/animation/easing.ts b/src/animation/easing.ts index e7602c0f5..e240ee18d 100644 --- a/src/animation/easing.ts +++ b/src/animation/easing.ts @@ -347,4 +347,5 @@ const easingFuncs = { } }; + export default easingFuncs; \ No newline at end of file diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 7e207746c..3560c9618 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -9,6 +9,7 @@ import { each, extend, filter, isString, keys } from '../core/util'; import Animator from '../animation/Animator'; import { CompoundPath } from '../export'; import { AnimationEasing } from '../animation/easing'; +import { createCubicEasingFunc } from '../animation/cubicEasing'; export const EASING_MAP: Record = { // From https://easings.net/ @@ -133,7 +134,11 @@ function createCompoundPathCSSAnimation( } function getEasingFunc(easing: AnimationEasing) { - return (isString(easing) && EASING_MAP[easing]) ? `cubic-bezier(${EASING_MAP[easing]})` : ''; + return isString(easing) + ? EASING_MAP[easing] + ? `cubic-bezier(${EASING_MAP[easing]})` + : createCubicEasingFunc(easing) ? easing : '' + : ''; } export function createCSSAnimation( diff --git a/test/animation-cubic-easing.html b/test/animation-cubic-easing.html new file mode 100644 index 000000000..800a249d1 --- /dev/null +++ b/test/animation-cubic-easing.html @@ -0,0 +1,81 @@ + + + + + Animation Keyframe Easing + + + + + + + +
+

SVG SSR

+
+ + + \ No newline at end of file From e8283b031330807f628fe65c70cae8ad41344c0b Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 3 Dec 2021 11:11:29 +0800 Subject: [PATCH 082/148] fix(svg): set attributes for svg dom when initializing. --- src/svg/Painter.ts | 3 ++- src/svg/patch.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 5e6786c88..78ec0bbe3 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -22,7 +22,7 @@ import { import { normalizeColor } from './helper'; import { defaults, extend, keys, logError, map } from '../core/util'; import Path from '../graphic/Path'; -import patch from './patch'; +import patch, { updateAttrs } from './patch'; import { getSize } from '../canvas/helper'; let svgId = 0; @@ -71,6 +71,7 @@ class SVGPainter implements PainterBase { const viewport = this._viewport = document.createElement('div'); viewport.style.cssText = 'position:relative;overflow:hidden'; const svgDom = this._svgDom = this._oldVNode.elm = createElement('svg'); + updateAttrs(null, this._oldVNode); viewport.appendChild(svgDom); root.appendChild(viewport); } diff --git a/src/svg/patch.ts b/src/svg/patch.ts index 32fbfb69b..aeba565c8 100644 --- a/src/svg/patch.ts +++ b/src/svg/patch.ts @@ -120,10 +120,10 @@ function removeVnodes(parentElm: Node, vnodes: SVGVNode[], startIdx: number, end } } -function updateAttrs(oldVnode: SVGVNode, vnode: SVGVNode): void { +export function updateAttrs(oldVnode: SVGVNode, vnode: SVGVNode): void { let key: string; const elm = vnode.elm as Element; - const oldAttrs = oldVnode.attrs || {}; + const oldAttrs = oldVnode && oldVnode.attrs || {}; const attrs = vnode.attrs || {}; if (oldAttrs === attrs) { From b8ed9ff8ca95a41484491be8aed9ce9cfa84569f Mon Sep 17 00:00:00 2001 From: pissang Date: Sun, 5 Dec 2021 23:57:40 +0800 Subject: [PATCH 083/148] feat(anim): ability to set min duration in animator. Still set attributes once when all values in keyframes are same. --- src/Element.ts | 2 +- src/animation/Animator.ts | 88 ++++++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index a8749c317..6b1a0b730 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -1765,7 +1765,7 @@ function animateTo( if (abortedCb) { animator.aborted(abortedCb); } - animator.start(cfg.easing, cfg.force); + animator.start(cfg.easing, cfg.force ? cfg.duration : 0); } return animators; diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 2784753a0..e1ceb3bf5 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -4,7 +4,7 @@ import Clip from './Clip'; import * as color from '../tool/color'; -import {isArrayLike, isFunction, keys, logError, map} from '../core/util'; +import {isArrayLike, isFunction, isNumber, keys, logError, map} from '../core/util'; import {ArrayLike, Dictionary} from '../core/types'; import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; @@ -202,15 +202,19 @@ class Track { private _needsSort: boolean = false - private _isAllValueEqual = true - private _additiveTrack: Track // Temporal storage for interpolated additive value. private _additiveValue: unknown // Info for run - private _lastFrame = 0 - private _lastFramePercent = 0 + /** + * Last frame + */ + private _lastFr = 0 + /** + * Percent of last frame. + */ + private _lastFrP = 0 constructor(propName: string) { this.propName = propName; @@ -230,8 +234,7 @@ class Track { } needsAnimate() { - return !this._isAllValueEqual - && this.keyframes.length >= 2 + return this.keyframes.length >= 2 && this.interpolable; } @@ -254,8 +257,8 @@ class Track { return; } // Not a number array. - if (arrayDim === 1 && typeof value[0] !== 'number' - || arrayDim === 2 && typeof value[0][0] !== 'number') { + if (arrayDim === 1 && !isNumber(value[0]) + || arrayDim === 2 && !isNumber(value[0][0])) { this.interpolable = false; return; } @@ -263,15 +266,9 @@ class Track { let lastFrame = keyframes[len - 1]; // For performance consideration. only check 1d array - if (this._isAllValueEqual) { - if (arrayDim === 1) { - if (!is1DArraySame(value, lastFrame.value as number[])) { - this._isAllValueEqual = false; - } - } - else { - this._isAllValueEqual = false; - } + if (arrayDim === 1 && is1DArraySame(value, lastFrame.value as number[])) { + // Ignore this frame. + return; } } this.arrDim = arrayDim; @@ -297,13 +294,13 @@ class Track { return; } - if (this._isAllValueEqual && len > 0) { + if (len > 0) { let lastFrame = keyframes[len - 1]; - if (this.isColor && !is1DArraySame(lastFrame.value as number[], value as number[])) { - this._isAllValueEqual = false; - } - else if (lastFrame.value !== value) { - this._isAllValueEqual = false; + if (lastFrame.value === value + || this.isColor && is1DArraySame(lastFrame.value as number[], value as number[]) + ) { + // Ignore this frame. + return; } } } @@ -412,13 +409,13 @@ class Track { // kf1-----kf2---------current--------kf3 // find kf2 and kf3 and do interpolation let frameIdx; - const lastFrame = this._lastFrame; + const lastFrame = this._lastFr; const min = Math.min; // In the easing function like elasticOut, percent may less than 0 if (percent < 0) { frameIdx = 0; } - else if (percent < this._lastFramePercent) { + else if (percent < this._lastFrP) { // Start from next key // PENDING start from lastFrame ? const start = min(lastFrame + 1, kfsNum - 1); @@ -427,7 +424,6 @@ class Track { break; } } - // PENDING really need to do this ? frameIdx = min(frameIdx, kfsNum - 2); } else { @@ -447,8 +443,8 @@ class Track { return; } - this._lastFrame = frameIdx; - this._lastFramePercent = percent; + this._lastFr = frameIdx; + this._lastFrP = percent; const range = (nextFrame.percent - frame.percent); if (range === 0) { @@ -745,10 +741,13 @@ export default class Animator { /** * Start the animation * @param easing - * @param forceAnimate + * @param minDuration Set min duration of animation. * @return */ - start(easing?: AnimationEasing, forceAnimate?: boolean) { + start( + easing?: AnimationEasing, + minDuration?: number + ) { if (this._started > 0) { return; } @@ -757,27 +756,37 @@ export default class Animator { const self = this; let tracks: Track[] = []; + let oneShotTracks: Track[] = []; + let maxTime = this._maxTime || 0; + if (minDuration) { + maxTime = Math.max(minDuration, maxTime); + } + for (let i = 0; i < this._trackKeys.length; i++) { const propName = this._trackKeys[i]; const track = this._tracks[propName]; const additiveTrack = this._getAdditiveTrack(propName); const kfs = track.keyframes; + const kfsNum = kfs.length; track.prepare(this._maxTime, additiveTrack); if (track.needsAnimate()) { tracks.push(track); } else if (!track.interpolable) { - const lastKf = kfs[kfs.length - 1]; + const lastKf = kfs[kfsNum - 1]; // Set final value. if (lastKf) { (self._target as any)[track.propName] = lastKf.value; } } + else if (kfsNum === 1) { + oneShotTracks.push(track); + } } // Add during callback on the last clip - if (tracks.length || forceAnimate) { + if (tracks.length || minDuration > 0) { const clip = new Clip({ - life: this._maxTime, + life: maxTime, loop: this._loop, delay: this._delay, onframe(percent: number) { @@ -803,6 +812,17 @@ export default class Animator { // Because target may be changed. tracks[i].step(self._target, percent); } + if (oneShotTracks) { + // For tracks that only has percent: 0 keyframe + // We do step once for setting the property to the targets. + // For example. animate().when(0, { x: 0 }).when(100, {x: 0}) + // Only the first keyframe will be keepd. + for (let i = 0; i < oneShotTracks.length; i++) { + oneShotTracks[i].step(self._target, percent); + } + oneShotTracks = null; + } + const onframeList = self._onframeCbs; if (onframeList) { for (let i = 0; i < onframeList.length; i++) { From c004d17f70224dbd2fdc9278e197fe679405ef6b Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 7 Dec 2021 12:27:51 +0800 Subject: [PATCH 084/148] fix(animation): optimize keyframe animation For the case: el.x = 0; el.animate().when(0, {x: 100}.when(100, {x: 100} Ensure the attribute will be set instead of ignored. --- src/animation/Animator.ts | 80 +++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index e1ceb3bf5..5d073d3ce 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -198,6 +198,12 @@ class Track { interpolable: boolean = true + /** + * If force run this track regardless there is only one keyframe. + * It can make sure the target can always be set with the value from keyframe. + */ + force?: boolean + private _finished: boolean private _needsSort: boolean = false @@ -234,7 +240,8 @@ class Track { } needsAnimate() { - return this.keyframes.length >= 2 + const kfsNum = this.keyframes.length; + return (kfsNum >= 2 || this.force && kfsNum >= 1) && this.interpolable; } @@ -411,32 +418,39 @@ class Track { let frameIdx; const lastFrame = this._lastFr; const min = Math.min; - // In the easing function like elasticOut, percent may less than 0 - if (percent < 0) { - frameIdx = 0; + let frame; + let nextFrame; + if (kfsNum === 1 && this.force) { + frame = nextFrame = keyframes[0]; } - else if (percent < this._lastFrP) { - // Start from next key - // PENDING start from lastFrame ? - const start = min(lastFrame + 1, kfsNum - 1); - for (frameIdx = start; frameIdx >= 0; frameIdx--) { - if (keyframes[frameIdx].percent <= percent) { - break; + else { + // In the easing function like elasticOut, percent may less than 0 + if (percent < 0) { + frameIdx = 0; + } + else if (percent < this._lastFrP) { + // Start from next key + // PENDING start from lastFrame ? + const start = min(lastFrame + 1, kfsNum - 1); + for (frameIdx = start; frameIdx >= 0; frameIdx--) { + if (keyframes[frameIdx].percent <= percent) { + break; + } } + frameIdx = min(frameIdx, kfsNum - 2); } - frameIdx = min(frameIdx, kfsNum - 2); - } - else { - for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) { - if (keyframes[frameIdx].percent > percent) { - break; + else { + for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) { + if (keyframes[frameIdx].percent > percent) { + break; + } } + frameIdx = min(frameIdx - 1, kfsNum - 2); } - frameIdx = min(frameIdx - 1, kfsNum - 2); - } - let nextFrame = keyframes[frameIdx + 1]; - let frame = keyframes[frameIdx]; + nextFrame = keyframes[frameIdx + 1]; + frame = keyframes[frameIdx]; + } // Defensive coding. if (!(frame && nextFrame)) { @@ -446,11 +460,8 @@ class Track { this._lastFr = frameIdx; this._lastFrP = percent; - const range = (nextFrame.percent - frame.percent); - if (range === 0) { - return; - } - let w = (percent - frame.percent) / range; + const interval = (nextFrame.percent - frame.percent); + let w = interval === 0 ? 1 : (percent - frame.percent) / interval; // Apply different easing of each keyframe. // Use easing specified in target frame. if (nextFrame.easingFunc) { @@ -662,6 +673,9 @@ export default class Animator { if (time !== 0) { track.addKeyframe(0, cloneValue(initialValue), easing); } + else { + track.force = true; + } this._trackKeys.push(propName); } @@ -756,7 +770,6 @@ export default class Animator { const self = this; let tracks: Track[] = []; - let oneShotTracks: Track[] = []; let maxTime = this._maxTime || 0; if (minDuration) { maxTime = Math.max(minDuration, maxTime); @@ -779,9 +792,6 @@ export default class Animator { (self._target as any)[track.propName] = lastKf.value; } } - else if (kfsNum === 1) { - oneShotTracks.push(track); - } } // Add during callback on the last clip if (tracks.length || minDuration > 0) { @@ -812,16 +822,6 @@ export default class Animator { // Because target may be changed. tracks[i].step(self._target, percent); } - if (oneShotTracks) { - // For tracks that only has percent: 0 keyframe - // We do step once for setting the property to the targets. - // For example. animate().when(0, { x: 0 }).when(100, {x: 0}) - // Only the first keyframe will be keepd. - for (let i = 0; i < oneShotTracks.length; i++) { - oneShotTracks[i].step(self._target, percent); - } - oneShotTracks = null; - } const onframeList = self._onframeCbs; if (onframeList) { From 220ab413c18500cb2f6205d63e7b6f1e1f2435f5 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 9 Dec 2021 14:42:18 +0800 Subject: [PATCH 085/148] animation: remove unused Clip#gap. fix max time issue --- src/animation/Animator.ts | 15 ++++++++------- src/animation/Clip.ts | 7 +------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 5d073d3ce..b0fcdd59f 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -417,7 +417,7 @@ class Track { // find kf2 and kf3 and do interpolation let frameIdx; const lastFrame = this._lastFr; - const min = Math.min; + const mathMin = Math.min; let frame; let nextFrame; if (kfsNum === 1 && this.force) { @@ -431,13 +431,13 @@ class Track { else if (percent < this._lastFrP) { // Start from next key // PENDING start from lastFrame ? - const start = min(lastFrame + 1, kfsNum - 1); + const start = mathMin(lastFrame + 1, kfsNum - 1); for (frameIdx = start; frameIdx >= 0; frameIdx--) { if (keyframes[frameIdx].percent <= percent) { break; } } - frameIdx = min(frameIdx, kfsNum - 2); + frameIdx = mathMin(frameIdx, kfsNum - 2); } else { for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) { @@ -445,7 +445,7 @@ class Track { break; } } - frameIdx = min(frameIdx - 1, kfsNum - 2); + frameIdx = mathMin(frameIdx - 1, kfsNum - 2); } nextFrame = keyframes[frameIdx + 1]; @@ -461,7 +461,8 @@ class Track { this._lastFrP = percent; const interval = (nextFrame.percent - frame.percent); - let w = interval === 0 ? 1 : (percent - frame.percent) / interval; + let w = interval === 0 ? 1 : mathMin((percent - frame.percent) / interval, 1); + // Apply different easing of each keyframe. // Use easing specified in target frame. if (nextFrame.easingFunc) { @@ -772,7 +773,7 @@ export default class Animator { let tracks: Track[] = []; let maxTime = this._maxTime || 0; if (minDuration) { - maxTime = Math.max(minDuration, maxTime); + maxTime = this._maxTime = Math.max(minDuration, maxTime); } for (let i = 0; i < this._trackKeys.length; i++) { @@ -781,7 +782,7 @@ export default class Animator { const additiveTrack = this._getAdditiveTrack(propName); const kfs = track.keyframes; const kfsNum = kfs.length; - track.prepare(this._maxTime, additiveTrack); + track.prepare(maxTime, additiveTrack); if (track.needsAnimate()) { tracks.push(track); } diff --git a/src/animation/Clip.ts b/src/animation/Clip.ts index 7429db192..0acf6bf40 100644 --- a/src/animation/Clip.ts +++ b/src/animation/Clip.ts @@ -4,7 +4,6 @@ * @config life(1000) 动画时长 * @config delay(0) 动画延迟时间 * @config loop(true) - * @config gap(0) 循环的间隔时间 * @config onframe * @config easing(optional) * @config ondestroy(optional) @@ -29,7 +28,6 @@ export interface ClipProps { life?: number delay?: number loop?: boolean - gap?: number easing?: AnimationEasing onframe?: OnframeCallback @@ -51,7 +49,6 @@ export default class Clip { animation: Animation loop: boolean - gap: number easing: AnimationEasing easingFunc: (p: number) => number @@ -71,8 +68,6 @@ export default class Clip { this.loop = opts.loop || false; - this.gap = opts.gap || 0; - this.onframe = opts.onframe || noop; this.ondestroy = opts.ondestroy || noop; this.onrestart = opts.onrestart || noop; @@ -117,7 +112,7 @@ export default class Clip { if (this.loop) { // Restart const remainder = elapsedTime % life; - this._startTime = globalTime - remainder + this.gap; + this._startTime = globalTime - remainder; this._pausedTime = 0; this.onrestart(); From c0c631894e8addae8af145ed45b0dc177567de32 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 10 Dec 2021 14:33:45 +0800 Subject: [PATCH 086/148] feat: support discrete animation --- src/Element.ts | 28 ++- src/animation/Animation.ts | 2 +- src/animation/Animator.ts | 173 ++++++++---------- .../spec/animation/ElementAnimation.test.ts | 28 +++ 4 files changed, 129 insertions(+), 102 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index 6b1a0b730..adce9e7a6 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -1,6 +1,6 @@ import Transformable from './core/Transformable'; import { AnimationEasing } from './animation/easing'; -import Animator, {cloneValue} from './animation/Animator'; +import Animator, {cloneValue, is1DArraySame} from './animation/Animator'; import { ZRenderType } from './zrender'; import { Dictionary, ElementEventName, ZRRawEvent, BuiltinTextPosition, AllPropTypes, @@ -1461,13 +1461,14 @@ class Element { * * @param path The key to fetch value from object. Mostly style or shape. * @param loop Whether to loop animation. + * @param allowDiscreteAnimation Whether to allow discrete animation * @example: * el.animate('style', false) * .when(1000, {x: 10} ) * .done(function(){ // Animation done }) * .start() */ - animate(key?: string, loop?: boolean) { + animate(key?: string, loop?: boolean, allowDiscreteAnimation?: boolean) { let target = key ? (this as any)[key] : this; if (process.env.NODE_ENV !== 'production') { @@ -1482,7 +1483,7 @@ class Element { } } - const animator = new Animator(target, loop); + const animator = new Animator(target, loop, allowDiscreteAnimation); key && (animator.targetName = key); this.addAnimator(animator, key); return animator; @@ -1824,6 +1825,22 @@ function copyValue(target: Dictionary, source: Dictionary, key: string } } +function needAnimateKey(target: Dictionary, source: Dictionary, key: string, force: boolean) { + const sourceVal = source[key]; + const targetVal = target[key]; + return !( + // Can't animate between null value. assign directly. For example. stroke animate from #fff to null. + sourceVal == null + || targetVal == null + // Not animate not changed value if not using force. + || !force && ( + sourceVal === targetVal + // Only check 1 dimension array + || isArrayLike(sourceVal) && isArrayLike(targetVal) && is1DArraySame(sourceVal, targetVal) + ) + ); +} + function animateToShallow( animatable: Element, topKey: string, @@ -1845,8 +1862,7 @@ function animateToShallow( for (let k = 0; k < targetKeys.length; k++) { const innerKey = targetKeys[k] as string; - if (source[innerKey] != null - && target[innerKey] != null // Can't animate between null value. assign directly. For example. stroke animate from #fff to null. + if (needAnimateKey(target, source, innerKey, cfg.force) && (animateAll || (animationProps as Dictionary)[innerKey]) ) { if (isObject(target[innerKey]) && !isArrayLike(target[innerKey])) { @@ -1953,7 +1969,7 @@ function animateToShallow( } } - const animator = new Animator(source, false, additive ? existsAnimatorsOnSameTarget : null); + const animator = new Animator(source, false, false, additive ? existsAnimatorsOnSameTarget : null); animator.targetName = topKey; if (cfg.scope) { animator.scope = cfg.scope; diff --git a/src/animation/Animation.ts b/src/animation/Animation.ts index 239c9948e..cf2ec8569 100644 --- a/src/animation/Animation.ts +++ b/src/animation/Animation.ts @@ -248,7 +248,7 @@ export default class Animation extends Eventful { */ // TODO Gap animate(target: T, options: { - loop?: boolean // Whether loop animation. + loop?: boolean // Whether loop animation }) { options = options || {}; diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index b0fcdd59f..02147a4cd 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -15,15 +15,10 @@ type InterpolatableType = string | number | NumberArray | NumberArray[]; const arraySlice = Array.prototype.slice; -export function interpolateNumber(p0: number, p1: number, percent: number): number { +function interpolateNumber(p0: number, p1: number, percent: number): number { return (p1 - p0) * percent + p0; } - -export function step(p0: any, p1: any, percent: number): any { - return percent > 0.5 ? p1 : p0; -} - -export function interpolate1DArray( +function interpolate1DArray( out: NumberArray, p0: NumberArray, p1: NumberArray, @@ -36,7 +31,7 @@ export function interpolate1DArray( } } -export function interpolate2DArray( +function interpolate2DArray( out: NumberArray[], p0: NumberArray[], p1: NumberArray[], @@ -133,7 +128,7 @@ function fillArray( } } -function is1DArraySame(arr0: NumberArray, arr1: NumberArray) { +export function is1DArraySame(arr0: NumberArray, arr1: NumberArray) { const len = arr0.length; if (len !== arr1.length) { return false; @@ -196,13 +191,9 @@ class Track { arrDim: number = 0 isColor: boolean - interpolable: boolean = true + discrete: boolean = false - /** - * If force run this track regardless there is only one keyframe. - * It can make sure the target can always be set with the value from keyframe. - */ - force?: boolean + _invalid: boolean = false; private _finished: boolean @@ -240,9 +231,7 @@ class Track { } needsAnimate() { - const kfsNum = this.keyframes.length; - return (kfsNum >= 2 || this.force && kfsNum >= 1) - && this.interpolable; + return this.keyframes.length >= 1; } getAdditiveTrack() { @@ -255,64 +244,43 @@ class Track { let keyframes = this.keyframes; let len = keyframes.length; - if (this.interpolable) { - // Handling values only if it's possible to be interpolated. - if (isArrayLike(value)) { - let arrayDim = guessArrayDim(value); - if (len > 0 && this.arrDim !== arrayDim) { // Two values has differnt dimension. - this.interpolable = false; - return; - } - // Not a number array. - if (arrayDim === 1 && !isNumber(value[0]) - || arrayDim === 2 && !isNumber(value[0][0])) { - this.interpolable = false; - return; - } - if (len > 0) { - let lastFrame = keyframes[len - 1]; + let discrete = false; - // For performance consideration. only check 1d array - if (arrayDim === 1 && is1DArraySame(value, lastFrame.value as number[])) { - // Ignore this frame. - return; - } - } - this.arrDim = arrayDim; + // Handling values only if it's possible to be interpolated. + if (isArrayLike(value)) { + let arrayDim = guessArrayDim(value); + if (len > 0 && this.arrDim !== arrayDim) { // Two values has differnt dimension. + discrete = true; + } + // Not a number array. + if (arrayDim === 1 && !isNumber(value[0]) + || arrayDim === 2 && !isNumber(value[0][0])) { + discrete = true; + } + this.arrDim = arrayDim; + } + else { + if (this.arrDim > 0) { // Previous value is array. + discrete = true; } - else { - if (this.arrDim > 0) { // Previous value is array. - this.interpolable = false; - return; - } - if (typeof value === 'string') { - const colorArray = color.parse(value); - if (colorArray) { - value = colorArray; - this.isColor = true; - } - else { - this.interpolable = false; - } - } - else if (typeof value !== 'number' || isNaN(value)) { - this.interpolable = false; - return; + if (typeof value === 'string') { + const colorArray = color.parse(value); + if (colorArray) { + value = colorArray; + this.isColor = true; } - - if (len > 0) { - let lastFrame = keyframes[len - 1]; - if (lastFrame.value === value - || this.isColor && is1DArraySame(lastFrame.value as number[], value as number[]) - ) { - // Ignore this frame. - return; - } + else { + discrete = true; } } + else if (typeof value !== 'number' || isNaN(value)) { + discrete = true; + } } + this.discrete = this.discrete || discrete; + const kf: Keyframe = { time, value, @@ -404,7 +372,7 @@ class Track { // Remove additive track if it's finished. this._additiveTrack = null; } - const isAdditive = this._additiveTrack != null; + const isAdditive = this._additiveTrack != null && !this.discrete; const valueKey = isAdditive ? 'additiveValue' : 'value'; const keyframes = this.keyframes; @@ -420,7 +388,7 @@ class Track { const mathMin = Math.min; let frame; let nextFrame; - if (kfsNum === 1 && this.force) { + if (kfsNum === 1) { frame = nextFrame = keyframes[0]; } else { @@ -477,7 +445,10 @@ class Track { targetArr = this._additiveValue = []; } - if (arrDim > 0) { + if (this.discrete) { + target[propName] = w < 1 ? frame[valueKey] : nextFrame[valueKey]; + } + else if (arrDim > 0) { arrDim === 1 ? interpolate1DArray( targetArr as NumberArray, @@ -504,14 +475,7 @@ class Track { } } else { - let value; - if (!this.interpolable) { - // String is step(0.5) - value = step(frame[valueKey], nextFrame[valueKey], w); - } - else { - value = interpolateNumber(frame[valueKey] as number, nextFrame[valueKey] as number, w); - } + const value = interpolateNumber(frame[valueKey] as number, nextFrame[valueKey] as number, w); if (isAdditive) { this._additiveValue = value; } @@ -576,16 +540,26 @@ export default class Animator { private _target: T private _loop: boolean - private _delay = 0 + private _delay: number private _maxTime = 0 - // Some status - private _paused = false + /** + * If animator is paused + * @default false + */ + private _paused: boolean // 0: Not started // 1: Invoked started // 2: Has been run for at least one frame. private _started = 0 + /** + * If allow discrete animation + * @default false + */ + private _allowDiscrete: boolean + private _allowDuplicate: boolean + private _additiveAnimators: Animator[] private _doneCbs: DoneCallback[] @@ -595,7 +569,12 @@ export default class Animator { private _clip: Clip = null - constructor(target: T, loop: boolean, additiveTo?: Animator[]) { + constructor( + target: T, + loop: boolean, + allowDiscreteAnimation?: boolean, // If doing discrete animation on the values can't be interpolated + additiveTo?: Animator[] + ) { this._target = target; this._loop = loop; if (loop && additiveTo) { @@ -603,14 +582,18 @@ export default class Animator { return; } this._additiveAnimators = additiveTo; + + this._allowDiscrete = allowDiscreteAnimation; } getMaxTime() { return this._maxTime; } + getDelay() { return this._delay; } + getLoop() { return this._loop; } @@ -674,9 +657,6 @@ export default class Animator { if (time !== 0) { track.addKeyframe(0, cloneValue(initialValue), easing); } - else { - track.force = true; - } this._trackKeys.push(propName); } @@ -784,13 +764,16 @@ export default class Animator { const kfsNum = kfs.length; track.prepare(maxTime, additiveTrack); if (track.needsAnimate()) { - tracks.push(track); - } - else if (!track.interpolable) { - const lastKf = kfs[kfsNum - 1]; - // Set final value. - if (lastKf) { - (self._target as any)[track.propName] = lastKf.value; + // Set value directly if discrete animation is not allowed. + if (!this._allowDiscrete && track.discrete) { + const lastKf = kfs[kfsNum - 1]; + // Set final value. + if (lastKf) { + (self._target as any)[track.propName] = lastKf.value; + } + } + else { + tracks.push(track); } } } @@ -799,7 +782,7 @@ export default class Animator { const clip = new Clip({ life: maxTime, loop: this._loop, - delay: this._delay, + delay: this._delay || 0, onframe(percent: number) { self._started = 2; // Remove additived animator if it's finished. @@ -943,7 +926,7 @@ export default class Animator { if (forwardToLast) { track.step(this._target, 1); } - // If the track has not been run for at least wrong frame. + // If the track has not been run for at least one frame. // The property may be stayed at the final state. when setToFinal is set true. // For example: // Animate x from 0 to 100, then animate to 150 immediately. diff --git a/test/ut/spec/animation/ElementAnimation.test.ts b/test/ut/spec/animation/ElementAnimation.test.ts index 88b21ffd0..26b528124 100644 --- a/test/ut/spec/animation/ElementAnimation.test.ts +++ b/test/ut/spec/animation/ElementAnimation.test.ts @@ -2,6 +2,34 @@ import {Polyline, Rect, init} from '../zrender'; import Animation from '../../../../src/animation/Animation'; describe('ElementAnimation', function () { + + it('Undefined value should not be animated.', function () { + const polyline = new Polyline(); + polyline.animateTo({ + shape: { + foo: 2 + } as any + }, { + setToFinal: true + }); + + expect(polyline.animators.length).toEqual(0); + }); + + + it('Equal value should not be animated.', function () { + const polyline = new Polyline(); + polyline.x = 100; + polyline.animateTo({ + x: 100 + }, { + setToFinal: true + }); + + expect(polyline.animators.length).toEqual(0); + }); + + it('Should be final state when setToFinal', function () { const polyline = new Polyline(); polyline.animateTo({ From 8e8af77d440c6dc82843867fb68741890752f7c0 Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 10 Dec 2021 17:50:57 +0800 Subject: [PATCH 087/148] feat(svg): support exporting base64-encoded dataURL. --- src/svg/Painter.ts | 10 ++++++---- test/svg.html | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 78ec0bbe3..7a93ab1c6 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -339,10 +339,12 @@ class SVGPainter implements PainterBase { } this._oldVNode = null; } - toDataURL() { - const str = this.renderToString(); - const html = encodeURIComponent(str); - return 'data:image/svg+xml;charset=UTF-8,' + html; + toDataURL(base64?: boolean) { + const str = encodeURIComponent(this.renderToString()); + const prefix = 'data:image/svg+xml;'; + return base64 + ? prefix + 'base64,' + window.btoa(unescape(str)) + : prefix + 'charset=UTF-8,' + str; } refreshHover = createMethodNotSupport('refreshHover') as PainterBase['refreshHover']; diff --git a/test/svg.html b/test/svg.html index 76602e3c7..8ae371854 100644 --- a/test/svg.html +++ b/test/svg.html @@ -33,6 +33,26 @@ }); zr.add(rect); + var txt1 = new zrender.Text({ + style: { + x: 10, + y: 250, + text: '切换为SVG 渲染器\n点击导出 Base64 DataURL', + width: 50, + height: 50, + lineHeight: 22, + fill: '#c0f', + fontSize: 18 + }, + draggable: true + }); + txt1.on('click', function () { + if (zr.painter.getType() === 'svg') { + console.log(zr.painter.toDataURL(true)); + } + }); + zr.add(txt1); + setTimeout(function () { rect.style.fill = { type: 'linear', From 893b1543f8eaf788a543f5fdde6663bbdf903775 Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 10 Dec 2021 20:55:18 +0800 Subject: [PATCH 088/148] fix: use `window.btoa` in browser, otherwise fallback to Node Buffer. --- src/svg/Painter.ts | 12 +++++++----- src/svg/helper.ts | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 7a93ab1c6..0ea756fbf 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -19,7 +19,7 @@ import { createBrushScope, createSVGVNode } from './core'; -import { normalizeColor } from './helper'; +import { normalizeColor, encodeBase64 } from './helper'; import { defaults, extend, keys, logError, map } from '../core/util'; import Path from '../graphic/Path'; import patch, { updateAttrs } from './patch'; @@ -340,11 +340,13 @@ class SVGPainter implements PainterBase { this._oldVNode = null; } toDataURL(base64?: boolean) { - const str = encodeURIComponent(this.renderToString()); + let str = encodeURIComponent(this.renderToString()); const prefix = 'data:image/svg+xml;'; - return base64 - ? prefix + 'base64,' + window.btoa(unescape(str)) - : prefix + 'charset=UTF-8,' + str; + if (base64) { + str = encodeBase64(str); + return str ? prefix + 'base64,' + str : null; + } + return prefix + 'charset=UTF-8,' + str; } refreshHover = createMethodNotSupport('refreshHover') as PainterBase['refreshHover']; diff --git a/src/svg/helper.ts b/src/svg/helper.ts index df49c461a..7b4d75843 100644 --- a/src/svg/helper.ts +++ b/src/svg/helper.ts @@ -169,4 +169,20 @@ export function getSRTTransformString( } return res.join(' '); -} \ No newline at end of file +} + +export const encodeBase64 = (function () { + if (typeof window !== 'undefined' && typeof window.btoa === 'function') { + return function (str: string) { + return window.btoa(unescape(str)); + }; + } + if (typeof Buffer !== 'undefined') { + return function (str: string) { + return Buffer.from(str).toString('base64'); + }; + } + return function (str: string): string { + return null; + }; +})(); \ No newline at end of file From 960fa6e80c9742a9bd25a69cd1c214572024c0ee Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 10 Dec 2021 21:45:16 +0800 Subject: [PATCH 089/148] fix: throw an error if base64 isn't natively supported. --- src/svg/Painter.ts | 8 ++------ src/svg/helper.ts | 3 +++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 0ea756fbf..46cf417f8 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -341,12 +341,8 @@ class SVGPainter implements PainterBase { } toDataURL(base64?: boolean) { let str = encodeURIComponent(this.renderToString()); - const prefix = 'data:image/svg+xml;'; - if (base64) { - str = encodeBase64(str); - return str ? prefix + 'base64,' + str : null; - } - return prefix + 'charset=UTF-8,' + str; + return 'data:image/svg+xml;' + + (base64 ? 'base64,' + encodeBase64(str) : 'charset=UTF-8,' + str); } refreshHover = createMethodNotSupport('refreshHover') as PainterBase['refreshHover']; diff --git a/src/svg/helper.ts b/src/svg/helper.ts index 7b4d75843..fb6e19e14 100644 --- a/src/svg/helper.ts +++ b/src/svg/helper.ts @@ -183,6 +183,9 @@ export const encodeBase64 = (function () { }; } return function (str: string): string { + if (process.env.NODE_ENV !== 'production') { + throw new Error('Base64 isn\'t natively supported in the current environment.'); + } return null; }; })(); \ No newline at end of file From e4bb5e2dd07a6cbc2e7fc4c53683efff8fdda84e Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 10 Dec 2021 21:51:58 +0800 Subject: [PATCH 090/148] fix: use const instead of let. --- src/svg/Painter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 46cf417f8..12ea01cd8 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -340,7 +340,7 @@ class SVGPainter implements PainterBase { this._oldVNode = null; } toDataURL(base64?: boolean) { - let str = encodeURIComponent(this.renderToString()); + const str = encodeURIComponent(this.renderToString()); return 'data:image/svg+xml;' + (base64 ? 'base64,' + encodeBase64(str) : 'charset=UTF-8,' + str); } From f66fb075a073459f77b208e2fc9bc4cc2aaa5cb7 Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 10 Dec 2021 22:25:27 +0800 Subject: [PATCH 091/148] fix: use `console.error` instead of `throw new Error`. --- src/svg/Painter.ts | 10 +++++++--- src/svg/helper.ts | 24 ++++++++++++------------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 12ea01cd8..0ea756fbf 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -340,9 +340,13 @@ class SVGPainter implements PainterBase { this._oldVNode = null; } toDataURL(base64?: boolean) { - const str = encodeURIComponent(this.renderToString()); - return 'data:image/svg+xml;' - + (base64 ? 'base64,' + encodeBase64(str) : 'charset=UTF-8,' + str); + let str = encodeURIComponent(this.renderToString()); + const prefix = 'data:image/svg+xml;'; + if (base64) { + str = encodeBase64(str); + return str ? prefix + 'base64,' + str : null; + } + return prefix + 'charset=UTF-8,' + str; } refreshHover = createMethodNotSupport('refreshHover') as PainterBase['refreshHover']; diff --git a/src/svg/helper.ts b/src/svg/helper.ts index fb6e19e14..0ed13a22e 100644 --- a/src/svg/helper.ts +++ b/src/svg/helper.ts @@ -2,7 +2,7 @@ import { MatrixArray } from '../core/matrix'; import Transformable, { TransformProp } from '../core/Transformable'; -import { RADIAN_TO_DEGREE, retrieve2 } from '../core/util'; +import { RADIAN_TO_DEGREE, retrieve2, logError } from '../core/util'; import Displayable from '../graphic/Displayable'; import { GradientObject } from '../graphic/Gradient'; import { LinearGradientObject } from '../graphic/LinearGradient'; @@ -172,19 +172,19 @@ export function getSRTTransformString( } export const encodeBase64 = (function () { - if (typeof window !== 'undefined' && typeof window.btoa === 'function') { - return function (str: string) { - return window.btoa(unescape(str)); - }; - } - if (typeof Buffer !== 'undefined') { - return function (str: string) { - return Buffer.from(str).toString('base64'); - }; - } + // if (typeof window !== 'undefined' && typeof window.btoa === 'function') { + // return function (str: string) { + // return window.btoa(unescape(str)); + // }; + // } + // if (typeof Buffer !== 'undefined') { + // return function (str: string) { + // return Buffer.from(str).toString('base64'); + // }; + // } return function (str: string): string { if (process.env.NODE_ENV !== 'production') { - throw new Error('Base64 isn\'t natively supported in the current environment.'); + logError('Base64 isn\'t natively supported in the current environment.'); } return null; }; From e3d7e84b7a669f146a6f079a33a5f0f390edc94f Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 10 Dec 2021 22:27:18 +0800 Subject: [PATCH 092/148] fix: fix unexpected dev code. --- src/svg/helper.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/svg/helper.ts b/src/svg/helper.ts index 0ed13a22e..c80d62ee0 100644 --- a/src/svg/helper.ts +++ b/src/svg/helper.ts @@ -172,16 +172,16 @@ export function getSRTTransformString( } export const encodeBase64 = (function () { - // if (typeof window !== 'undefined' && typeof window.btoa === 'function') { - // return function (str: string) { - // return window.btoa(unescape(str)); - // }; - // } - // if (typeof Buffer !== 'undefined') { - // return function (str: string) { - // return Buffer.from(str).toString('base64'); - // }; - // } + if (typeof window !== 'undefined' && typeof window.btoa === 'function') { + return function (str: string) { + return window.btoa(unescape(str)); + }; + } + if (typeof Buffer !== 'undefined') { + return function (str: string) { + return Buffer.from(str).toString('base64'); + }; + } return function (str: string): string { if (process.env.NODE_ENV !== 'production') { logError('Base64 isn\'t natively supported in the current environment.'); From 9b8ec04250dc264f7ad0ed96217fc69635e46718 Mon Sep 17 00:00:00 2001 From: plainheart Date: Sat, 11 Dec 2021 15:41:33 +0800 Subject: [PATCH 093/148] refactor: add `hasGlobalWindow` to the `env` detector & simplify the code. --- src/animation/requestAnimationFrame.ts | 4 +++- src/config.ts | 4 +++- src/core/env.ts | 2 ++ src/svg/Painter.ts | 2 +- src/svg/helper.ts | 5 +++-- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/animation/requestAnimationFrame.ts b/src/animation/requestAnimationFrame.ts index 829e7161f..629ce7616 100644 --- a/src/animation/requestAnimationFrame.ts +++ b/src/animation/requestAnimationFrame.ts @@ -1,9 +1,11 @@ +import env from '../core/env'; + type RequestAnimationFrameType = typeof window.requestAnimationFrame let requestAnimationFrame: RequestAnimationFrameType; requestAnimationFrame = ( - typeof window !== 'undefined' + env.hasGlobalWindow && ( (window.requestAnimationFrame && window.requestAnimationFrame.bind(window)) // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809 diff --git a/src/config.ts b/src/config.ts index 7be4a7898..fe7528abe 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +1,9 @@ +import env from './core/env'; + let dpr = 1; // If in browser environment -if (typeof window !== 'undefined') { +if (env.hasGlobalWindow) { dpr = Math.max( window.devicePixelRatio || (window.screen && (window.screen as any).deviceXDPI / (window.screen as any).logicalXDPI) diff --git a/src/core/env.ts b/src/core/env.ts index fced5aa0b..cd52e3d91 100644 --- a/src/core/env.ts +++ b/src/core/env.ts @@ -23,6 +23,8 @@ class Env { domSupported = false transformSupported = false transform3dSupported = false + + hasGlobalWindow = typeof window !== 'undefined' } const env = new Env(); diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 0ea756fbf..db3829a1a 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -344,7 +344,7 @@ class SVGPainter implements PainterBase { const prefix = 'data:image/svg+xml;'; if (base64) { str = encodeBase64(str); - return str ? prefix + 'base64,' + str : null; + return str && prefix + 'base64,' + str; } return prefix + 'charset=UTF-8,' + str; } diff --git a/src/svg/helper.ts b/src/svg/helper.ts index c80d62ee0..02c1e46c1 100644 --- a/src/svg/helper.ts +++ b/src/svg/helper.ts @@ -2,7 +2,7 @@ import { MatrixArray } from '../core/matrix'; import Transformable, { TransformProp } from '../core/Transformable'; -import { RADIAN_TO_DEGREE, retrieve2, logError } from '../core/util'; +import { RADIAN_TO_DEGREE, retrieve2, logError, isFunction } from '../core/util'; import Displayable from '../graphic/Displayable'; import { GradientObject } from '../graphic/Gradient'; import { LinearGradientObject } from '../graphic/LinearGradient'; @@ -10,6 +10,7 @@ import Path from '../graphic/Path'; import { ImagePatternObject, PatternObject, SVGPatternObject } from '../graphic/Pattern'; import { RadialGradientObject } from '../graphic/RadialGradient'; import { parse } from '../tool/color'; +import env from '../core/env'; const mathRound = Math.round; @@ -172,7 +173,7 @@ export function getSRTTransformString( } export const encodeBase64 = (function () { - if (typeof window !== 'undefined' && typeof window.btoa === 'function') { + if (env.hasGlobalWindow && isFunction(window.btoa)) { return function (str: string) { return window.btoa(unescape(str)); }; From f00ace2421670afa82893ce4941e1d6a9ea33eb8 Mon Sep 17 00:00:00 2001 From: pissang Date: Mon, 13 Dec 2021 22:51:56 +0800 Subject: [PATCH 094/148] feat: support gradient animation --- src/animation/Animator.ts | 149 ++++++++++++++++++++++++++++++++++---- src/core/PathProxy.ts | 2 +- test/animation.html | 11 +++ 3 files changed, 148 insertions(+), 14 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 02147a4cd..826813c56 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -4,15 +4,36 @@ import Clip from './Clip'; import * as color from '../tool/color'; -import {isArrayLike, isFunction, isNumber, keys, logError, map} from '../core/util'; +import {isArrayLike, isFunction, isGradientObject, isNumber, keys, logError, map} from '../core/util'; import {ArrayLike, Dictionary} from '../core/types'; import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; import { createCubicEasingFunc } from './cubicEasing'; +import { LinearGradientObject } from '../export'; +import { isLinearGradient, isRadialGradient } from '../svg/helper'; type NumberArray = ArrayLike type InterpolatableType = string | number | NumberArray | NumberArray[]; +interface ParsedColorStop { + color: number[], + offset: number +}; + +interface ParsedGradientObject { + colorStops: ParsedColorStop[] + x: number + y: number + global: boolean +} +interface ParsedLinearGradientObject extends ParsedGradientObject { + x2: number + y2: number +} +interface ParsedRadialGradientObject extends ParsedGradientObject { + r: number +} + const arraySlice = Array.prototype.slice; function interpolateNumber(p0: number, p1: number, percent: number): number { @@ -29,6 +50,7 @@ function interpolate1DArray( for (let i = 0; i < len; i++) { out[i] = interpolateNumber(p0[i], p1[i], percent); } + return out; } function interpolate2DArray( @@ -48,6 +70,7 @@ function interpolate2DArray( out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent); } } + return out; } function add1DArray( @@ -81,6 +104,22 @@ function add2DArray( } return out; } + +function fillColorStops(val0: ParsedColorStop[], val1: ParsedColorStop[]) { + const len0 = val0.length; + const len1 = val1.length; + + const shorterArr = len0 > len1 ? val1 : val0; + const shorterLen = Math.min(len0, len1); + const last = shorterArr[shorterLen - 1] || { color: [0, 0, 0, 0], offset: 0 }; + for (let i = shorterLen; i < Math.max(len0, len1); i++) { + // Use last color stop to fill the shorter array + shorterArr.push({ + offset: last.offset, + color: last.color.slice() + }); + } +} // arr0 is source array, arr1 is target array. // Do some preprocess to avoid error happened when interpolating from arr0 to arr1 function fillArray( @@ -159,9 +198,10 @@ export function cloneValue(value: InterpolatableType) { } function rgba2String(rgba: number[]): string { - rgba[0] = Math.floor(rgba[0]); - rgba[1] = Math.floor(rgba[1]); - rgba[2] = Math.floor(rgba[2]); + rgba[0] = Math.floor(rgba[0]) || 0; + rgba[1] = Math.floor(rgba[1]) || 0; + rgba[2] = Math.floor(rgba[2]) || 0; + rgba[3] = rgba[3] == null ? 1 : rgba[3]; return 'rgba(' + rgba.join(',') + ')'; } @@ -190,6 +230,12 @@ class Track { // Larger than 0 if value is array arrDim: number = 0 isColor: boolean + /** + * 0: Not gradient + * 1: Linear Gradient + * 2: Radial Gradient + */ + isGradient: 0 | 1 | 2 discrete: boolean = false @@ -274,6 +320,33 @@ class Track { discrete = true; } } + else if (isGradientObject(value)) { + // TODO Color to gradient or gradient to color. + const parsedGradient = { + colorStops: map(value.colorStops, colorStop => ({ + offset: colorStop.offset, + color: color.parse(colorStop.color) + })), + x: (value as LinearGradientObject).x, + y: (value as LinearGradientObject).y + }; + let gradientType: 1 | 2; + if (isLinearGradient(value)) { + gradientType = 1; + (parsedGradient as ParsedLinearGradientObject).x2 = value.x2; + (parsedGradient as ParsedLinearGradientObject).y2 = value.y2; + } + else if (isRadialGradient(value)) { + gradientType = 2; + (parsedGradient as ParsedRadialGradientObject).r = value.r; + } + // Not all gradient + if (len > 0 && this.isGradient !== gradientType) { + discrete = true; + } + value = parsedGradient; + this.isGradient = gradientType; + } else if (typeof value !== 'number' || isNaN(value)) { discrete = true; } @@ -310,18 +383,33 @@ class Track { const arrDim = this.arrDim; const kfsLen = kfs.length; const lastKf = kfs[kfsLen - 1]; + const isDiscrete = this.discrete; - for (let i = 0; i < kfsLen; i++) { - kfs[i].percent = kfs[i].time / maxTime; - - if (arrDim > 0 && i !== kfsLen - 1) { - // Align array with target frame. - fillArray(kfs[i].value as NumberArray, lastKf.value as NumberArray, arrDim); + if (!isDiscrete) { + for (let i = 0; i < kfsLen; i++) { + const kf = kfs[i]; + const value = kf.value; + const lastValue = lastKf.value; + kf.percent = kf.time / maxTime; + if (arrDim > 0 && i !== kfsLen - 1) { + // Align array with target frame. + fillArray(value as NumberArray, lastValue as NumberArray, arrDim); + } + else if (this.isGradient) { + fillColorStops( + (value as ParsedLinearGradientObject).colorStops, + (lastValue as ParsedLinearGradientObject).colorStops + ); + } } } // Only apply additive animaiton on INTERPOLABLE SAME TYPE values. - if (additiveTrack + if ( + !isDiscrete + // TODO support gradient + && !this.isGradient + && additiveTrack // If two track both will be animated and have same value format. && this.needsAnimate() && additiveTrack.needsAnimate() @@ -372,7 +460,7 @@ class Track { // Remove additive track if it's finished. this._additiveTrack = null; } - const isAdditive = this._additiveTrack != null && !this.discrete; + const isAdditive = this._additiveTrack != null; const valueKey = isAdditive ? 'additiveValue' : 'value'; const keyframes = this.keyframes; @@ -380,6 +468,7 @@ class Track { const propName = this.propName; const arrDim = this.arrDim; const isValueColor = this.isColor; + const gradientType = this.isGradient; // Find the range keyframes // kf1-----kf2---------current--------kf3 // find kf2 and kf3 and do interpolation @@ -463,6 +552,41 @@ class Track { w ); } + else if (gradientType) { + const val = frame[valueKey] as ParsedGradientObject; + const nextVal = nextFrame[valueKey] as ParsedGradientObject; + target[propName] = { + type: gradientType === 1 ? 'linear' : 'radial', + x: interpolateNumber(val.x, nextVal.x, w), + y: interpolateNumber(val.x, nextVal.x, w), + // TODO performance + colorStops: map(val.colorStops, (colorStop, idx) => { + const nextColorStop = nextVal.colorStops[idx]; + return { + offset: interpolateNumber(colorStop.offset, nextColorStop.offset, w), + color: rgba2String(interpolate1DArray( + [] as number[], colorStop.color, nextColorStop.color, w + ) as number[]) + }; + }), + global: nextVal.global + }; + if (gradientType === 1) { + // Linear + target[propName].x2 = interpolateNumber( + (val as ParsedLinearGradientObject).x2, (nextVal as ParsedLinearGradientObject).x2, w + ); + target[propName].y2 = interpolateNumber( + (val as ParsedLinearGradientObject).y2, (nextVal as ParsedLinearGradientObject).y2, w + ); + } + else { + // Radial + target[propName].r = interpolateNumber( + (val as ParsedRadialGradientObject).r, (nextVal as ParsedRadialGradientObject).r, w + ); + } + } else if (isValueColor) { interpolate1DArray( targetArr, @@ -755,7 +879,6 @@ export default class Animator { if (minDuration) { maxTime = this._maxTime = Math.max(minDuration, maxTime); } - for (let i = 0; i < this._trackKeys.length; i++) { const propName = this._trackKeys[i]; const track = this._tracks[propName]; diff --git a/src/core/PathProxy.ts b/src/core/PathProxy.ts index fd35e3ade..f0331f947 100644 --- a/src/core/PathProxy.ts +++ b/src/core/PathProxy.ts @@ -11,7 +11,7 @@ import * as vec2 from './vector'; import BoundingRect from './BoundingRect'; import {devicePixelRatio as dpr} from '../config'; import { fromLine, fromCubic, fromQuadratic, fromArc } from './bbox'; -import { cubicAt, cubicLength, cubicSubdivide, quadraticLength, quadraticSubdivide } from './curve'; +import { cubicLength, cubicSubdivide, quadraticLength, quadraticSubdivide } from './curve'; const CMD = { M: 1, diff --git a/test/animation.html b/test/animation.html index 69e436d69..970890f14 100644 --- a/test/animation.html +++ b/test/animation.html @@ -23,6 +23,12 @@ gradient.addColorStop(0, 'red'); gradient.addColorStop(1, 'black'); + + var gradient2 = new zrender.LinearGradient(); + gradient2.addColorStop(0, 'black'); + gradient2.addColorStop(0.5, 'blue'); + gradient2.addColorStop(1, 'white'); + var circle = new zrender.Circle({ position: [100, 100], scale: [1, 1], @@ -52,6 +58,11 @@ position: [200, 0] }) .start(); + circle.animate('style', true) + .when(1000, { + fill: gradient2 + }) + .start(); circle.animate('', true) .when(1000, { position: [200, 0] From 552fcc7d27a15f1bb0712a4357ad6b37d5cf0d5f Mon Sep 17 00:00:00 2001 From: pissang Date: Mon, 13 Dec 2021 23:13:21 +0800 Subject: [PATCH 095/148] fix gradient animation in state transition --- src/Element.ts | 7 +++++-- src/animation/Animator.ts | 27 +++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index adce9e7a6..5419a0892 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -20,7 +20,8 @@ import { logError, mixin, isArrayLike, - isTypedArray + isTypedArray, + isGradientObject } from './core/util'; import Polyline from './graphic/shape/Polyline'; import Group from './graphic/Group'; @@ -1865,7 +1866,9 @@ function animateToShallow( if (needAnimateKey(target, source, innerKey, cfg.force) && (animateAll || (animationProps as Dictionary)[innerKey]) ) { - if (isObject(target[innerKey]) && !isArrayLike(target[innerKey])) { + if (isObject(target[innerKey]) + && !isArrayLike(target[innerKey]) && !isGradientObject(target[innerKey]) + ) { if (topKey) { // logError('Only support 1 depth nest object animation.'); // Assign directly. diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 826813c56..40e9d65dd 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -4,13 +4,14 @@ import Clip from './Clip'; import * as color from '../tool/color'; -import {isArrayLike, isFunction, isGradientObject, isNumber, keys, logError, map} from '../core/util'; +import {extend, isArrayLike, isFunction, isGradientObject, isNumber, keys, logError, map} from '../core/util'; import {ArrayLike, Dictionary} from '../core/types'; import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; import { createCubicEasingFunc } from './cubicEasing'; import { LinearGradientObject } from '../export'; import { isLinearGradient, isRadialGradient } from '../svg/helper'; +import { GradientObject } from '../graphic/Gradient'; type NumberArray = ArrayLike type InterpolatableType = string | number | NumberArray | NumberArray[]; @@ -322,23 +323,17 @@ class Track { } else if (isGradientObject(value)) { // TODO Color to gradient or gradient to color. - const parsedGradient = { - colorStops: map(value.colorStops, colorStop => ({ - offset: colorStop.offset, - color: color.parse(colorStop.color) - })), - x: (value as LinearGradientObject).x, - y: (value as LinearGradientObject).y - }; + const parsedGradient = extend({}, value) as unknown as ParsedGradientObject; + parsedGradient.colorStops = map(value.colorStops, colorStop => ({ + offset: colorStop.offset, + color: color.parse(colorStop.color) + })); let gradientType: 1 | 2; if (isLinearGradient(value)) { gradientType = 1; - (parsedGradient as ParsedLinearGradientObject).x2 = value.x2; - (parsedGradient as ParsedLinearGradientObject).y2 = value.y2; } else if (isRadialGradient(value)) { gradientType = 2; - (parsedGradient as ParsedRadialGradientObject).r = value.r; } // Not all gradient if (len > 0 && this.isGradient !== gradientType) { @@ -1110,6 +1105,14 @@ export default class Animator { if (track.isColor) { val = rgba2String(val as number[]); } + else if (track.isGradient) { + const colorStops = map((val as ParsedGradientObject).colorStops, colorStop => ({ + offset: colorStop.offset, + color: rgba2String(colorStop.color) + })); + val = extend({}, val) as unknown as ParsedGradientObject; + (val as GradientObject).colorStops = colorStops; + } (target as any)[propName] = val; } From b6306a6478b398f70279aa472c114e375f602d39 Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 14 Dec 2021 13:59:37 +0800 Subject: [PATCH 096/148] fix importing from global export --- src/animation/Animator.ts | 1 - src/svg/cssAnimation.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 40e9d65dd..25fae55dc 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -9,7 +9,6 @@ import {ArrayLike, Dictionary} from '../core/types'; import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; import { createCubicEasingFunc } from './cubicEasing'; -import { LinearGradientObject } from '../export'; import { isLinearGradient, isRadialGradient } from '../svg/helper'; import { GradientObject } from '../graphic/Gradient'; diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 3560c9618..b5df70614 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -7,7 +7,7 @@ import PathProxy from '../core/PathProxy'; import { getPathPrecision, getSRTTransformString } from './helper'; import { each, extend, filter, isString, keys } from '../core/util'; import Animator from '../animation/Animator'; -import { CompoundPath } from '../export'; +import CompoundPath from '../graphic/CompoundPath'; import { AnimationEasing } from '../animation/easing'; import { createCubicEasingFunc } from '../animation/cubicEasing'; From 4d55109e37aa40c6e18f4b2a8649856f1f5887f7 Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 14 Dec 2021 14:42:22 +0800 Subject: [PATCH 097/148] fix discrete animation --- src/animation/Animator.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 25fae55dc..ced0662aa 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -379,12 +379,12 @@ class Track { const lastKf = kfs[kfsLen - 1]; const isDiscrete = this.discrete; - if (!isDiscrete) { - for (let i = 0; i < kfsLen; i++) { - const kf = kfs[i]; - const value = kf.value; - const lastValue = lastKf.value; - kf.percent = kf.time / maxTime; + for (let i = 0; i < kfsLen; i++) { + const kf = kfs[i]; + const value = kf.value; + const lastValue = lastKf.value; + kf.percent = kf.time / maxTime; + if (!isDiscrete) { if (arrDim > 0 && i !== kfsLen - 1) { // Align array with target frame. fillArray(value as NumberArray, lastValue as NumberArray, arrDim); From 63affe899ff85e2bef4efc060f0d51e25384581a Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 14 Dec 2021 14:56:15 +0800 Subject: [PATCH 098/148] feat: add Animator#duration --- src/Element.ts | 5 ++++- src/animation/Animator.ts | 29 +++++++++++++++++++---------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index 5419a0892..57475503b 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -1767,7 +1767,10 @@ function animateTo( if (abortedCb) { animator.aborted(abortedCb); } - animator.start(cfg.easing, cfg.force ? cfg.duration : 0); + if (cfg.force) { + animator.duration(cfg.duration); + } + animator.start(cfg.easing); } return animators; diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index ced0662aa..4e7b11430 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -661,6 +661,11 @@ export default class Animator { private _delay: number private _maxTime = 0 + /** + * If force run regardless of empty tracks when duration is set. + */ + private _force: boolean; + /** * If animator is paused * @default false @@ -676,7 +681,6 @@ export default class Animator { * @default false */ private _allowDiscrete: boolean - private _allowDuplicate: boolean private _additiveAnimators: Animator[] @@ -798,6 +802,18 @@ export default class Animator { return !!this._paused; } + /** + * Set duration of animator. + * Will run this duration regardless the track max time or if trackes exits. + * @param duration + * @returns + */ + duration(duration: number) { + this._maxTime = duration; + this._force = true; + return this; + } + private _doneCallback() { this._setTracksFinished(); // Clear clip @@ -854,13 +870,9 @@ export default class Animator { /** * Start the animation * @param easing - * @param minDuration Set min duration of animation. * @return */ - start( - easing?: AnimationEasing, - minDuration?: number - ) { + start(easing?: AnimationEasing) { if (this._started > 0) { return; } @@ -870,9 +882,6 @@ export default class Animator { let tracks: Track[] = []; let maxTime = this._maxTime || 0; - if (minDuration) { - maxTime = this._maxTime = Math.max(minDuration, maxTime); - } for (let i = 0; i < this._trackKeys.length; i++) { const propName = this._trackKeys[i]; const track = this._tracks[propName]; @@ -895,7 +904,7 @@ export default class Animator { } } // Add during callback on the last clip - if (tracks.length || minDuration > 0) { + if (tracks.length || this._force) { const clip = new Clip({ life: maxTime, loop: this._loop, From b3c9512afe5655ca4e75317b7328e25a812ed34e Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 14 Dec 2021 15:17:03 +0800 Subject: [PATCH 099/148] feat(animation): support negative keyframe time. Which means the initial value is before the animation start. --- src/animation/Animator.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 4e7b11430..a23a99560 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -772,11 +772,12 @@ export default class Animator { // zrLog('Invalid property ' + propName); continue; } - // If time is 0 + // If time is <= 0 // Then props is given initialize value + // Note: initial percent can be negative, which means the initial value is before the animation start. // Else // Initialize value from current prop value - if (time !== 0) { + if (time > 0) { track.addKeyframe(0, cloneValue(initialValue), easing); } @@ -880,8 +881,8 @@ export default class Animator { const self = this; - let tracks: Track[] = []; - let maxTime = this._maxTime || 0; + const tracks: Track[] = []; + const maxTime = this._maxTime || 0; for (let i = 0; i < this._trackKeys.length; i++) { const propName = this._trackKeys[i]; const track = this._tracks[propName]; From c6cc31e71277eff8517b125321e83f6bc1597bf7 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 15 Dec 2021 13:46:33 +0800 Subject: [PATCH 100/148] fix(animation): use raw value in discrete animation. --- src/animation/Animator.ts | 218 +++++++++++++--------------- test/animation-additive-simple.html | 1 - 2 files changed, 103 insertions(+), 116 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index a23a99560..12e290fdc 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -4,7 +4,7 @@ import Clip from './Clip'; import * as color from '../tool/color'; -import {extend, isArrayLike, isFunction, isGradientObject, isNumber, keys, logError, map} from '../core/util'; +import {extend, isArrayLike, isFunction, isGradientObject, isNumber, isString, keys, logError, map} from '../core/util'; import {ArrayLike, Dictionary} from '../core/types'; import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; @@ -125,7 +125,7 @@ function fillColorStops(val0: ParsedColorStop[], val1: ParsedColorStop[]) { function fillArray( val0: NumberArray | NumberArray[], val1: NumberArray | NumberArray[], - arrDim: number + arrDim: 1 | 2 ) { // TODO Handling different length TypedArray let arr0 = val0 as (number | number[])[]; @@ -206,36 +206,51 @@ function rgba2String(rgba: number[]): string { return 'rgba(' + rgba.join(',') + ')'; } -function guessArrayDim(value: ArrayLike): number { +function guessArrayDim(value: ArrayLike): 1 | 2 { return isArrayLike(value && (value as ArrayLike)[0]) ? 2 : 1; } +const VALUE_TYPE_NUMBER = 0; +const VALUE_TYPE_1D_ARRAY = 1; +const VALUE_TYPE_2D_ARRAY = 2; +const VALUE_TYPE_COLOR = 3; +const VALUE_TYPE_LINEAR_GRADIENT = 4; +const VALUE_TYPE_RADIAL_GRADIENT = 5; +// Other value type that can only use discrete animation. +const VALUE_TYPE_UNKOWN = 6; + +type ValueType = 0 | 1 | 2 | 3 | 4 | 5 | 6; + type Keyframe = { time: number value: unknown percent: number + // Raw value for discrete animation. + rawValue: unknown easing?: AnimationEasing // Raw easing easingFunc?: (percent: number) => number additiveValue?: unknown } + +function isGradientValueType(valType: ValueType): valType is 4 | 5 { + return valType === VALUE_TYPE_LINEAR_GRADIENT || valType === VALUE_TYPE_RADIAL_GRADIENT; +} +function isArrayValueType(valType: ValueType): valType is 1 | 2 { + return valType === VALUE_TYPE_1D_ARRAY || valType === VALUE_TYPE_2D_ARRAY; +} + + let tmpRgba: number[] = [0, 0, 0, 0]; + class Track { keyframes: Keyframe[] = [] propName: string - // Larger than 0 if value is array - arrDim: number = 0 - isColor: boolean - /** - * 0: Not gradient - * 1: Linear Gradient - * 2: Radial Gradient - */ - isGradient: 0 | 1 | 2 + valType: ValueType discrete: boolean = false @@ -284,73 +299,68 @@ class Track { return this._additiveTrack; } - addKeyframe(time: number, value: unknown, easing?: AnimationEasing) { + addKeyframe(time: number, rawValue: unknown, easing?: AnimationEasing) { this._needsSort = true; let keyframes = this.keyframes; let len = keyframes.length; let discrete = false; + let valType: ValueType = VALUE_TYPE_UNKOWN; + let value = rawValue; // Handling values only if it's possible to be interpolated. - if (isArrayLike(value)) { - let arrayDim = guessArrayDim(value); - if (len > 0 && this.arrDim !== arrayDim) { // Two values has differnt dimension. - discrete = true; - } + if (isArrayLike(rawValue)) { + let arrayDim = guessArrayDim(rawValue); + valType = arrayDim; // Not a number array. - if (arrayDim === 1 && !isNumber(value[0]) - || arrayDim === 2 && !isNumber(value[0][0])) { + if (arrayDim === 1 && !isNumber(rawValue[0]) + || arrayDim === 2 && !isNumber(rawValue[0][0])) { discrete = true; } - this.arrDim = arrayDim; } else { - if (this.arrDim > 0) { // Previous value is array. - discrete = true; + if (!isNaN(+rawValue)) { + valType = VALUE_TYPE_NUMBER; } - - if (typeof value === 'string') { - const colorArray = color.parse(value); + else if (isString(rawValue)) { + const colorArray = color.parse(rawValue); if (colorArray) { value = colorArray; - this.isColor = true; - } - else { - discrete = true; + valType = VALUE_TYPE_COLOR; } } - else if (isGradientObject(value)) { + else if (isGradientObject(rawValue)) { // TODO Color to gradient or gradient to color. const parsedGradient = extend({}, value) as unknown as ParsedGradientObject; - parsedGradient.colorStops = map(value.colorStops, colorStop => ({ + parsedGradient.colorStops = map(rawValue.colorStops, colorStop => ({ offset: colorStop.offset, color: color.parse(colorStop.color) })); - let gradientType: 1 | 2; - if (isLinearGradient(value)) { - gradientType = 1; - } - else if (isRadialGradient(value)) { - gradientType = 2; + if (isLinearGradient(rawValue)) { + valType = VALUE_TYPE_LINEAR_GRADIENT; } - // Not all gradient - if (len > 0 && this.isGradient !== gradientType) { - discrete = true; + else if (isRadialGradient(rawValue)) { + valType = VALUE_TYPE_RADIAL_GRADIENT; } value = parsedGradient; - this.isGradient = gradientType; - } - else if (typeof value !== 'number' || isNaN(value)) { - discrete = true; } } + if (len === 0) { + // Inference type from the first keyframe. + this.valType = valType; + } + else if (valType !== this.valType) { // Not same value type. + discrete = true; + } + this.discrete = this.discrete || discrete; const kf: Keyframe = { time, value, + rawValue, percent: 0 }; if (easing) { @@ -374,10 +384,12 @@ class Track { }); } - const arrDim = this.arrDim; + const valType = this.valType; const kfsLen = kfs.length; const lastKf = kfs[kfsLen - 1]; const isDiscrete = this.discrete; + const isArr = isArrayValueType(valType); + const isGradient = isGradientValueType(valType); for (let i = 0; i < kfsLen; i++) { const kf = kfs[i]; @@ -385,11 +397,11 @@ class Track { const lastValue = lastKf.value; kf.percent = kf.time / maxTime; if (!isDiscrete) { - if (arrDim > 0 && i !== kfsLen - 1) { + if (isArr && i !== kfsLen - 1) { // Align array with target frame. - fillArray(value as NumberArray, lastValue as NumberArray, arrDim); + fillArray(value as NumberArray, lastValue as NumberArray, valType); } - else if (this.isGradient) { + else if (isGradient) { fillColorStops( (value as ParsedLinearGradientObject).colorStops, (lastValue as ParsedLinearGradientObject).colorStops @@ -402,13 +414,12 @@ class Track { if ( !isDiscrete // TODO support gradient - && !this.isGradient + && valType !== VALUE_TYPE_RADIAL_GRADIENT && additiveTrack // If two track both will be animated and have same value format. && this.needsAnimate() && additiveTrack.needsAnimate() - && arrDim === additiveTrack.arrDim - && this.isColor === additiveTrack.isColor + && valType === additiveTrack.valType && !additiveTrack._finished ) { this._additiveTrack = additiveTrack; @@ -416,30 +427,17 @@ class Track { const startValue = kfs[0].value; // Calculate difference for (let i = 0; i < kfsLen; i++) { - if (arrDim === 0) { - if (this.isColor) { - kfs[i].additiveValue = - add1DArray([], kfs[i].value as NumberArray, startValue as NumberArray, -1); - } - else { - kfs[i].additiveValue = kfs[i].value as number - (startValue as number); - } + if (valType === VALUE_TYPE_NUMBER) { + kfs[i].additiveValue = kfs[i].value as number - (startValue as number); } - else if (arrDim === 1) { - kfs[i].additiveValue = add1DArray( - [], - kfs[i].value as NumberArray, - startValue as NumberArray, - -1 - ); + else if (valType === VALUE_TYPE_COLOR) { + kfs[i].additiveValue = + add1DArray([], kfs[i].value as NumberArray, startValue as NumberArray, -1); } - else if (arrDim === 2) { - kfs[i].additiveValue = add2DArray( - [], - kfs[i].value as NumberArray[], - startValue as NumberArray[], - -1 - ); + else if (isArrayValueType(valType)) { + kfs[i].additiveValue = valType === VALUE_TYPE_1D_ARRAY + ? add1DArray([], kfs[i].value as NumberArray, startValue as NumberArray, -1) + : add2DArray([], kfs[i].value as NumberArray[], startValue as NumberArray[], -1); } } } @@ -457,12 +455,11 @@ class Track { const isAdditive = this._additiveTrack != null; const valueKey = isAdditive ? 'additiveValue' : 'value'; + const valType = this.valType; const keyframes = this.keyframes; const kfsNum = keyframes.length; const propName = this.propName; - const arrDim = this.arrDim; - const isValueColor = this.isColor; - const gradientType = this.isGradient; + const isValueColor = valType === VALUE_TYPE_COLOR; // Find the range keyframes // kf1-----kf2---------current--------kf3 // find kf2 and kf3 and do interpolation @@ -524,15 +521,16 @@ class Track { let targetArr = isAdditive ? this._additiveValue : (isValueColor ? tmpRgba : target[propName]); - if ((arrDim > 0 || isValueColor) && !targetArr) { + if ((isArrayValueType(valType) || isValueColor) && !targetArr) { targetArr = this._additiveValue = []; } if (this.discrete) { - target[propName] = w < 1 ? frame[valueKey] : nextFrame[valueKey]; + // use raw value without parse in discrete animation. + target[propName] = w < 1 ? frame.rawValue : nextFrame.rawValue; } - else if (arrDim > 0) { - arrDim === 1 + else if (isArrayValueType(valType)) { + valType === VALUE_TYPE_1D_ARRAY ? interpolate1DArray( targetArr as NumberArray, frame[valueKey] as NumberArray, @@ -546,11 +544,12 @@ class Track { w ); } - else if (gradientType) { + else if (isGradientValueType(valType)) { const val = frame[valueKey] as ParsedGradientObject; const nextVal = nextFrame[valueKey] as ParsedGradientObject; + const isLinearGradient = valType === VALUE_TYPE_LINEAR_GRADIENT; target[propName] = { - type: gradientType === 1 ? 'linear' : 'radial', + type: isLinearGradient ? 'linear' : 'radial', x: interpolateNumber(val.x, nextVal.x, w), y: interpolateNumber(val.x, nextVal.x, w), // TODO performance @@ -565,7 +564,7 @@ class Track { }), global: nextVal.global }; - if (gradientType === 1) { + if (isLinearGradient) { // Linear target[propName].x2 = interpolateNumber( (val as ParsedLinearGradientObject).x2, (nextVal as ParsedLinearGradientObject).x2, w @@ -609,26 +608,24 @@ class Track { } private _addToTarget(target: any) { - const arrDim = this.arrDim; + const valType = this.valType; const propName = this.propName; const additiveValue = this._additiveValue; - if (arrDim === 0) { - if (this.isColor) { - // TODO reduce unnecessary parse - color.parse(target[propName], tmpRgba); - add1DArray(tmpRgba, tmpRgba, additiveValue as NumberArray, 1); - target[propName] = rgba2String(tmpRgba); - } - else { - // Add a difference value based on the change of previous frame. - target[propName] = target[propName] + additiveValue; - } + if (valType === VALUE_TYPE_NUMBER) { + // Add a difference value based on the change of previous frame. + target[propName] = target[propName] + additiveValue; } - else if (arrDim === 1) { + else if (valType === VALUE_TYPE_COLOR) { + // TODO reduce unnecessary parse + color.parse(target[propName], tmpRgba); + add1DArray(tmpRgba, tmpRgba, additiveValue as NumberArray, 1); + target[propName] = rgba2String(tmpRgba); + } + else if (valType === VALUE_TYPE_1D_ARRAY) { add1DArray(target[propName], target[propName], additiveValue as NumberArray, 1); } - else if (arrDim === 2) { + else if (valType === VALUE_TYPE_2D_ARRAY) { add2DArray(target[propName], target[propName], additiveValue as NumberArray[], 1); } } @@ -756,10 +753,11 @@ export default class Animator { let initialValue; const additiveTrack = this._getAdditiveTrack(propName); if (additiveTrack) { - const lastFinalKf = additiveTrack.keyframes[additiveTrack.keyframes.length - 1]; + const addtiveTrackKfs = additiveTrack.keyframes; + const lastFinalKf = addtiveTrackKfs[addtiveTrackKfs.length - 1]; // Use the last state of additived animator. initialValue = lastFinalKf && lastFinalKf.value; - if (additiveTrack.isColor && initialValue) { + if (additiveTrack.valType === VALUE_TYPE_COLOR && initialValue) { // Convert to rgba string initialValue = rgba2String(initialValue as number[]); } @@ -896,8 +894,10 @@ export default class Animator { const lastKf = kfs[kfsNum - 1]; // Set final value. if (lastKf) { - (self._target as any)[track.propName] = lastKf.value; + // use raw value without parse. + (self._target as any)[track.propName] = lastKf.rawValue; } + track.setFinished(); } else { tracks.push(track); @@ -1110,20 +1110,8 @@ export default class Animator { const kf = kfs[firstOrLast ? 0 : kfs.length - 1]; if (kf) { // TODO CLONE? - let val: unknown = cloneValue(kf.value as any); - if (track.isColor) { - val = rgba2String(val as number[]); - } - else if (track.isGradient) { - const colorStops = map((val as ParsedGradientObject).colorStops, colorStop => ({ - offset: colorStop.offset, - color: rgba2String(colorStop.color) - })); - val = extend({}, val) as unknown as ParsedGradientObject; - (val as GradientObject).colorStops = colorStops; - } - - (target as any)[propName] = val; + // Use raw value without parse. + (target as any)[propName] = cloneValue(kf.rawValue as any); } } } diff --git a/test/animation-additive-simple.html b/test/animation-additive-simple.html index 00724e609..a09ea5d4b 100644 --- a/test/animation-additive-simple.html +++ b/test/animation-additive-simple.html @@ -75,7 +75,6 @@ hexogonNormal.animateTo({ rotation, - scale, scaleX: scale, scaleY: scale }, { From 7117eb884efa751c9cb0a8c926f05de5a12b025a Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 15 Dec 2021 15:53:10 +0800 Subject: [PATCH 101/148] fix(svg): fix css animation --- src/Handler.ts | 1 - src/animation/Animator.ts | 1 - src/svg/cssAnimation.ts | 17 ++++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Handler.ts b/src/Handler.ts index f41f87e53..8acf0522f 100644 --- a/src/Handler.ts +++ b/src/Handler.ts @@ -131,7 +131,6 @@ const handlerNames = [ type HandlerName = 'click' |'dblclick' |'mousewheel' |'mouseout' | 'mouseup' |'mousedown' |'mousemove' |'contextmenu'; - // TODO draggable class Handler extends Eventful { diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 12e290fdc..98083b256 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -10,7 +10,6 @@ import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; import { createCubicEasingFunc } from './cubicEasing'; import { isLinearGradient, isRadialGradient } from '../svg/helper'; -import { GradientObject } from '../graphic/Gradient'; type NumberArray = ArrayLike type InterpolatableType = string | number | NumberArray | NumberArray[]; diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index b5df70614..10f00c791 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -5,7 +5,7 @@ import Path from '../graphic/Path'; import SVGPathRebuilder from './SVGPathRebuilder'; import PathProxy from '../core/PathProxy'; import { getPathPrecision, getSRTTransformString } from './helper'; -import { each, extend, filter, isString, keys } from '../core/util'; +import { each, extend, filter, isNumber, isString, keys } from '../core/util'; import Animator from '../animation/Animator'; import CompoundPath from '../graphic/CompoundPath'; import { AnimationEasing } from '../animation/easing'; @@ -219,14 +219,17 @@ export function createCSSAnimation( const kf = kfs[i]; const percent = Math.round(kf.time / maxTime * 100) + '%'; const kfEasing = getEasingFunc(kf.easing); + const rawValue = kf.rawValue; - cssKfs[percent] = cssKfs[percent] || {}; - cssKfs[percent][attrName] = - track.isColor ? col2str(kf.value as any) : kf.value; + // TODO gradient + if (isString(rawValue) || isNumber(rawValue)) { + cssKfs[percent] = cssKfs[percent] || {}; + cssKfs[percent][attrName] = kf.rawValue; - if (kfEasing) { - // TODO. If different property have different easings. - cssKfs[percent][animationTimingFunctionAttrName] = kfEasing; + if (kfEasing) { + // TODO. If different property have different easings. + cssKfs[percent][animationTimingFunctionAttrName] = kfEasing; + } } } } From 000c53dae6be29fb7ad57fd22d887fe37f85ebf7 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 15 Dec 2021 16:17:12 +0800 Subject: [PATCH 102/148] revert an unsure change. --- src/animation/Animator.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 98083b256..285912232 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -731,8 +731,9 @@ export default class Animator { /** * Set Animation keyframe - * @param time 关键帧时间,单位是ms - * @param props 关键帧的属性值,key-value表示 + * @param time time of keyframe in ms + * @param props key-value props of keyframe. + * @param easing */ when(time: number, props: Dictionary, easing?: AnimationEasing) { return this.whenWithKeys(time, props, keys(props) as string[], easing); @@ -1060,8 +1061,6 @@ export default class Animator { else if (this._started === 1) { track.step(this._target, 0); } - // Set track to finished - track.setFinished(); } } let allAborted = true; From 3e57c586a72a8d1a1d9ae13c08dce56d8a659663 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 15 Dec 2021 16:20:29 +0800 Subject: [PATCH 103/148] fix linting issues from deepscan --- src/canvas/dashStyle.ts | 1 - src/svg/cssAnimation.ts | 8 -------- 2 files changed, 9 deletions(-) diff --git a/src/canvas/dashStyle.ts b/src/canvas/dashStyle.ts index e57ca85bc..657eb36d4 100644 --- a/src/canvas/dashStyle.ts +++ b/src/canvas/dashStyle.ts @@ -6,7 +6,6 @@ export function normalizeLineDash(lineType: any, lineWidth?: number): number[] | if (!lineType || lineType === 'solid' || !(lineWidth > 0)) { return null; } - lineWidth = lineWidth || 1; return lineType === 'dashed' ? [4 * lineWidth, 2 * lineWidth] : lineType === 'dotted' diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 10f00c791..38fe5cfe3 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -52,14 +52,6 @@ function buildPathString(el: Path, kfShape: Path['shape'], path: PathProxy) { return svgPathBuilder.getStr(); } -function col2str(rgba: number[]): string { - rgba[0] = Math.floor(rgba[0]); - rgba[1] = Math.floor(rgba[1]); - rgba[2] = Math.floor(rgba[2]); - - return `rgba(${rgba.join(',')})`; -} - function setTransformOrigin(target: Record, transform: Transformable) { const {originX, originY} = transform; if (originX || originY) { From 582bf5dd5cc3f5efe0bee913b36fe2705613c042 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 15 Dec 2021 17:06:45 +0800 Subject: [PATCH 104/148] clean code --- src/Element.ts | 61 +++++++++++++++++++++++---------------- src/animation/Animator.ts | 2 ++ 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index 57475503b..065659af4 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -21,7 +21,8 @@ import { mixin, isArrayLike, isTypedArray, - isGradientObject + isGradientObject, + filter } from './core/util'; import Polyline from './graphic/shape/Polyline'; import Group from './graphic/Group'; @@ -1628,13 +1629,15 @@ class Element { const elProto = Element.prototype; elProto.type = 'element'; elProto.name = ''; - elProto.ignore = false; - elProto.silent = false; - elProto.isGroup = false; - elProto.draggable = false; - elProto.dragging = false; - elProto.ignoreClip = false; + + elProto.ignore = + elProto.silent = + elProto.isGroup = + elProto.draggable = + elProto.dragging = + elProto.ignoreClip = elProto.__inHover = false; + elProto.__dirty = REDRAW_BIT; @@ -1654,7 +1657,9 @@ class Element { ) { Object.defineProperty(elProto, key, { get() { - logDeprecatedError(key, xKey, yKey); + if (process.env.NODE_ENV !== 'production') { + logDeprecatedError(key, xKey, yKey); + } if (!this[privateKey]) { const pos: number[] = this[privateKey] = []; enhanceArray(this, pos); @@ -1662,7 +1667,9 @@ class Element { return this[privateKey]; }, set(pos: number[]) { - logDeprecatedError(key, xKey, yKey); + if (process.env.NODE_ENV !== 'production') { + logDeprecatedError(key, xKey, yKey); + } this[xKey] = pos[0]; this[yKey] = pos[1]; this[privateKey] = pos; @@ -1688,7 +1695,10 @@ class Element { }); } } - if (Object.defineProperty && (!(env as any).browser.ie || (env as any).browser.version > 8)) { + if (Object.defineProperty + // Just don't support ie8 + // && (!(env as any).browser.ie || (env as any).browser.version > 8) + ) { createLegacyProperty('position', '_legacyPos', 'x', 'y'); createLegacyProperty('scale', '_legacyScale', 'scaleX', 'scaleY'); createLegacyProperty('origin', '_legacyOrigin', 'originX', 'originY'); @@ -1870,7 +1880,8 @@ function animateToShallow( && (animateAll || (animationProps as Dictionary)[innerKey]) ) { if (isObject(target[innerKey]) - && !isArrayLike(target[innerKey]) && !isGradientObject(target[innerKey]) + && !isArrayLike(target[innerKey]) + && !isGradientObject(target[innerKey]) ) { if (topKey) { // logError('Only support 1 depth nest object animation.'); @@ -1918,22 +1929,18 @@ function animateToShallow( ) { // Find last animator animating same prop. const existsAnimators = animatable.animators; - let existsAnimatorsOnSameTarget: Animator[] = []; - for (let i = 0; i < existsAnimators.length; i++) { - // Use key string instead object reference because ref may be changed. - if (existsAnimators[i].targetName === topKey) { - existsAnimatorsOnSameTarget.push(existsAnimators[i]); - } - } - if (!additive && existsAnimatorsOnSameTarget.length) { + if (!additive) { // Stop exists animation on specific tracks. Only one animator available for each property. // TODO Should invoke previous animation callback? - for (let i = 0; i < existsAnimatorsOnSameTarget.length; i++) { - const allAborted = existsAnimatorsOnSameTarget[i].stopTracks(changedKeys); - if (allAborted) { // This animator can't be used. - const idx = indexOf(existsAnimators, existsAnimatorsOnSameTarget[i]); - existsAnimators.splice(idx, 1); + for (let i = 0; i < existsAnimators.length; i++) { + const animator = existsAnimators[i]; + if (animator.targetName === topKey) { + const allAborted = existsAnimators[i].stopTracks(changedKeys); + if (allAborted) { // This animator can't be used. + const idx = indexOf(existsAnimators, existsAnimators[i]); + existsAnimators.splice(idx, 1); + } } } } @@ -1975,7 +1982,11 @@ function animateToShallow( } } - const animator = new Animator(source, false, false, additive ? existsAnimatorsOnSameTarget : null); + const animator = new Animator(source, false, false, additive ? filter( + // Use key string instead object reference because ref may be changed. + existsAnimators, animator => animator.targetName === topKey + ) : null); + animator.targetName = topKey; if (cfg.scope) { animator.scope = cfg.scope; diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index 285912232..e604db4f9 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -1061,6 +1061,8 @@ export default class Animator { else if (this._started === 1) { track.step(this._target, 0); } + // Set track to finished + track.setFinished(); } } let allAborted = true; From 826ce2f1594e72d16a575e9a97e8b6e3863fd8cd Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 15 Dec 2021 17:28:06 +0800 Subject: [PATCH 105/148] fix previous tracks may not stopped when animation values are same --- src/Element.ts | 98 +++++++++++++++++++++------------------ src/animation/Animator.ts | 13 ------ 2 files changed, 54 insertions(+), 57 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index 065659af4..4e254f80e 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -1,6 +1,6 @@ import Transformable from './core/Transformable'; import { AnimationEasing } from './animation/easing'; -import Animator, {cloneValue, is1DArraySame} from './animation/Animator'; +import Animator, {cloneValue} from './animation/Animator'; import { ZRenderType } from './zrender'; import { Dictionary, ElementEventName, ZRRawEvent, BuiltinTextPosition, AllPropTypes, @@ -1839,26 +1839,29 @@ function copyValue(target: Dictionary, source: Dictionary, key: string } } -function needAnimateKey(target: Dictionary, source: Dictionary, key: string, force: boolean) { - const sourceVal = source[key]; - const targetVal = target[key]; - return !( - // Can't animate between null value. assign directly. For example. stroke animate from #fff to null. - sourceVal == null - || targetVal == null - // Not animate not changed value if not using force. - || !force && ( - sourceVal === targetVal - // Only check 1 dimension array - || isArrayLike(sourceVal) && isArrayLike(targetVal) && is1DArraySame(sourceVal, targetVal) - ) - ); +function isValueSame(val1: any, val2: any) { + return val1 === val2 + // Only check 1 dimension array + || isArrayLike(val1) && isArrayLike(val2) && is1DArraySame(val1, val2); +} + +function is1DArraySame(arr0: ArrayLike, arr1: ArrayLike) { + const len = arr0.length; + if (len !== arr1.length) { + return false; + } + for (let i = 0; i < len; i++) { + if (arr0[i] !== arr1[i]) { + return false; + } + } + return true; } function animateToShallow( animatable: Element, topKey: string, - source: Dictionary, + animateObj: Dictionary, target: Dictionary, cfg: ElementAnimateConfig, animationProps: Dictionary | true, @@ -1876,7 +1879,9 @@ function animateToShallow( for (let k = 0; k < targetKeys.length; k++) { const innerKey = targetKeys[k] as string; - if (needAnimateKey(target, source, innerKey, cfg.force) + if ( + target[innerKey] != null + && animateObj[innerKey] != null && (animateAll || (animationProps as Dictionary)[innerKey]) ) { if (isObject(target[innerKey]) @@ -1888,7 +1893,7 @@ function animateToShallow( // Assign directly. // TODO richText? if (!reverse) { - source[innerKey] = target[innerKey]; + animateObj[innerKey] = target[innerKey]; animatable.updateDuringAnimation(topKey); } continue; @@ -1896,7 +1901,7 @@ function animateToShallow( animateToShallow( animatable, innerKey, - source[innerKey], + animateObj[innerKey], target[innerKey], cfg, animationProps && (animationProps as Dictionary)[innerKey], @@ -1905,13 +1910,16 @@ function animateToShallow( ); } else { - animatableKeys.push(innerKey); + // Not animate not changed value if not using force. + if (cfg.force || !isValueSame(target[innerKey], animateObj[innerKey])) { + animatableKeys.push(innerKey); + } changedKeys.push(innerKey); } } else if (!reverse) { // Assign target value directly. - source[innerKey] = target[innerKey]; + animateObj[innerKey] = target[innerKey]; animatable.updateDuringAnimation(topKey); // Previous animation will be stopped on the changed keys. // So direct assign is also included. @@ -1921,30 +1929,32 @@ function animateToShallow( const keyLen = animatableKeys.length; + + // Find last animator animating same prop. + const existsAnimators = animatable.animators; + + // Stop previous animations on the same property. + if (!additive && changedKeys.length) { + // Stop exists animation on specific tracks. Only one animator available for each property. + // TODO Should invoke previous animation callback? + for (let i = 0; i < existsAnimators.length; i++) { + const animator = existsAnimators[i]; + if (animator.targetName === topKey) { + const allAborted = existsAnimators[i].stopTracks(changedKeys); + if (allAborted) { // This animator can't be used. + const idx = indexOf(existsAnimators, existsAnimators[i]); + existsAnimators.splice(idx, 1); + } + } + } + } + if (keyLen > 0 // cfg.force is mainly for keep invoking onframe and ondone callback even if animation is not necessary. // So if there is already has animators. There is no need to create another animator if not necessary. // Or it will always add one more with empty target. || (cfg.force && !animators.length) ) { - // Find last animator animating same prop. - const existsAnimators = animatable.animators; - - if (!additive) { - // Stop exists animation on specific tracks. Only one animator available for each property. - // TODO Should invoke previous animation callback? - for (let i = 0; i < existsAnimators.length; i++) { - const animator = existsAnimators[i]; - if (animator.targetName === topKey) { - const allAborted = existsAnimators[i].stopTracks(changedKeys); - if (allAborted) { // This animator can't be used. - const idx = indexOf(existsAnimators, existsAnimators[i]); - existsAnimators.splice(idx, 1); - } - } - } - } - let revertedSource: Dictionary; let reversedTarget: Dictionary; let sourceClone: Dictionary; @@ -1955,7 +1965,7 @@ function animateToShallow( } for (let i = 0; i < keyLen; i++) { const innerKey = animatableKeys[i]; - reversedTarget[innerKey] = source[innerKey]; + reversedTarget[innerKey] = animateObj[innerKey]; if (setToFinal) { revertedSource[innerKey] = target[innerKey]; } @@ -1966,7 +1976,7 @@ function animateToShallow( // to prevent the "final" values from being read in any other places (like other running // animator during callbacks). // But if `setToFinal: true` this feature can not be satisfied. - source[innerKey] = target[innerKey]; + animateObj[innerKey] = target[innerKey]; } } } @@ -1975,14 +1985,14 @@ function animateToShallow( for (let i = 0; i < keyLen; i++) { const innerKey = animatableKeys[i]; // NOTE: Must clone source after the stopTracks. The property may be modified in stopTracks. - sourceClone[innerKey] = cloneValue(source[innerKey]); + sourceClone[innerKey] = cloneValue(animateObj[innerKey]); // Use copy, not change the original reference // Copy from target to source. - copyValue(source, target, innerKey); + copyValue(animateObj, target, innerKey); } } - const animator = new Animator(source, false, false, additive ? filter( + const animator = new Animator(animateObj, false, false, additive ? filter( // Use key string instead object reference because ref may be changed. existsAnimators, animator => animator.targetName === topKey ) : null); diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index e604db4f9..c87872a15 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -166,19 +166,6 @@ function fillArray( } } -export function is1DArraySame(arr0: NumberArray, arr1: NumberArray) { - const len = arr0.length; - if (len !== arr1.length) { - return false; - } - for (let i = 0; i < len; i++) { - if (arr0[i] !== arr1[i]) { - return false; - } - } - return true; -} - export function cloneValue(value: InterpolatableType) { if (isArrayLike(value)) { const len = value.length; From 571de74295d44ff49b05f7ef61c0bb73c061f6b9 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 15 Dec 2021 20:03:45 +0800 Subject: [PATCH 106/148] fix(animation): fix animateTo twice will cause animation lost --- src/Element.ts | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index 4e254f80e..734c82cb6 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -29,7 +29,6 @@ import Group from './graphic/Group'; import Point from './core/Point'; import { LIGHT_LABEL_COLOR, DARK_LABEL_COLOR } from './config'; import { parse, stringify } from './tool/color'; -import env from './core/env'; import { REDRAW_BIT } from './graphic/constants'; export interface ElementAnimateConfig { @@ -1868,14 +1867,16 @@ function animateToShallow( animators: Animator[], reverse: boolean // If `true`, animate from the `target` to current state. ) { - const animatableKeys: string[] = []; - const changedKeys: string[] = []; const targetKeys = keys(target); const duration = cfg.duration; const delay = cfg.delay; const additive = cfg.additive; const setToFinal = cfg.setToFinal; const animateAll = !isObject(animationProps); + // Find last animator animating same prop. + const existsAnimators = animatable.animators; + + let animationKeys: string[] = []; for (let k = 0; k < targetKeys.length; k++) { const innerKey = targetKeys[k] as string; @@ -1910,11 +1911,7 @@ function animateToShallow( ); } else { - // Not animate not changed value if not using force. - if (cfg.force || !isValueSame(target[innerKey], animateObj[innerKey])) { - animatableKeys.push(innerKey); - } - changedKeys.push(innerKey); + animationKeys.push(innerKey); } } else if (!reverse) { @@ -1923,32 +1920,35 @@ function animateToShallow( animatable.updateDuringAnimation(topKey); // Previous animation will be stopped on the changed keys. // So direct assign is also included. - changedKeys.push(innerKey); + animationKeys.push(innerKey); } } - const keyLen = animatableKeys.length; - - - // Find last animator animating same prop. - const existsAnimators = animatable.animators; - + let keyLen = animationKeys.length; // Stop previous animations on the same property. - if (!additive && changedKeys.length) { + if (!additive && keyLen) { // Stop exists animation on specific tracks. Only one animator available for each property. // TODO Should invoke previous animation callback? for (let i = 0; i < existsAnimators.length; i++) { const animator = existsAnimators[i]; if (animator.targetName === topKey) { - const allAborted = existsAnimators[i].stopTracks(changedKeys); + const allAborted = animator.stopTracks(animationKeys); if (allAborted) { // This animator can't be used. - const idx = indexOf(existsAnimators, existsAnimators[i]); + const idx = indexOf(existsAnimators, animator); existsAnimators.splice(idx, 1); } } } } + // Ignore values not changed. + // NOTE: Must filter it after previous animation stopped + // and make sure the value to compare is using initial frame if animation is not started yet when setToFinal is used. + if (!cfg.force) { + animationKeys = filter(animationKeys, key => !isValueSame(target[key], animateObj[key])); + keyLen = animationKeys.length; + } + if (keyLen > 0 // cfg.force is mainly for keep invoking onframe and ondone callback even if animation is not necessary. // So if there is already has animators. There is no need to create another animator if not necessary. @@ -1964,7 +1964,7 @@ function animateToShallow( revertedSource = {}; } for (let i = 0; i < keyLen; i++) { - const innerKey = animatableKeys[i]; + const innerKey = animationKeys[i]; reversedTarget[innerKey] = animateObj[innerKey]; if (setToFinal) { revertedSource[innerKey] = target[innerKey]; @@ -1983,7 +1983,7 @@ function animateToShallow( else if (setToFinal) { sourceClone = {}; for (let i = 0; i < keyLen; i++) { - const innerKey = animatableKeys[i]; + const innerKey = animationKeys[i]; // NOTE: Must clone source after the stopTracks. The property may be modified in stopTracks. sourceClone[innerKey] = cloneValue(animateObj[innerKey]); // Use copy, not change the original reference @@ -2003,16 +2003,16 @@ function animateToShallow( } if (setToFinal && revertedSource) { - animator.whenWithKeys(0, revertedSource, animatableKeys); + animator.whenWithKeys(0, revertedSource, animationKeys); } if (sourceClone) { - animator.whenWithKeys(0, sourceClone, animatableKeys); + animator.whenWithKeys(0, sourceClone, animationKeys); } animator.whenWithKeys( duration == null ? 500 : duration, reverse ? reversedTarget : target, - animatableKeys + animationKeys ).delay(delay || 0); animatable.addAnimator(animator, topKey); From a168ab40cbc926cddbf46341f83b9b43e81a5deb Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 17 Dec 2021 12:09:01 +0800 Subject: [PATCH 107/148] remove `pathToImage` --- src/PainterBase.ts | 1 - src/canvas/Painter.ts | 62 --------------------------------------- src/svg-legacy/Painter.ts | 1 - src/svg/Painter.ts | 1 - src/zrender.ts | 10 ------- zrender | 1 + 6 files changed, 1 insertion(+), 75 deletions(-) create mode 120000 zrender diff --git a/src/PainterBase.ts b/src/PainterBase.ts index c41fbd178..3b7100abb 100644 --- a/src/PainterBase.ts +++ b/src/PainterBase.ts @@ -38,7 +38,6 @@ export interface PainterBase { getViewportRootOffset: () => {offsetLeft: number, offsetTop: number} refreshHover(): void - pathToImage(e: Path, dpr: number): ZRImage configLayer(zlevel: number, config: Dictionary): void setBackgroundColor(backgroundColor: string | GradientObject | PatternObject): void diff --git a/src/canvas/Painter.ts b/src/canvas/Painter.ts index b125739d8..691e1a275 100644 --- a/src/canvas/Painter.ts +++ b/src/canvas/Painter.ts @@ -976,66 +976,4 @@ export default class CanvasPainter implements PainterBase { getHeight() { return this._height; } - - pathToImage(path: Path, dpr?: number): ZRImage { - dpr = dpr || this.dpr; - - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - const rect = path.getBoundingRect(); - const style = path.style; - const shadowBlurSize = style.shadowBlur * dpr; - const shadowOffsetX = style.shadowOffsetX * dpr; - const shadowOffsetY = style.shadowOffsetY * dpr; - const lineWidth = path.hasStroke() ? style.lineWidth : 0; - - const leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize); - const rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize); - const topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize); - const bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize); - const width = rect.width + leftMargin + rightMargin; - const height = rect.height + topMargin + bottomMargin; - - canvas.width = width * dpr; - canvas.height = height * dpr; - - ctx.scale(dpr, dpr); - ctx.clearRect(0, 0, width, height); - (ctx as ZRCanvasRenderingContext).dpr = dpr; - - const pathTransform = { - x: path.x, - y: path.y, - scaleX: path.scaleX, - scaleY: path.scaleY, - rotation: path.rotation, - originX: path.originX, - originY: path.originY - }; - path.x = leftMargin - rect.x; - path.y = topMargin - rect.y; - path.rotation = 0; - path.scaleX = 1; - path.scaleY = 1; - path.updateTransform(); - if (path) { - brush(ctx, path, { - inHover: false, - viewWidth: this._width, - viewHeight: this._height - }, true); - } - - const imgShape = new ZRImage({ - style: { - x: 0, - y: 0, - image: canvas - } - }); - - util.extend(path, pathTransform); - - return imgShape; - } }; \ No newline at end of file diff --git a/src/svg-legacy/Painter.ts b/src/svg-legacy/Painter.ts index cd25d6d7b..d4c213b75 100644 --- a/src/svg-legacy/Painter.ts +++ b/src/svg-legacy/Painter.ts @@ -404,7 +404,6 @@ class SVGPainter implements PainterBase { return 'data:image/svg+xml;charset=UTF-8,' + html; } refreshHover = createMethodNotSupport('refreshHover') as PainterBase['refreshHover']; - pathToImage = createMethodNotSupport('pathToImage') as PainterBase['pathToImage']; configLayer = createMethodNotSupport('configLayer') as PainterBase['configLayer']; } diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index db3829a1a..c159f722f 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -350,7 +350,6 @@ class SVGPainter implements PainterBase { } refreshHover = createMethodNotSupport('refreshHover') as PainterBase['refreshHover']; - pathToImage = createMethodNotSupport('pathToImage') as PainterBase['pathToImage']; configLayer = createMethodNotSupport('configLayer') as PainterBase['configLayer']; } diff --git a/src/zrender.ts b/src/zrender.ts index 23aa6b130..3cefd62b2 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -334,16 +334,6 @@ class ZRender { return this.painter.getHeight(); } - /** - * Converting a path to image. - * It has much better performance of drawing image rather than drawing a vector path. - */ - pathToImage(e: Path, dpr: number) { - if (this.painter.pathToImage) { - return this.painter.pathToImage(e, dpr); - } - } - /** * Set default cursor * @param cursorStyle='default' 例如 crosshair diff --git a/zrender b/zrender new file mode 120000 index 000000000..475836947 --- /dev/null +++ b/zrender @@ -0,0 +1 @@ +/Users/lang/Develop/zrender \ No newline at end of file From d0bd5dfb9979af02a6112b8699e32938bc55ec3f Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 17 Dec 2021 13:02:45 +0800 Subject: [PATCH 108/148] feat: support anchor transform --- src/Element.ts | 32 +++++++++---------------------- src/core/Transformable.ts | 40 +++++++++++++++++++++++++-------------- src/graphic/Path.ts | 6 +++--- 3 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index 734c82cb6..b5c50ea4a 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -1,4 +1,4 @@ -import Transformable from './core/Transformable'; +import Transformable, {TRANSFORMABLE_PROPS, TransformProp} from './core/Transformable'; import { AnimationEasing } from './animation/easing'; import Animator, {cloneValue} from './animation/Animator'; import { ZRenderType } from './zrender'; @@ -22,7 +22,8 @@ import { isArrayLike, isTypedArray, isGradientObject, - filter + filter, + reduce } from './core/util'; import Polyline from './graphic/shape/Polyline'; import Group from './graphic/Group'; @@ -230,7 +231,7 @@ interface ElementEventHandlerProps { ondrop: ElementEventCallback } -export interface ElementProps extends Partial { +export interface ElementProps extends Partial, Partial> { name?: string ignore?: boolean isGroup?: boolean @@ -239,15 +240,6 @@ export interface ElementProps extends Partial { silent?: boolean ignoreClip?: boolean - // From transform - x?: number - y?: number - scaleX?: number - scaleY?: number - originX?: number - originY?: number - rotation?: number - globalScaleRatio?: number textConfig?: ElementTextConfig @@ -266,17 +258,11 @@ export interface ElementProps extends Partial { export const PRESERVED_NORMAL_STATE = '__zr_normal__'; // export const PRESERVED_MERGED_STATE = '__zr_merged__'; -const PRIMARY_STATES_KEYS = ['x', 'y', 'scaleX', 'scaleY', 'originX', 'originY', 'rotation', 'ignore'] as const; -const DEFAULT_ANIMATABLE_MAP: Partial> = { - x: true, - y: true, - scaleX: true, - scaleY: true, - originX: true, - originY: true, - rotation: true, - ignore: false -}; +const PRIMARY_STATES_KEYS = (TRANSFORMABLE_PROPS as any).concat(['ignore']) as [TransformProp, 'ignore']; +const DEFAULT_ANIMATABLE_MAP = reduce(TRANSFORMABLE_PROPS, (obj, key) => { + obj[key] = true; + return obj; +}, {ignore: false} as Partial>); export type ElementStatePropNames = (typeof PRIMARY_STATES_KEYS)[number] | 'textConfig'; export type ElementState = Pick & ElementCommonState diff --git a/src/core/Transformable.ts b/src/core/Transformable.ts index cc5f683a0..0ab5ce796 100644 --- a/src/core/Transformable.ts +++ b/src/core/Transformable.ts @@ -28,6 +28,12 @@ class Transformable { skewY: number rotation: number + + /** + * Will translated the element to the anchor position before applying other transforms. + */ + anchorX: number + anchorY: number /** * Origin of scale, rotation, skew */ @@ -293,6 +299,8 @@ class Transformable { const oy = target.originY || 0; const sx = target.scaleX; const sy = target.scaleY; + const ax = target.anchorX; + const ay = target.anchorY; const rotation = target.rotation || 0; const x = target.x; const y = target.y; @@ -300,12 +308,14 @@ class Transformable { // TODO: zrender use different hand in coordinate system and y axis is inversed. const skewY = target.skewY ? Math.tan(-target.skewY) : 0; - // The order of transform (-origin * scale * skew * rotate * origin * translate). + // The order of transform (-anchor * -origin * scale * skew * rotate * origin * translate). // We merge (-origin * scale * skew) into one. Also did identity in these operations. // origin - if (ox || oy) { - m[4] = -ox * sx - skewX * oy * sy; - m[5] = -oy * sy - skewY * ox * sx; + if (ox || oy || ax || ay) { + const dx = ox + ax; + const dy = oy + ay; + m[4] = -dx * sx - skewX * dy * sy; + m[5] = -dy * sy - skewY * dx * sx; } else { m[4] = m[5] = 0; @@ -329,21 +339,23 @@ class Transformable { private static initDefaultProps = (function () { const proto = Transformable.prototype; - proto.x = 0; - proto.y = 0; - proto.scaleX = 1; - proto.scaleY = 1; - proto.originX = 0; - proto.originY = 0; - proto.skewX = 0; - proto.skewY = 0; - proto.rotation = 0; + proto.scaleX = + proto.scaleY = proto.globalScaleRatio = 1; + proto.x = + proto.y = + proto.originX = + proto.originY = + proto.skewX = + proto.skewY = + proto.rotation = + proto.anchorX = + proto.anchorY = 0; })() }; export const TRANSFORMABLE_PROPS = [ - 'x', 'y', 'originX', 'originY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY' + 'x', 'y', 'originX', 'originY', 'anchorX', 'anchorY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY' ] as const; export type TransformProp = (typeof TRANSFORMABLE_PROPS)[number] diff --git a/src/graphic/Path.ts b/src/graphic/Path.ts index 08559c040..b1b16d087 100644 --- a/src/graphic/Path.ts +++ b/src/graphic/Path.ts @@ -17,6 +17,7 @@ import Animator from '../animation/Animator'; import { lum } from '../tool/color'; import { DARK_LABEL_COLOR, LIGHT_LABEL_COLOR, DARK_MODE_THRESHOLD, LIGHTER_LABEL_COLOR } from '../config'; import { REDRAW_BIT, SHAPE_CHANGED_BIT, STYLE_CHANGED_BIT } from './constants'; +import { TRANSFORMABLE_PROPS } from '../core/Transformable'; export interface PathStyleProps extends CommonStyleProps { @@ -125,10 +126,9 @@ export type PathState = Pick & { hoverLayer?: boolean } -const pathCopyParams = [ - 'x', 'y', 'rotation', 'scaleX', 'scaleY', 'originX', 'originY', 'invisible', +const pathCopyParams = (TRANSFORMABLE_PROPS as readonly string[]).concat(['invisible', 'culling', 'z', 'z2', 'zlevel', 'parent' -] as const; +]) as (keyof Path)[]; class Path extends Displayable { From 5dcb5ff4965a66a2bbfa5d6ea2472514607bc741 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 17 Dec 2021 13:08:31 +0800 Subject: [PATCH 109/148] fix(animation): fix boolean value type not using discrete animation --- src/animation/Animator.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index c87872a15..dd1cd0e76 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -306,14 +306,19 @@ class Track { } } else { - if (!isNaN(+rawValue)) { + if (isNumber(value)) { valType = VALUE_TYPE_NUMBER; } else if (isString(rawValue)) { - const colorArray = color.parse(rawValue); - if (colorArray) { - value = colorArray; - valType = VALUE_TYPE_COLOR; + if (!isNaN(+rawValue)) { // Can be string number like '2' + valType = VALUE_TYPE_NUMBER; + } + else { + const colorArray = color.parse(rawValue); + if (colorArray) { + value = colorArray; + valType = VALUE_TYPE_COLOR; + } } } else if (isGradientObject(rawValue)) { @@ -337,7 +342,8 @@ class Track { // Inference type from the first keyframe. this.valType = valType; } - else if (valType !== this.valType) { // Not same value type. + // Not same value type or can't be interpolated. + else if (valType !== this.valType || valType === VALUE_TYPE_UNKOWN) { discrete = true; } From b9482d47eaae8641b0ca10d3f2bffd48b9e74cb8 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 17 Dec 2021 13:15:49 +0800 Subject: [PATCH 110/148] fix linting issues --- src/PainterBase.ts | 2 -- src/canvas/Painter.ts | 4 +--- src/zrender.ts | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/PainterBase.ts b/src/PainterBase.ts index 3b7100abb..fa42c37ad 100644 --- a/src/PainterBase.ts +++ b/src/PainterBase.ts @@ -1,5 +1,3 @@ -import Path from './graphic/Path'; -import ZRImage from './graphic/Image'; import { GradientObject } from './graphic/Gradient'; import { PatternObject } from './graphic/Pattern'; import { Dictionary } from './core/types'; diff --git a/src/canvas/Painter.ts b/src/canvas/Painter.ts index 691e1a275..4bc818182 100644 --- a/src/canvas/Painter.ts +++ b/src/canvas/Painter.ts @@ -2,17 +2,15 @@ import {devicePixelRatio} from '../config'; import * as util from '../core/util'; import Layer, { LayerConfig } from './Layer'; import requestAnimationFrame from '../animation/requestAnimationFrame'; -import ZRImage from '../graphic/Image'; import env from '../core/env'; import Displayable from '../graphic/Displayable'; -import { WXCanvasRenderingContext, ZRCanvasRenderingContext } from '../core/types'; +import { WXCanvasRenderingContext } from '../core/types'; import { GradientObject } from '../graphic/Gradient'; import { ImagePatternObject } from '../graphic/Pattern'; import Storage from '../Storage'; import { brush, BrushScope, brushSingle } from './graphic'; import { PainterBase } from '../PainterBase'; import BoundingRect from '../core/BoundingRect'; -import Path from '../graphic/Path'; import { REDRAW_BIT } from '../graphic/constants'; import { getSize } from './helper'; import type IncrementalDisplayable from '../graphic/IncrementalDisplayable'; diff --git a/src/zrender.ts b/src/zrender.ts index 3cefd62b2..0bfb47685 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -24,7 +24,6 @@ import { EventCallback } from './core/Eventful'; import Displayable from './graphic/Displayable'; import { lum } from './tool/color'; import { DARK_MODE_THRESHOLD } from './config'; -import Path from './graphic/Path'; import Group from './graphic/Group'; From 7732252fb65ee51763be419909e9d58c8bf8fe9a Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 17 Dec 2021 16:36:49 +0800 Subject: [PATCH 111/148] remove symlink --- zrender | 1 - 1 file changed, 1 deletion(-) delete mode 120000 zrender diff --git a/zrender b/zrender deleted file mode 120000 index 475836947..000000000 --- a/zrender +++ /dev/null @@ -1 +0,0 @@ -/Users/lang/Develop/zrender \ No newline at end of file From 7f8421536b98df2132cba4416341572ae77ac481 Mon Sep 17 00:00:00 2001 From: pissang Date: Mon, 20 Dec 2021 09:58:52 +0800 Subject: [PATCH 112/148] optimize code based on review --- src/Element.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index b5c50ea4a..f33f4f2f1 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -1865,22 +1865,22 @@ function animateToShallow( let animationKeys: string[] = []; for (let k = 0; k < targetKeys.length; k++) { const innerKey = targetKeys[k] as string; + const targetVal = target[innerKey]; if ( - target[innerKey] != null - && animateObj[innerKey] != null + targetVal != null && animateObj[innerKey] != null && (animateAll || (animationProps as Dictionary)[innerKey]) ) { - if (isObject(target[innerKey]) - && !isArrayLike(target[innerKey]) - && !isGradientObject(target[innerKey]) + if (isObject(targetVal) + && !isArrayLike(targetVal) + && !isGradientObject(targetVal) ) { if (topKey) { // logError('Only support 1 depth nest object animation.'); // Assign directly. // TODO richText? if (!reverse) { - animateObj[innerKey] = target[innerKey]; + animateObj[innerKey] = targetVal; animatable.updateDuringAnimation(topKey); } continue; @@ -1889,7 +1889,7 @@ function animateToShallow( animatable, innerKey, animateObj[innerKey], - target[innerKey], + targetVal, cfg, animationProps && (animationProps as Dictionary)[innerKey], animators, @@ -1902,7 +1902,7 @@ function animateToShallow( } else if (!reverse) { // Assign target value directly. - animateObj[innerKey] = target[innerKey]; + animateObj[innerKey] = targetVal; animatable.updateDuringAnimation(topKey); // Previous animation will be stopped on the changed keys. // So direct assign is also included. From 03f4a1e0ba3e371c50b8af875de8d0ba44228f47 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 22 Dec 2021 13:39:01 +0800 Subject: [PATCH 113/148] fix(animation): ignore loop animation when switching state. --- src/Element.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index f33f4f2f1..eb87cc5f9 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -752,7 +752,8 @@ class Element { const animator = this.animators[i]; const fromStateTransition = animator.__fromStateTransition; // Ignore animation from state transition(except normal). - if (fromStateTransition && fromStateTransition !== PRESERVED_NORMAL_STATE) { + // Ignore loop animation. + if (animator.getLoop() || fromStateTransition && fromStateTransition !== PRESERVED_NORMAL_STATE) { continue; } @@ -1161,10 +1162,13 @@ class Element { for (let i = 0; i < this.animators.length; i++) { const animator = this.animators[i]; const targetName = animator.targetName; - animator.__changeFinalValue(targetName - ? ((state || normalState) as any)[targetName] - : (state || normalState) - ); + // Ignore loop animation + if (!animator.getLoop()) { + animator.__changeFinalValue(targetName + ? ((state || normalState) as any)[targetName] + : (state || normalState) + ); + } } } From f2594ec7df63f737ec39ec2ecb1a283092d8ad54 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 22 Dec 2021 14:24:48 +0800 Subject: [PATCH 114/148] fix: optimize image type check in svg renderer. --- src/svg/graphic.ts | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index df63f1b10..66b33b907 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -29,7 +29,7 @@ import mapStyleToAttrs from './mapStyleToAttrs'; import { SVGVNodeAttrs, createVNode, SVGVNode, vNodeToString, BrushScope } from './core'; import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; -import { assert, logError, map, retrieve2 } from '../core/util'; +import { assert, isFunction, isString, logError, map, retrieve2 } from '../core/util'; import Polyline from '../graphic/shape/Polyline'; import Polygon from '../graphic/shape/Polygon'; import { GradientObject } from '../graphic/Gradient'; @@ -42,6 +42,13 @@ import { DEFAULT_FONT, DEFAULT_FONT_FAMILY } from '../core/platform'; const round = Math.round; +function isImageLike(val: any): val is HTMLImageElement { + return val && isString(val.src); +} +function isCanvasLike(val: any): val is HTMLCanvasElement { + return val && isFunction(val.toDataURL); +} + type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; @@ -204,12 +211,14 @@ export function brushSVGImage(el: ZRImage, scope: BrushScope) { const style = el.style; let image = style.image; - if (image instanceof HTMLImageElement) { - image = image.src; - } - // heatmap layer in geo may be a canvas - else if (image instanceof HTMLCanvasElement) { - image = image.toDataURL(); + if (image && !isString(image)) { + if (isImageLike(image)) { + image = image.src; + } + // heatmap layer in geo may be a canvas + else if (isCanvasLike(image)) { + image = image.toDataURL(); + } } if (!image) { @@ -472,13 +481,13 @@ function setPattern( let imageHeight = val.imageHeight; let imageSrc; const patternImage = val.image; - if (typeof patternImage === 'string') { + if (isString(patternImage)) { imageSrc = patternImage; } - else if (patternImage instanceof HTMLImageElement) { + else if (isImageLike(patternImage)) { imageSrc = patternImage.src; } - else if (patternImage instanceof HTMLCanvasElement) { + else if (isCanvasLike(patternImage)) { imageSrc = patternImage.toDataURL(); } From 7a2a4209e58574f2c8930eccebb462e59e8059d5 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 22 Dec 2021 14:30:26 +0800 Subject: [PATCH 115/148] update test case --- test/svg-ssr.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/svg-ssr.html b/test/svg-ssr.html index cac180bb9..bad5ad70b 100644 --- a/test/svg-ssr.html +++ b/test/svg-ssr.html @@ -135,6 +135,8 @@ // Image with cliPath + const imageObj = new Image(); + imageObj.src = 'asset/test.png'; for (let k = 0; k < 2; k++) { const group = new zrender.Group(); for (let i = 0; i < 10; i++) { @@ -142,7 +144,7 @@ x: i * cellSize + (cellSize - elSize) / 2, y: cellSize * (5 + k), style: { - image: 'asset/test.png', + image: k ? 'asset/test.png' : imageObj, width: elSize, height: elSize } From 13ba53c8074ba7d59b90734abb7cc3785e5706a2 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 22 Dec 2021 16:48:25 +0800 Subject: [PATCH 116/148] feat(ssr): add useViewBox opt --- src/svg/Painter.ts | 14 ++++++++++---- src/svg/core.ts | 10 ++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index c159f722f..1a1f7f418 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -119,7 +119,8 @@ class SVGPainter implements PainterBase { renderToVNode(opts?: { animation?: boolean willUpdate?: boolean - compress?: boolean + compress?: boolean, + useViewBox?: boolean }) { opts = opts || {}; @@ -176,14 +177,18 @@ class SVGPainter implements PainterBase { } } - return createSVGVNode(width, height, children); + return createSVGVNode(width, height, children, opts.useViewBox); } renderToString(opts?: { /** * If add css animation. */ - cssAnimation: boolean + cssAnimation?: boolean + /** + * If use viewBox + */ + useViewBox?: boolean }) { const defaultOpts = { cssAnimation: true @@ -193,7 +198,8 @@ class SVGPainter implements PainterBase { return vNodeToString(this.renderToVNode({ animation: defaultOpts.cssAnimation, willUpdate: false, - compress: true + compress: true, + useViewBox: opts.useViewBox }), { newline: true }); } diff --git a/src/svg/core.ts b/src/svg/core.ts index 511b65a13..412038a44 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -174,7 +174,12 @@ export function createBrushScope(zrId: string): BrushScope { }; } -export function createSVGVNode(width?: number | string, height?: number | string, children?: SVGVNode[]) { +export function createSVGVNode( + width: number | string, + height: number | string, + children?: SVGVNode[], + useViewBox?: boolean +) { return createVNode( 'svg', 'root', @@ -184,7 +189,8 @@ export function createSVGVNode(width?: number | string, height?: number | string 'xmlns': SVGNS, 'xmlns:xlink': XLINKNS, 'version': '1.1', - 'baseProfile': 'full' + 'baseProfile': 'full', + 'viewBox': useViewBox ? `0 0 ${width} ${height}` : false }, children ); From d239b31fb60850182eaa3ab830823839756b9159 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 22 Dec 2021 19:01:03 +0800 Subject: [PATCH 117/148] feat(ssr): change useViewBox to be true by default --- src/svg/Painter.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 1a1f7f418..be1697c7d 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -20,7 +20,7 @@ import { createSVGVNode } from './core'; import { normalizeColor, encodeBase64 } from './helper'; -import { defaults, extend, keys, logError, map } from '../core/util'; +import { defaults, extend, keys, logError, map, retrieve2 } from '../core/util'; import Path from '../graphic/Path'; import patch, { updateAttrs } from './patch'; import { getSize } from '../canvas/helper'; @@ -183,23 +183,21 @@ class SVGPainter implements PainterBase { renderToString(opts?: { /** * If add css animation. + * @default true */ cssAnimation?: boolean /** * If use viewBox + * @default true */ useViewBox?: boolean }) { - const defaultOpts = { - cssAnimation: true - }; - opts = defaults(opts || {}, defaultOpts); - + opts = opts || {}; return vNodeToString(this.renderToVNode({ - animation: defaultOpts.cssAnimation, + animation: retrieve2(opts.cssAnimation, true), willUpdate: false, compress: true, - useViewBox: opts.useViewBox + useViewBox: retrieve2(opts.useViewBox, true) }), { newline: true }); } From b0022565c81bf4216b6e21fa72f93773a3f2543a Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 22 Dec 2021 19:09:00 +0800 Subject: [PATCH 118/148] style: remove unused code --- src/svg/Painter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index be1697c7d..51a4d8ca7 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -20,7 +20,7 @@ import { createSVGVNode } from './core'; import { normalizeColor, encodeBase64 } from './helper'; -import { defaults, extend, keys, logError, map, retrieve2 } from '../core/util'; +import { extend, keys, logError, map, retrieve2 } from '../core/util'; import Path from '../graphic/Path'; import patch, { updateAttrs } from './patch'; import { getSize } from '../canvas/helper'; From 2ab3c96230943cf1379bf9edb6ae732dc0ab8e0b Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 23 Dec 2021 15:26:05 +0800 Subject: [PATCH 119/148] chore: add .js in the import statement of es modules. --- build/processLib.js | 67 + build/transformImport.js | 109 + index.d.ts | 4 +- index.js | 9 +- package-lock.json | 129 +- package.json | 4 +- src/all.ts | 8 + test/boundingbox.html | 18 +- test/css-transform-inverse.html | 98 +- test/cubic.html | 16 +- test/lib/esl.js | 1 - test/lib/requireES.js | 349 - test/lib/rollup.browser.js | 10626 ------------------------------ test/pathContain.html | 28 +- test/pathConvert.html | 243 +- test/sector.html | 37 +- test/text.html | 2 - 17 files changed, 532 insertions(+), 11216 deletions(-) create mode 100644 build/processLib.js create mode 100644 build/transformImport.js create mode 100644 src/all.ts delete mode 100644 test/lib/esl.js delete mode 100644 test/lib/requireES.js delete mode 100644 test/lib/rollup.browser.js diff --git a/build/processLib.js b/build/processLib.js new file mode 100644 index 000000000..65acca6c6 --- /dev/null +++ b/build/processLib.js @@ -0,0 +1,67 @@ +// Porcess generated lib files. +// Like adding js extension in the import statement. + +const { transformImport } = require('./transformImport'); +const globby = require('globby'); +const path = require('path'); +const fs = require('fs'); +const chalk = require('chalk'); +const rollup = require('rollup'); +const nodeResolve = require('@rollup/plugin-node-resolve').default; + +function addJsExtension(moduleName) { + // Ignore 'tslib' + if (!(moduleName.startsWith('.'))) { + return moduleName; + } + if (moduleName.endsWith('.ts')) { + // Replace ts with js + return moduleName.replace(/\.ts$/, '.js'); + } + else if (moduleName.endsWith('.js')) { + return moduleName; + } + else { + return moduleName + '.js' + } +} + +async function transform() { + const libFiles = await globby(path.join(__dirname, '../lib/**/*.js')); + + for (let file of libFiles) { + const code = fs.readFileSync(file, 'utf-8'); + fs.writeFileSync(file, transformImport(code, addJsExtension), 'utf-8'); + } + + // Transform index; + const indexFile = path.join(__dirname, '../index.js'); + fs.writeFileSync( + indexFile, + transformImport( + fs.readFileSync(indexFile, 'utf-8'), + (mduleName) => addJsExtension(mduleName).replace('./src', './lib') + ) + ) +} + +async function testLibBundling() { + +} + +transform().then(() => { + console.log(chalk.green('Added .js extensions.')); + console.log(chalk.gray('Start testing generated libs...')); + return testLibBundling(); +}).then(() => { + return rollup.rollup({ + input: path.resolve(__dirname, '../index.js'), + plugins: [nodeResolve()] + }); +}).then(() => { + console.log(chalk.green('Libs can be bundled!')); +}) + + + +// Do bundling test to check if transform is correct. \ No newline at end of file diff --git a/build/transformImport.js b/build/transformImport.js new file mode 100644 index 000000000..722829d1a --- /dev/null +++ b/build/transformImport.js @@ -0,0 +1,109 @@ +// adding js extension in the import statement. + +// Reference: +// https://regexr.com/47jlq +// https://gist.github.com/manekinekko/7e58a17bc62a9be47172 +const regexp = /((?:(?:import)|(?:export))\s+?(?:(?:(?:[\w*\s{},]*)\s+from\s+?)|))(?:(?:"(.*?)")|(?:'(.*?)'))([\s]*?(?:;|$|))/g; + +module.exports.transformImport = function (code, processModuleName) { + return code.replace(regexp, (str, prefix, moduleNameA, moduleNameB, postfix) => { + let moduleName = (moduleNameA === undefined ? moduleNameB : moduleNameA).trim(); + const quote = moduleNameA === undefined ? "'" : '"'; + return prefix + quote + processModuleName(moduleName) + quote + postfix; + // Not support other extensions. + }); +} + + +const testCases = `import videos from './videos/index.js' + +export default (socket, context) => { + // dynamically importing all the socket.io handler (it's dynamic import that happen at run time) + import { + something +} from "./test/okbb" + +const f = 2; + +import test from 'obb' + + +import { + Component +} from '@angular2/core'; + +import defaultMember from "module-0"; + +import * as name from "module-1 "; + +import { member } from " module-2"; + +import { member as alias } from "module-3"; + +import { member1 , member2 } from "module-4"; + +import { member1 , member2 as alias2 , member3 as alias3 } from "module-5"; + +import defaultMember, { member, member } from "module-6"; + +import defaultMember, * as name from "module-7"; + +import "module-8"; + +import "module-9" // comment no problem + +import "module-b' // doesn't match -> the opening and closing quation mark are different + +importing hya from 'ttt' + +import fbsfrom '' + + +// Export expressions. +export { aaa }; + +export * from "module-10"; + +export { aaa } from "module-11"; + +// Should not be parsed +export default aaa; + +export function bbb () { +}; +` + +module.exports.runTest = function () { + const expected = [ + './videos/index.js', + './test/okbb', + 'obb', + '@angular2/core', + 'module-0', + 'module-1', + 'module-2', + 'module-3', + 'module-4', + 'module-5', + 'module-6', + 'module-7', + 'module-8', + 'module-9', + 'module-10', + 'module-11' + ] + let cursor = 0; + module.exports.transformImport(testCases, (moduleName) => { + if (expected[cursor] !== moduleName) { + throw new Error(`Expected ${expected[cursor]}. Actual ${moduleName}`); + } + cursor++; + return moduleName; + }) + if (cursor !== expected.length) { + throw new Error('Test failed'); + } + console.log('All test passed!') +} + +// module.exports.runTest(); \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index 047e18390..21fff2ecb 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,2 +1,2 @@ -export * from './lib/zrender'; -export * from './lib/export'; +export * from './src/zrender'; +export * from './src/export'; diff --git a/index.js b/index.js index a95f37b0a..256c4b514 100644 --- a/index.js +++ b/index.js @@ -1,8 +1 @@ -export * from './lib/zrender'; -export * from './lib/export'; - -import {registerPainter} from './lib/zrender'; -import CanvasPainter from './lib/canvas/Painter'; -import SVGPainter from './lib/svg/Painter'; -registerPainter('canvas', CanvasPainter); -registerPainter('svg', SVGPainter); +export * from './lib/all.js' \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index efe1775e4..58af8f9b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1179,15 +1179,41 @@ "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", - "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, + "@rollup/plugin-node-resolve": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.1.tgz", + "integrity": "sha512-6QKtRevXLrmEig9UiMYt2fSvee9TyltGRfw+qSs6xjUnxwjOzTOqy+/Lpxsgjb8mJn1EQNbCDAvt89O4uzL5kw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "dependencies": { + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, "@rollup/plugin-replace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-3.0.0.tgz", @@ -1373,6 +1399,15 @@ "integrity": "sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw==", "dev": true }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -1541,6 +1576,12 @@ "tsutils": "^3.21.0" }, "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -1550,6 +1591,26 @@ "ms": "2.1.2" } }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -1923,6 +1984,12 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "builtin-modules": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", + "dev": true + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2650,17 +2717,27 @@ "dev": true }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "fast-json-stable-stringify": { @@ -2676,9 +2753,9 @@ "dev": true }, "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -2888,9 +2965,9 @@ }, "dependencies": { "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true } } @@ -3136,6 +3213,12 @@ "is-extglob": "^2.1.1" } }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4963,13 +5046,21 @@ "dev": true }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" + }, + "dependencies": { + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + } } }, "mime-db": { diff --git a/package.json b/package.json index 648f00ed6..f849f7ad1 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "prepare:nightly": "node build/prepareNightly.js", "prepare:nightly-next": "node build/prepareNightly.js --next", "build:bundle": "node build/build.js", - "build:lib": "npx tsc -m ES2015 --outDir lib", + "build:lib": "npx tsc -m ES2015 --outDir lib && node build/processLib.js", "watch:bundle": "node build/build.js --watch", "watch:lib": "npx tsc -w -m ES2015 --outDir lib", "test": "npx jest --config test/ut/jest.config.js", @@ -36,6 +36,7 @@ ], "devDependencies": { "@microsoft/api-extractor": "^7.7.2", + "@rollup/plugin-node-resolve": "^11.0.0", "@rollup/plugin-replace": "^3.0.0", "@types/jest": "^27.0.2", "@typescript-eslint/eslint-plugin": "^4.9.1", @@ -44,6 +45,7 @@ "commander": "2.11.0", "eslint": "6.3.0", "fs-extra": "4.0.2", + "globby": "^11.0.4", "jest": "^27.2.5", "jsdom": "^16.0.0", "rollup": "^1.28.0", diff --git a/src/all.ts b/src/all.ts new file mode 100644 index 000000000..cb7eae50b --- /dev/null +++ b/src/all.ts @@ -0,0 +1,8 @@ +export * from './zrender'; +export * from './export'; + +import {registerPainter} from './zrender'; +import CanvasPainter from './canvas/Painter'; +import SVGPainter from './svg/Painter'; +registerPainter('canvas', CanvasPainter); +registerPainter('svg', SVGPainter); diff --git a/test/boundingbox.html b/test/boundingbox.html index 61e7b4298..9396edddc 100644 --- a/test/boundingbox.html +++ b/test/boundingbox.html @@ -3,22 +3,27 @@ 包围盒计算测试 - -
- + \ No newline at end of file diff --git a/test/css-transform-inverse.html b/test/css-transform-inverse.html index f235303f3..c27c9eb11 100644 --- a/test/css-transform-inverse.html +++ b/test/css-transform-inverse.html @@ -24,8 +24,6 @@ - - @@ -179,52 +177,58 @@ - + diff --git a/test/cubic.html b/test/cubic.html index 72c857480..ce821090f 100644 --- a/test/cubic.html +++ b/test/cubic.html @@ -3,14 +3,19 @@ cubic 函数测试 - - - + diff --git a/test/lib/esl.js b/test/lib/esl.js deleted file mode 100644 index 1f3ec7758..000000000 --- a/test/lib/esl.js +++ /dev/null @@ -1 +0,0 @@ -var define,require,esl;!function(n){function e(n,e){function r(n){0===n.indexOf(".")&&i.push(n)}var i=[];if("string"==typeof n?r(n):$(n,function(n){r(n)}),i.length>0)throw new Error("[REQUIRE_FATAL]Relative ID is not allowed in global require: "+i.join(", "));var o=C.waitSeconds;return o&&n instanceof Array&&(D&&clearTimeout(D),D=setTimeout(t,1e3*o)),_(n,e)}function t(){function n(a,u){if(!o[a]&&!l(a,N)){o[a]=1,l(a,L)||r[a]||(r[a]=1,e.push(a));var f=T[a];f?u&&(r[a]||(r[a]=1,e.push(a)),$(f.depMs,function(e){n(e.absId,e.hard)})):i[a]||(i[a]=1,t.push(a))}}var e=[],t=[],r={},i={},o={};for(var a in F)n(a,1);if(e.length||t.length)throw new Error("[MODULE_TIMEOUT]Hang( "+(e.join(", ")||"none")+" ) Miss( "+(t.join(", ")||"none")+" )")}function r(n){$(H,function(e){a(n,e.deps,e.factory)}),H.length=0,u(n)}function i(n,e,t){if(null==t&&(null==e?(t=n,n=null):(t=e,e=null,n instanceof Array&&(e=n,n=null))),null!=t){var r=window.opera;if(!n&&document.attachEvent&&(!r||"[object Opera]"!==r.toString())){var i=S();n=i&&i.getAttribute("data-require-id")}n?a(n,e,t):H[0]={deps:e,factory:t}}}function o(){var n=C.config[this.id];return n&&"object"==typeof n?n:{}}function a(n,e,t){T[n]||(T[n]={id:n,depsDec:e,deps:e||["require","exports","module"],factoryDeps:[],factory:t,exports:{},config:o,state:z,require:w(n),depMs:[],depMkv:{},depRs:[],depPMs:[]})}function u(n){var e=T[n];if(e&&!l(n,B)){var t=e.deps,r=e.factory,i=0;"function"==typeof r&&(i=Math.min(r.length,t.length),!e.depsDec&&r.toString().replace(/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/gm,"").replace(/require\(\s*(['"'])([^'"]+)\1\s*\)/g,function(n,e,r){t.push(r)}));var o=[];$(t,function(t,r){var a,u,f=I(t),c=q(f.mod,n);c&&!P[c]?(f.res&&(u={id:t,mod:c,res:f.res},F[c]=1,e.depPMs.push(c),e.depRs.push(u)),a=e.depMkv[c],a||(a={id:f.mod,absId:c,hard:i>r},e.depMs.push(a),e.depMkv[c]=a,o.push(c))):a={absId:c},i>r&&e.factoryDeps.push(u||a)}),e.state=B,s(n),m(o)}}function f(){for(var n in F)c(n),d(n)}function c(n){function e(n){if(!l(n,B))return!1;if(l(n,L)||t[n])return!0;t[n]=1;var r=T[n],i=!0;return $(r.depMs,function(n){return i=e(n.absId)}),i&&$(r.depRs,function(n){return i=!(!n.absId||!l(n.absId,N))}),i&&(r.state=L),i}var t={};e(n)}function s(e){function t(){if(!r&&i.state===L){r=1;var t=1,o=[];if($(i.factoryDeps,function(n){var e=n.absId;return P[e]||(d(e),l(e,N))?void o.push(e):(t=0,!1)}),t){try{var a=p(o,{require:i.require,exports:i.exports,module:i}),u=i.factory,f="function"==typeof u?u.apply(n,a):u;null!=f&&(i.exports=f),i.invokeFactory=null,delete F[e]}catch(c){if(r=0,/^\[MODULE_MISS\]"([^"]+)/.test(c.message)){var s=i.depMkv[RegExp.$1];return void(s&&(s.hard=1))}throw c}g(e)}}}var r,i=T[e];i.invokeFactory=t,$(i.depPMs,function(n){v(n,function(){$(i.depRs,function(t){t.absId||t.mod!==n||(t.absId=q(t.id,e),m([t.absId],f))})})})}function l(n,e){return T[n]&&T[n].state>=e}function d(n){var e=T[n];e&&e.invokeFactory&&e.invokeFactory()}function p(n,e){var t=[];return $(n,function(n,r){t[r]=e[n]||h(n)}),t}function v(n,e){if(l(n,N))return void e();var t=Q[n];t||(t=Q[n]=[]),t.push(e)}function g(n){var e=Q[n]||[],t=T[n];t.state=N;for(var r=e.length;r--;)e[r]();e.length=0,delete Q[n]}function h(n){return l(n,N)?T[n].exports:null}function m(e,t,r,i){function o(){if(!a){var r=1;$(e,function(n){return P[n]?void 0:r=!!l(n,N)}),r&&(a=1,"function"==typeof t&&t.apply(n,p(e,P)))}}if("string"==typeof e){if(d(e),!l(e,N))throw new Error('[MODULE_MISS]"'+e+'" is not exists!');return h(e)}i=i||{};var a=0;e instanceof Array&&(o(),a||($(e,function(n){P[n]||l(n,N)||(v(n,o),i[n]||(n.indexOf("!")>0?b:y)(n,r),u(n))}),f()))}function y(n){function e(){var e=t.readyState;if("undefined"==typeof e||/^(loaded|complete)$/.test(e)){t.onload=t.onreadystatechange=null,t=null,r(n);for(var i in F)u(i);f()}}if(!G[n]&&!T[n]){G[n]=1;var t=document.createElement("script");t.setAttribute("data-require-id",n),t.src=E(n+".js"),t.async=!0,t.readyState?t.onreadystatechange=e:t.onload=e,U(t)}}function b(n,e){function t(e){u.exports=e||!0,g(n)}function i(r){var i=e?T[e].require:_;r.load(a.res,i,t,o.call({id:n}))}if(!T[n]){var a=I(n),u={id:n,state:B};T[n]=u,t.fromText=function(n,e){F[n]=1,new Function(e)(),r(n)},i(h(a.mod))}}function M(n,e){var t=R(n,1,e);return t.sort(j),t}function k(){C.baseUrl=C.baseUrl.replace(/\/$/,"")+"/",J=M(C.paths),V=M(C.map,1),$(V,function(n){n.v=M(n.v)}),K=[],$(C.packages,function(n){var e=n;"string"==typeof n&&(e={name:n.split("/")[0],location:n,main:"main"}),e.location=e.location||e.name,e.main=(e.main||"main").replace(/\.js$/i,""),e.reg=O(e.name),K.push(e)}),K.sort(j),W=M(C.urlArgs,1),X=M(C.noRequests),$(X,function(n){var e=n.v,t={};n.v=t,e instanceof Array||(e=[e]),$(e,function(n){t[n]=1})})}function x(n,e,t){$(e,function(e){return e.reg.test(n)?(t(e.v,e.k,e),!1):void 0})}function E(n){var e=/(\.[a-z0-9]+)$/i,t=/(\?[^#]*)$/,r="",i=n,o="";t.test(n)&&(o=RegExp.$1,n=n.replace(t,"")),e.test(n)&&(r=RegExp.$1,i=n.replace(e,""));var a,u=i;return x(i,J,function(n,e){u=u.replace(e,n),a=1}),a||x(i,K,function(n,e,t){u=u.replace(t.name,t.location)}),/^([a-z]{2,10}:\/)?\//i.test(u)||(u=C.baseUrl+u),u+=r+o,x(i,W,function(n){u+=(u.indexOf("?")>0?"&":"?")+n}),u}function w(n){function e(e,r){if("string"==typeof e)return t[e]||(t[e]=m(q(e,n))),t[e];if(e instanceof Array){var i=[],o=[],a=[];$(e,function(e,t){var r=I(e),u=q(r.mod,n);o.push(u),F[u]=1,r.res?(i.push(u),a[t]=null):a[t]=u});var u={};$(o,function(n){var e;x(n,X,function(n){e=n}),e&&(e["*"]?u[n]=1:$(o,function(t){return e[t]?(u[n]=1,!1):void 0}))}),m(o,function(){$(a,function(t,r){null==t&&(a[r]=q(e[r],n))}),m(a,r,n)},n,u)}}var t={};return e.toUrl=function(e){return E(q(e,n))},e}function q(n,e){if(!n)return"";e=e||"";var t=I(n);if(!t)return n;var r=t.res,i=A(t.mod,e);if($(K,function(n){var e=n.name;return e===i?(i=e+"/"+n.main,!1):void 0}),x(e,V,function(n){x(i,n,function(n,e){i=i.replace(e,n)})}),r){var o=h(i);r=o.normalize?o.normalize(r,function(n){return q(n,e)}):q(r,e),i+="!"+r}return i}function A(n,e){if(0===n.indexOf(".")){var t=e.split("/"),r=n.split("/"),i=t.length-1,o=r.length,a=0,u=0;n:for(var f=0;o>f;f++)switch(r[f]){case"..":if(!(i>a))break n;a++,u++;break;case".":u++;break;default:break n}return t.length=i-a,r=r.slice(u),t.concat(r).join("/")}return n}function I(n){var e=n.split("!");return e[0]?{mod:e[0],res:e[1]}:null}function R(n,e,t){var r=[];for(var i in n)if(n.hasOwnProperty(i)){var o={k:i,v:n[i]};r.push(o),e&&(o.reg="*"===i&&t?/^/:O(i))}return r}function S(){if(Y)return Y;if(Z&&"interactive"===Z.readyState)return Z;for(var n=document.getElementsByTagName("script"),e=n.length;e--;){var t=n[e];if("interactive"===t.readyState)return Z=t,t}}function U(n){Y=n,ee?ne.insertBefore(n,ee):ne.appendChild(n),Y=null}function O(n){return new RegExp("^"+n+"(/|$)")}function $(n,e){if(n instanceof Array)for(var t=0,r=n.length;r>t&&e(n[t],t)!==!1;t++);}function j(n,e){var t=n.k||n.name,r=e.k||e.name;return"*"===r?-1:"*"===t?1:r.length-t.length}var D,T={},F={},z=1,B=2,L=3,N=4,P={require:e,exports:1,module:1},_=w(),C={baseUrl:"./",paths:{},config:{},map:{},packages:[],waitSeconds:0,noRequests:{},urlArgs:{}};e.version="1.8.8",e.loader="esl",e.toUrl=_.toUrl;var H=[];i.amd={};var Q={},G={};e.config=function(n){if(n){for(var e in C){var t=n[e],r=C[e];if(t)if("urlArgs"===e&&"string"==typeof t)C.urlArgs["*"]=t;else if(r instanceof Array)r.push.apply(r,t);else if("object"==typeof r)for(var i in t)r[i]=t[i];else C[e]=t}k()}},k();var J,K,V,W,X,Y,Z,ne=document.getElementsByTagName("head")[0],ee=document.getElementsByTagName("base")[0];ee&&(ne=ee.parentNode),define||(define=i,require||(require=e),esl=e)}(this); \ No newline at end of file diff --git a/test/lib/requireES.js b/test/lib/requireES.js deleted file mode 100644 index 12290f3bd..000000000 --- a/test/lib/requireES.js +++ /dev/null @@ -1,349 +0,0 @@ -/** - * Load es modules in browser. - * rollup.browser.js is required. - * - * [Usage]: - * - * // AMD config like. - * requireES.config({ - * baseUrl: '..', - * paths: { - * ... - * }, - * packages: [ - * {...}, ... - * ], - * urlArgs: +new Date(), - * sourceMap: true // Enable sourceMap for debugging. `false` by default. - * }); - * - * requireES([ - * 'xxx/moduleA', - * 'yyy/moduleB' - * ], function (moduleA, moduleB) { - * ... - * }); - * - * [Caution]: - * - * 1) Modules are not shared between different calling of `requireES(...)`. - * - * 2) Whether import `*` or `default` is determined by the module itself. - * That is, if the module (like `xxx/SomeClz`) only export `default` , it - * imports `default`, otherwise (like `xxx/util`) it imports `*`. - */ - -/* global define, ActiveXObject */ - -(function (global, factory) { - typeof define === 'function' && define.amd - ? define(['rollup'], factory) - : (global.requireES = factory(global.rollup)); - }(this, function (rollup) { 'use strict'; - - var TOP_MODULE_NAME = 'topModuleInRequireES'; - - var amdCfg = { - baseUrl: cwd(), - paths: {}, - packages: [], - urlArgs: null - }; - - /** - * Like AMD config. - * - * @param {Object} cfg { - * @param {string} [cfg.baseUrl='.'] - * @param {Object} [cfg.paths={}] - * @param {Array.} [cfg.packages=[]] - * @param {string} [cfg.urlArgs=''] - * @param {boolean} [cfg.sourceMap=false] - */ - function amdConfig(cfg) { - if (cfg.baseUrl != null) { - amdCfg.baseUrl = resolve(cwd(), cfg.baseUrl); - } - if (cfg.paths) { - amdCfg.paths = extend({}, cfg.paths); - } - if (cfg.packages) { - amdCfg.packages.length = 0; - for (var i = 0; i < cfg.packages.length; i++) { - amdCfg.packages[i] = extend({}, cfg.packages[i]); - } - } - if (cfg.urlArgs != null) { - amdCfg.urlArgs = cfg.urlArgs; - } - if (cfg.sourceMap != null) { - amdCfg.sourceMap = cfg.sourceMap; - } - } - - /** - * Load es modules and convert to AMD modules. - * - * @param {Array.} moduleIds like ['./echarts', ...] - * @param {Function} onload Arguments: loaded modules, - * which has been converted to AMD module. - */ - function requireES(moduleIds, onload) { - - if (!(moduleIds instanceof Array)) { - throw new Error('`path` should be an array'); - } - - if (!moduleIds.length) { - return; - } - - var topCode = generateTopModuleCode(moduleIds); - - rollup.rollup({ - input: TOP_MODULE_NAME, - legacy: true, - plugins: [{ - resolveId: function (importee, importor) { - if (importee === TOP_MODULE_NAME) { - return importee; - } - // console.log('resolveid', importee, importor); - return getAbsolutePath( - importee, - importor !== TOP_MODULE_NAME ? importor : null - ); - }, - load: function (path) { - if (path === TOP_MODULE_NAME) { - return topCode; - } - // TODO Use tag to void browser cache and cache manually. - return ajax(location.origin + path); - } - }] - }).then(function (bundle) { - return bundle.generate({ - format: 'iife', - legacy: true, - // But only bundle.write support generating inline source map. - sourcemap: 'inline', - name: TOP_MODULE_NAME - }); - }).then(function (result) { - - var code = result.code; - - if (amdCfg.sourceMap) { - code = addSourceMap(code, result.map); - } - - var modules = (new Function( - 'var __DEV__ = true; ' - + code - + '\n return ' + TOP_MODULE_NAME - ))(); - - var exportsList = []; - for (var i = 0; i < moduleIds.length; i++) { - var mod = modules['m' + i]; - // Guess whether `*` or `default` is required: if only `default` - // exported, like 'xxx/SomeClz', `default` is required. - if (onlyDefaultExported(mod)) { - mod = mod['default']; - } - exportsList.push(mod); - } - onload && onload.apply(null, exportsList); - }); - } - - requireES.config = amdConfig; - - function onlyDefaultExported(mod) { - for (var name in mod) { - if (mod.hasOwnProperty(name)) { - if (name !== 'default') { - return false; - } - } - } - return true; - } - - function generateTopModuleCode(moduleIds) { - var code = []; - - for (var i = 0; i < moduleIds.length; i++) { - var moduleId = moduleIds[i]; - code.push('import * as m' + i + ' from "' + moduleId + '";'); - } - - for (var i = 0; i < moduleIds.length; i++) { - code.push('export {m' + i + '};'); - } - - return code.join('\n'); - } - - // Get absolute path. `basePath` can be omitted if moduleId is absolute. - function getAbsolutePath(moduleId, basePath) { - moduleId = addExt(moduleId); - - for (var path in amdCfg.paths) { - if (amdCfg.paths.hasOwnProperty(path)) { - if (moduleId.indexOf(path) === 0) { - moduleId = moduleId.replace(path, amdCfg.paths[path]); - return resolve(amdCfg.baseUrl, moduleId); - } - } - } - - for (var i = 0; i < amdCfg.packages.length; i++) { - var packageCfg = amdCfg.packages[i]; - var moduleIdArr = moduleId.split('/'); - if (moduleIdArr[0] === packageCfg.name) { - moduleIdArr[0] = packageCfg.location; - if (!moduleIdArr[1]) { - moduleIdArr[1] = packageCfg.main; - } - moduleId = moduleIdArr.join('/'); - return resolve(amdCfg.baseUrl, moduleId); - } - } - - if (basePath) { - moduleId = resolve(dir(basePath), moduleId); - } - - if (moduleId.charAt(0) !== '/') { - throw new Error('"' + moduleId + '" can not be found.'); - } - - return moduleId; - } - - function addExt(moduleId) { - if (moduleId.split('/').pop().indexOf('.') < 0) { - moduleId += '.js'; - } - return moduleId; - } - - function ajax(toUrl) { - if (amdCfg.urlArgs != null) { - toUrl += '?' + amdCfg.urlArgs; - } - - return new Promise(function (promiseResolve, promiseReject) { - var xhr = window.XMLHttpRequest - ? new XMLHttpRequest() - : new ActiveXObject('Microsoft.XMLHTTP'); - - xhr.open('GET', toUrl, true); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - (xhr.status >= 200 && xhr.status < 300) - ? promiseResolve(xhr.responseText) - : promiseReject({ - status: xhr.status, - content: xhr.responseText - }); - xhr.onreadystatechange = new Function(); - xhr = null; - } - }; - - xhr.send(null); - }); - } - - // Nodejs `path.resolve`. - function resolve() { - var resolvedPath = ''; - var resolvedAbsolute; - - for (var i = arguments.length - 1; i >= 0 && !resolvedAbsolute; i--) { - var path = arguments[i]; - if (path) { - resolvedPath = path + '/' + resolvedPath; - resolvedAbsolute = path[0] === '/'; - } - } - - if (!resolvedAbsolute) { - throw new Error('At least one absolute path should be input.'); - } - - // Normalize the path - resolvedPath = normalizePathArray(resolvedPath.split('/'), false).join('/'); - - return '/' + resolvedPath; - } - - // resolves . and .. elements in a path array with directory names there - // must be no slashes or device names (c:\) in the array - // (so also no leading and trailing slashes - it does not distinguish - // relative and absolute paths) - function normalizePathArray(parts, allowAboveRoot) { - var res = []; - for (var i = 0; i < parts.length; i++) { - var p = parts[i]; - - // ignore empty parts - if (!p || p === '.') { - continue; - } - - if (p === '..') { - if (res.length && res[res.length - 1] !== '..') { - res.pop(); - } else if (allowAboveRoot) { - res.push('..'); - } - } else { - res.push(p); - } - } - - return res; - } - - function addSourceMap(code, map) { - // Use unescape(encodeURIComponent) to avoid the error on Chrome: - // Uncaught (in promise) DOMException: Failed to execute 'btoa' on 'Window': - // The string to be encoded contains characters outside of the Latin1 range - var dataURI = btoa(unescape(encodeURIComponent(map.toString()))); // jshint ignore:line - dataURI = 'data:application/json;charset=utf-8;base64,' + dataURI; - - // Split the string to prevent sourcemap tooling from mistaking - // this for an actual sourceMappingURL. - code += '//# ' + 'sourceMa' + 'ppingURL' + '=' + dataURI + '\n'; - - return code; - } - - function cwd() { - // Only support that works in browser. - return dir(location.pathname); - } - - function dir(path) { - if (path) { - return path.charAt(path.length - 1) === '/' ? path : resolve(path, '..'); - } - } - - function extend(target, source) { - for (var key in source) { - if (source.hasOwnProperty(key)) { - target[key] = source[key]; - } - } - return target; - } - - return requireES; - -})); diff --git a/test/lib/rollup.browser.js b/test/lib/rollup.browser.js deleted file mode 100644 index 5800e748e..000000000 --- a/test/lib/rollup.browser.js +++ /dev/null @@ -1,10626 +0,0 @@ -/* - Rollup.js v0.50.0 - Sat Sep 16 2017 09:48:09 GMT-0400 (EDT) - commit b949eb08169115ff66648838cbc4833379bf9440 - - - https://github.com/rollup/rollup - - Released under the MIT License. -*/ - -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (factory((global.rollup = {}))); -}(this, (function (exports) { 'use strict'; - -const DEBUG = false; -const map = new Map; - -let timeStartHelper; -let timeEndHelper; - -if ( typeof process === 'undefined' || typeof process.hrtime === 'undefined' ) { - timeStartHelper = function timeStartHelper () { - return window.performance.now(); - }; - - timeEndHelper = function timeEndHelper ( previous ) { - return window.performance.now() - previous; - }; -} else { - timeStartHelper = function timeStartHelper () { - return process.hrtime(); - }; - - timeEndHelper = function timeEndHelper ( previous ) { - const hrtime = process.hrtime( previous ); - return hrtime[0] * 1e3 + Math.floor( hrtime[1] / 1e6 ); - }; -} - -function timeStart ( label ) { - if ( !map.has( label ) ) { - map.set( label, { - time: 0 - }); - } - map.get( label ).start = timeStartHelper(); -} - -function timeEnd ( label ) { - if ( map.has( label ) ) { - const item = map.get( label ); - item.time += timeEndHelper( item.start ); - } -} - -function flushTime ( log ) { - if ( log === void 0 ) log = defaultLog; - - for ( const item of map.entries() ) { - log( item[0], item[1].time ); - } - map.clear(); -} - -function defaultLog ( label, time ) { - if ( DEBUG ) { - /* eslint-disable no-console */ - console.info( '%dms: %s', time, label ); - /* eslint-enable no-console */ - } -} - -const absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|/])/; -const relativePath = /^\.?\.\//; - -function isAbsolute ( path ) { - return absolutePath.test( path ); -} - -function isRelative ( path ) { - return relativePath.test( path ); -} - -function normalize ( path ) { - return path.replace( /\\/g, '/' ); -} - -function basename ( path ) { - return path.split( /(\/|\\)/ ).pop(); -} - -function dirname ( path ) { - const match = /(\/|\\)[^/\\]*$/.exec( path ); - if ( !match ) { return '.'; } - - const dir = path.slice( 0, -match[0].length ); - - // If `dir` is the empty string, we're at root. - return dir ? dir : '/'; -} - -function extname ( path ) { - const match = /\.[^.]+$/.exec( basename( path ) ); - if ( !match ) { return ''; } - return match[0]; -} - -function relative ( from, to ) { - const fromParts = from.split( /[/\\]/ ).filter( Boolean ); - const toParts = to.split( /[/\\]/ ).filter( Boolean ); - - while ( fromParts[0] && toParts[0] && fromParts[0] === toParts[0] ) { - fromParts.shift(); - toParts.shift(); - } - - while ( toParts[0] === '.' || toParts[0] === '..' ) { - const toPart = toParts.shift(); - if ( toPart === '..' ) { - fromParts.pop(); - } - } - - while ( fromParts.pop() ) { - toParts.unshift( '..' ); - } - - return toParts.join( '/' ); -} - -function resolve () { - var paths = [], len = arguments.length; - while ( len-- ) paths[ len ] = arguments[ len ]; - - let resolvedParts = paths.shift().split( /[/\\]/ ); - - paths.forEach( path => { - if ( isAbsolute( path ) ) { - resolvedParts = path.split( /[/\\]/ ); - } else { - const parts = path.split( /[/\\]/ ); - - while ( parts[0] === '.' || parts[0] === '..' ) { - const part = parts.shift(); - if ( part === '..' ) { - resolvedParts.pop(); - } - } - - resolvedParts.push.apply( resolvedParts, parts ); - } - }); - - return resolvedParts.join( '/' ); // TODO windows... -} - -const nope = method => `Cannot use fs.${method} inside browser`; - -const lstatSync = nope( 'lstatSync' ); -const readdirSync = nope( 'readdirSync' ); -const readFileSync = nope( 'readFileSync' ); -const realpathSync = nope( 'realpathSync' ); -const writeFile = nope( 'writeFile' ); - -var keys = Object.keys; - -function blank () { - return Object.create( null ); -} - -function forOwn ( object, func ) { - Object.keys( object ).forEach( key => func( object[ key ], key ) ); -} - -function assign ( target ) { - var sources = [], len = arguments.length - 1; - while ( len-- > 0 ) sources[ len ] = arguments[ len + 1 ]; - - sources.forEach( source => { - for ( const key in source ) { - if ( source.hasOwnProperty( key ) ) { target[ key ] = source[ key ]; } - } - }); - - return target; -} - -function mapSequence ( array, fn ) { - const results = []; - let promise = Promise.resolve(); - - function next ( member, i ) { - return Promise.resolve( fn( member ) ).then( value => results[i] = value ); - } - - for ( let i = 0; i < array.length; i += 1 ) { - promise = promise.then( () => next( array[i], i ) ); - } - - return promise.then( () => results ); -} - -function validateKeys ( actualKeys, allowedKeys ) { - let i = actualKeys.length; - - while ( i-- ) { - const key = actualKeys[i]; - - if ( allowedKeys.indexOf( key ) === -1 ) { - return new Error( - `Unexpected key '${ key }' found, expected one of: ${ allowedKeys.join( ', ' ) }` - ); - } - } -} - -function error ( props ) { - // use the same constructor as props (if it's an error object) - // so that err.name is preserved etc - // (Object.keys below does not update these values because they - // are properties on the prototype chain) - // basically if props is a SyntaxError it will not be overriden as a generic Error - const constructor = (props instanceof Error) ? props.constructor : Error; - const err = new constructor( props.message ); - - Object.keys( props ).forEach( key => { - err[ key ] = props[ key ]; - }); - - throw err; -} - -// this looks ridiculous, but it prevents sourcemap tooling from mistaking -// this for an actual sourceMappingURL -let SOURCEMAPPING_URL = 'sourceMa'; -SOURCEMAPPING_URL += 'ppingURL'; - -const SOURCEMAPPING_URL_RE = new RegExp( `^#\\s+${SOURCEMAPPING_URL}=.+\\n?` ); - -var charToInteger = {}; -var integerToChar = {}; - -'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split( '' ).forEach( function ( char, i ) { - charToInteger[ char ] = i; - integerToChar[ i ] = char; -}); - -function decode$1 ( string ) { - var result = [], - len = string.length, - i, - hasContinuationBit, - shift = 0, - value = 0, - integer, - shouldNegate; - - for ( i = 0; i < len; i += 1 ) { - integer = charToInteger[ string[i] ]; - - if ( integer === undefined ) { - throw new Error( 'Invalid character (' + string[i] + ')' ); - } - - hasContinuationBit = integer & 32; - - integer &= 31; - value += integer << shift; - - if ( hasContinuationBit ) { - shift += 5; - } else { - shouldNegate = value & 1; - value >>= 1; - - result.push( shouldNegate ? -value : value ); - - // reset - value = shift = 0; - } - } - - return result; -} - -function encode$1 ( value ) { - var result, i; - - if ( typeof value === 'number' ) { - result = encodeInteger( value ); - } else { - result = ''; - for ( i = 0; i < value.length; i += 1 ) { - result += encodeInteger( value[i] ); - } - } - - return result; -} - -function encodeInteger ( num ) { - var result = '', clamped; - - if ( num < 0 ) { - num = ( -num << 1 ) | 1; - } else { - num <<= 1; - } - - do { - clamped = num & 31; - num >>= 5; - - if ( num > 0 ) { - clamped |= 32; - } - - result += integerToChar[ clamped ]; - } while ( num > 0 ); - - return result; -} - -function decodeSegments ( encodedSegments ) { - var i = encodedSegments.length; - var segments = new Array( i ); - - while ( i-- ) { segments[i] = decode$1( encodedSegments[i] ); } - return segments; -} - -function decode$$1 ( mappings ) { - var sourceFileIndex = 0; // second field - var sourceCodeLine = 0; // third field - var sourceCodeColumn = 0; // fourth field - var nameIndex = 0; // fifth field - - var lines = mappings.split( ';' ); - var numLines = lines.length; - var decoded = new Array( numLines ); - - var i; - var j; - var line; - var generatedCodeColumn; - var decodedLine; - var segments; - var segment; - var result; - - for ( i = 0; i < numLines; i += 1 ) { - line = lines[i]; - - generatedCodeColumn = 0; // first field - reset each time - decodedLine = []; - - segments = decodeSegments( line.split( ',' ) ); - - for ( j = 0; j < segments.length; j += 1 ) { - segment = segments[j]; - - if ( !segment.length ) { - break; - } - - generatedCodeColumn += segment[0]; - - result = [ generatedCodeColumn ]; - decodedLine.push( result ); - - if ( segment.length === 1 ) { - // only one field! - continue; - } - - sourceFileIndex += segment[1]; - sourceCodeLine += segment[2]; - sourceCodeColumn += segment[3]; - - result.push( sourceFileIndex, sourceCodeLine, sourceCodeColumn ); - - if ( segment.length === 5 ) { - nameIndex += segment[4]; - result.push( nameIndex ); - } - } - - decoded[i] = decodedLine; - } - - return decoded; -} - -function encode$$1 ( decoded ) { - var offsets = { - generatedCodeColumn: 0, - sourceFileIndex: 0, // second field - sourceCodeLine: 0, // third field - sourceCodeColumn: 0, // fourth field - nameIndex: 0 // fifth field - }; - - return decoded.map( function (line) { - offsets.generatedCodeColumn = 0; // first field - reset each time - return line.map( encodeSegment ).join( ',' ); - }).join( ';' ); - - function encodeSegment ( segment ) { - if ( !segment.length ) { - return segment; - } - - var result = new Array( segment.length ); - - result[0] = segment[0] - offsets.generatedCodeColumn; - offsets.generatedCodeColumn = segment[0]; - - if ( segment.length === 1 ) { - // only one field! - return encode$1( result ); - } - - result[1] = segment[1] - offsets.sourceFileIndex; - result[2] = segment[2] - offsets.sourceCodeLine; - result[3] = segment[3] - offsets.sourceCodeColumn; - - offsets.sourceFileIndex = segment[1]; - offsets.sourceCodeLine = segment[2]; - offsets.sourceCodeColumn = segment[3]; - - if ( segment.length === 5 ) { - result[4] = segment[4] - offsets.nameIndex; - offsets.nameIndex = segment[4]; - } - - return encode$1( result ); - } -} - -var charToInteger$1 = {}; -var integerToChar$1 = {}; - -'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split( '' ).forEach( function ( char, i ) { - charToInteger$1[ char ] = i; - integerToChar$1[ i ] = char; -}); - - - -function encode ( value ) { - var result; - - if ( typeof value === 'number' ) { - result = encodeInteger$1( value ); - } else { - result = ''; - for ( var i = 0; i < value.length; i += 1 ) { - result += encodeInteger$1( value[i] ); - } - } - - return result; -} - -function encodeInteger$1 ( num ) { - var result = ''; - - if ( num < 0 ) { - num = ( -num << 1 ) | 1; - } else { - num <<= 1; - } - - do { - var clamped = num & 31; - num >>= 5; - - if ( num > 0 ) { - clamped |= 32; - } - - result += integerToChar$1[ clamped ]; - } while ( num > 0 ); - - return result; -} - -function Chunk ( start, end, content ) { - this.start = start; - this.end = end; - this.original = content; - - this.intro = ''; - this.outro = ''; - - this.content = content; - this.storeName = false; - this.edited = false; - - // we make these non-enumerable, for sanity while debugging - Object.defineProperties( this, { - previous: { writable: true, value: null }, - next: { writable: true, value: null } - }); -} - -Chunk.prototype = { - appendLeft: function appendLeft ( content ) { - this.outro += content; - }, - - appendRight: function appendRight ( content ) { - this.intro = this.intro + content; - }, - - clone: function clone () { - var chunk = new Chunk( this.start, this.end, this.original ); - - chunk.intro = this.intro; - chunk.outro = this.outro; - chunk.content = this.content; - chunk.storeName = this.storeName; - chunk.edited = this.edited; - - return chunk; - }, - - contains: function contains ( index ) { - return this.start < index && index < this.end; - }, - - eachNext: function eachNext ( fn ) { - var chunk = this; - while ( chunk ) { - fn( chunk ); - chunk = chunk.next; - } - }, - - eachPrevious: function eachPrevious ( fn ) { - var chunk = this; - while ( chunk ) { - fn( chunk ); - chunk = chunk.previous; - } - }, - - edit: function edit ( content, storeName, contentOnly ) { - this.content = content; - if ( !contentOnly ) { - this.intro = ''; - this.outro = ''; - } - this.storeName = storeName; - - this.edited = true; - - return this; - }, - - prependLeft: function prependLeft ( content ) { - this.outro = content + this.outro; - }, - - prependRight: function prependRight ( content ) { - this.intro = content + this.intro; - }, - - split: function split ( index ) { - var sliceIndex = index - this.start; - - var originalBefore = this.original.slice( 0, sliceIndex ); - var originalAfter = this.original.slice( sliceIndex ); - - this.original = originalBefore; - - var newChunk = new Chunk( index, this.end, originalAfter ); - newChunk.outro = this.outro; - this.outro = ''; - - this.end = index; - - if ( this.edited ) { - // TODO is this block necessary?... - newChunk.edit( '', false ); - this.content = ''; - } else { - this.content = originalBefore; - } - - newChunk.next = this.next; - if ( newChunk.next ) { newChunk.next.previous = newChunk; } - newChunk.previous = this; - this.next = newChunk; - - return newChunk; - }, - - toString: function toString () { - return this.intro + this.content + this.outro; - }, - - trimEnd: function trimEnd ( rx ) { - this.outro = this.outro.replace( rx, '' ); - if ( this.outro.length ) { return true; } - - var trimmed = this.content.replace( rx, '' ); - - if ( trimmed.length ) { - if ( trimmed !== this.content ) { - this.split( this.start + trimmed.length ).edit( '', false ); - } - - return true; - } else { - this.edit( '', false ); - - this.intro = this.intro.replace( rx, '' ); - if ( this.intro.length ) { return true; } - } - }, - - trimStart: function trimStart ( rx ) { - this.intro = this.intro.replace( rx, '' ); - if ( this.intro.length ) { return true; } - - var trimmed = this.content.replace( rx, '' ); - - if ( trimmed.length ) { - if ( trimmed !== this.content ) { - this.split( this.end - trimmed.length ); - this.edit( '', false ); - } - - return true; - } else { - this.edit( '', false ); - - this.outro = this.outro.replace( rx, '' ); - if ( this.outro.length ) { return true; } - } - } -}; - -var _btoa; - -if ( typeof window !== 'undefined' && typeof window.btoa === 'function' ) { - _btoa = window.btoa; -} else if ( typeof Buffer === 'function' ) { - _btoa = function (str) { return new Buffer( str ).toString( 'base64' ); }; -} else { - _btoa = function () { - throw new Error( 'Unsupported environment: `window.btoa` or `Buffer` should be supported.' ); - }; -} - -var btoa = _btoa; - -function SourceMap ( properties ) { - this.version = 3; - - this.file = properties.file; - this.sources = properties.sources; - this.sourcesContent = properties.sourcesContent; - this.names = properties.names; - this.mappings = properties.mappings; -} - -SourceMap.prototype = { - toString: function toString () { - return JSON.stringify( this ); - }, - - toUrl: function toUrl () { - return 'data:application/json;charset=utf-8;base64,' + btoa( this.toString() ); - } -}; - -function guessIndent ( code ) { - var lines = code.split( '\n' ); - - var tabbed = lines.filter( function (line) { return /^\t+/.test( line ); } ); - var spaced = lines.filter( function (line) { return /^ {2,}/.test( line ); } ); - - if ( tabbed.length === 0 && spaced.length === 0 ) { - return null; - } - - // More lines tabbed than spaced? Assume tabs, and - // default to tabs in the case of a tie (or nothing - // to go on) - if ( tabbed.length >= spaced.length ) { - return '\t'; - } - - // Otherwise, we need to guess the multiple - var min = spaced.reduce( function ( previous, current ) { - var numSpaces = /^ +/.exec( current )[0].length; - return Math.min( numSpaces, previous ); - }, Infinity ); - - return new Array( min + 1 ).join( ' ' ); -} - -function getRelativePath ( from, to ) { - var fromParts = from.split( /[\/\\]/ ); - var toParts = to.split( /[\/\\]/ ); - - fromParts.pop(); // get dirname - - while ( fromParts[0] === toParts[0] ) { - fromParts.shift(); - toParts.shift(); - } - - if ( fromParts.length ) { - var i = fromParts.length; - while ( i-- ) { fromParts[i] = '..'; } - } - - return fromParts.concat( toParts ).join( '/' ); -} - -var toString$1 = Object.prototype.toString; - -function isObject ( thing ) { - return toString$1.call( thing ) === '[object Object]'; -} - -function getLocator ( source ) { - var originalLines = source.split( '\n' ); - - var start = 0; - var lineRanges = originalLines.map( function ( line, i ) { - var end = start + line.length + 1; - var range = { start: start, end: end, line: i }; - - start = end; - return range; - }); - - var i = 0; - - function rangeContains ( range, index ) { - return range.start <= index && index < range.end; - } - - function getLocation ( range, index ) { - return { line: range.line, column: index - range.start }; - } - - return function locate ( index ) { - var range = lineRanges[i]; - - var d = index >= range.end ? 1 : -1; - - while ( range ) { - if ( rangeContains( range, index ) ) { return getLocation( range, index ); } - - i += d; - range = lineRanges[i]; - } - }; -} - -function Mappings ( hires ) { - var this$1 = this; - - var offsets = { - generatedCodeColumn: 0, - sourceIndex: 0, - sourceCodeLine: 0, - sourceCodeColumn: 0, - sourceCodeName: 0 - }; - - var generatedCodeLine = 0; - var generatedCodeColumn = 0; - - this.raw = []; - var rawSegments = this.raw[ generatedCodeLine ] = []; - - var pending = null; - - this.addEdit = function ( sourceIndex, content, original, loc, nameIndex ) { - if ( content.length ) { - rawSegments.push([ - generatedCodeColumn, - sourceIndex, - loc.line, - loc.column, - nameIndex ]); - } else if ( pending ) { - rawSegments.push( pending ); - } - - this$1.advance( content ); - pending = null; - }; - - this.addUneditedChunk = function ( sourceIndex, chunk, original, loc, sourcemapLocations ) { - var originalCharIndex = chunk.start; - var first = true; - - while ( originalCharIndex < chunk.end ) { - if ( hires || first || sourcemapLocations[ originalCharIndex ] ) { - rawSegments.push([ - generatedCodeColumn, - sourceIndex, - loc.line, - loc.column, - -1 - ]); - } - - if ( original[ originalCharIndex ] === '\n' ) { - loc.line += 1; - loc.column = 0; - generatedCodeLine += 1; - this$1.raw[ generatedCodeLine ] = rawSegments = []; - generatedCodeColumn = 0; - } else { - loc.column += 1; - generatedCodeColumn += 1; - } - - originalCharIndex += 1; - first = false; - } - - pending = [ - generatedCodeColumn, - sourceIndex, - loc.line, - loc.column, - -1 ]; - }; - - this.advance = function (str) { - if ( !str ) { return; } - - var lines = str.split( '\n' ); - var lastLine = lines.pop(); - - if ( lines.length ) { - generatedCodeLine += lines.length; - this$1.raw[ generatedCodeLine ] = rawSegments = []; - generatedCodeColumn = lastLine.length; - } else { - generatedCodeColumn += lastLine.length; - } - }; - - this.encode = function () { - return this$1.raw.map( function (segments) { - var generatedCodeColumn = 0; - - return segments.map( function (segment) { - var arr = [ - segment[0] - generatedCodeColumn, - segment[1] - offsets.sourceIndex, - segment[2] - offsets.sourceCodeLine, - segment[3] - offsets.sourceCodeColumn - ]; - - generatedCodeColumn = segment[0]; - offsets.sourceIndex = segment[1]; - offsets.sourceCodeLine = segment[2]; - offsets.sourceCodeColumn = segment[3]; - - if ( ~segment[4] ) { - arr.push( segment[4] - offsets.sourceCodeName ); - offsets.sourceCodeName = segment[4]; - } - - return encode( arr ); - }).join( ',' ); - }).join( ';' ); - }; -} - -var Stats = function Stats () { - Object.defineProperties( this, { - startTimes: { value: {} } - }); -}; - -Stats.prototype.time = function time ( label ) { - this.startTimes[ label ] = process.hrtime(); -}; - -Stats.prototype.timeEnd = function timeEnd ( label ) { - var elapsed = process.hrtime( this.startTimes[ label ] ); - - if ( !this[ label ] ) { this[ label ] = 0; } - this[ label ] += elapsed[0] * 1e3 + elapsed[1] * 1e-6; -}; - -var warned = { - insertLeft: false, - insertRight: false, - storeName: false -}; - -function MagicString$1 ( string, options ) { - if ( options === void 0 ) options = {}; - - var chunk = new Chunk( 0, string.length, string ); - - Object.defineProperties( this, { - original: { writable: true, value: string }, - outro: { writable: true, value: '' }, - intro: { writable: true, value: '' }, - firstChunk: { writable: true, value: chunk }, - lastChunk: { writable: true, value: chunk }, - lastSearchedChunk: { writable: true, value: chunk }, - byStart: { writable: true, value: {} }, - byEnd: { writable: true, value: {} }, - filename: { writable: true, value: options.filename }, - indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, - sourcemapLocations: { writable: true, value: {} }, - storedNames: { writable: true, value: {} }, - indentStr: { writable: true, value: guessIndent( string ) } - }); - - this.byStart[ 0 ] = chunk; - this.byEnd[ string.length ] = chunk; -} - -MagicString$1.prototype = { - addSourcemapLocation: function addSourcemapLocation ( char ) { - this.sourcemapLocations[ char ] = true; - }, - - append: function append ( content ) { - if ( typeof content !== 'string' ) { throw new TypeError( 'outro content must be a string' ); } - - this.outro += content; - return this; - }, - - appendLeft: function appendLeft ( index, content ) { - if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); } - - this._split( index ); - - var chunk = this.byEnd[ index ]; - - if ( chunk ) { - chunk.appendLeft( content ); - } else { - this.intro += content; - } - - return this; - }, - - appendRight: function appendRight ( index, content ) { - if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); } - - this._split( index ); - - var chunk = this.byStart[ index ]; - - if ( chunk ) { - chunk.appendRight( content ); - } else { - this.outro += content; - } - - return this; - }, - - clone: function clone () { - var cloned = new MagicString$1( this.original, { filename: this.filename }); - - var originalChunk = this.firstChunk; - var clonedChunk = cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone(); - - while ( originalChunk ) { - cloned.byStart[ clonedChunk.start ] = clonedChunk; - cloned.byEnd[ clonedChunk.end ] = clonedChunk; - - var nextOriginalChunk = originalChunk.next; - var nextClonedChunk = nextOriginalChunk && nextOriginalChunk.clone(); - - if ( nextClonedChunk ) { - clonedChunk.next = nextClonedChunk; - nextClonedChunk.previous = clonedChunk; - - clonedChunk = nextClonedChunk; - } - - originalChunk = nextOriginalChunk; - } - - cloned.lastChunk = clonedChunk; - - if ( this.indentExclusionRanges ) { - cloned.indentExclusionRanges = this.indentExclusionRanges.slice(); - } - - Object.keys( this.sourcemapLocations ).forEach( function (loc) { - cloned.sourcemapLocations[ loc ] = true; - }); - - return cloned; - }, - - generateMap: function generateMap ( options ) { - var this$1 = this; - - options = options || {}; - - var sourceIndex = 0; - var names = Object.keys( this.storedNames ); - var mappings = new Mappings( options.hires ); - - var locate = getLocator( this.original ); - - if ( this.intro ) { - mappings.advance( this.intro ); - } - - this.firstChunk.eachNext( function (chunk) { - var loc = locate( chunk.start ); - - if ( chunk.intro.length ) { mappings.advance( chunk.intro ); } - - if ( chunk.edited ) { - mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); - } else { - mappings.addUneditedChunk( sourceIndex, chunk, this$1.original, loc, this$1.sourcemapLocations ); - } - - if ( chunk.outro.length ) { mappings.advance( chunk.outro ); } - }); - - var map = new SourceMap({ - file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ), - sources: [ options.source ? getRelativePath( options.file || '', options.source ) : null ], - sourcesContent: options.includeContent ? [ this.original ] : [ null ], - names: names, - mappings: mappings.encode() - }); - return map; - }, - - getIndentString: function getIndentString () { - return this.indentStr === null ? '\t' : this.indentStr; - }, - - indent: function indent ( indentStr, options ) { - var this$1 = this; - - var pattern = /^[^\r\n]/gm; - - if ( isObject( indentStr ) ) { - options = indentStr; - indentStr = undefined; - } - - indentStr = indentStr !== undefined ? indentStr : ( this.indentStr || '\t' ); - - if ( indentStr === '' ) { return this; } // noop - - options = options || {}; - - // Process exclusion ranges - var isExcluded = {}; - - if ( options.exclude ) { - var exclusions = typeof options.exclude[0] === 'number' ? [ options.exclude ] : options.exclude; - exclusions.forEach( function (exclusion) { - for ( var i = exclusion[0]; i < exclusion[1]; i += 1 ) { - isExcluded[i] = true; - } - }); - } - - var shouldIndentNextCharacter = options.indentStart !== false; - var replacer = function (match) { - if ( shouldIndentNextCharacter ) { return ("" + indentStr + match); } - shouldIndentNextCharacter = true; - return match; - }; - - this.intro = this.intro.replace( pattern, replacer ); - - var charIndex = 0; - - var chunk = this.firstChunk; - - while ( chunk ) { - var end = chunk.end; - - if ( chunk.edited ) { - if ( !isExcluded[ charIndex ] ) { - chunk.content = chunk.content.replace( pattern, replacer ); - - if ( chunk.content.length ) { - shouldIndentNextCharacter = chunk.content[ chunk.content.length - 1 ] === '\n'; - } - } - } else { - charIndex = chunk.start; - - while ( charIndex < end ) { - if ( !isExcluded[ charIndex ] ) { - var char = this$1.original[ charIndex ]; - - if ( char === '\n' ) { - shouldIndentNextCharacter = true; - } else if ( char !== '\r' && shouldIndentNextCharacter ) { - shouldIndentNextCharacter = false; - - if ( charIndex === chunk.start ) { - chunk.prependRight( indentStr ); - } else { - this$1._splitChunk( chunk, charIndex ); - chunk = chunk.next; - chunk.prependRight( indentStr ); - } - } - } - - charIndex += 1; - } - } - - charIndex = chunk.end; - chunk = chunk.next; - } - - this.outro = this.outro.replace( pattern, replacer ); - - return this; - }, - - insert: function insert () { - throw new Error( 'magicString.insert(...) is deprecated. Use prependRight(...) or appendLeft(...)' ); - }, - - insertLeft: function insertLeft ( index, content ) { - if ( !warned.insertLeft ) { - console.warn( 'magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead' ); // eslint-disable-line no-console - warned.insertLeft = true; - } - - return this.appendLeft( index, content ); - }, - - insertRight: function insertRight ( index, content ) { - if ( !warned.insertRight ) { - console.warn( 'magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead' ); // eslint-disable-line no-console - warned.insertRight = true; - } - - return this.prependRight( index, content ); - }, - - move: function move ( start, end, index ) { - if ( index >= start && index <= end ) { throw new Error( 'Cannot move a selection inside itself' ); } - - this._split( start ); - this._split( end ); - this._split( index ); - - var first = this.byStart[ start ]; - var last = this.byEnd[ end ]; - - var oldLeft = first.previous; - var oldRight = last.next; - - var newRight = this.byStart[ index ]; - if ( !newRight && last === this.lastChunk ) { return this; } - var newLeft = newRight ? newRight.previous : this.lastChunk; - - if ( oldLeft ) { oldLeft.next = oldRight; } - if ( oldRight ) { oldRight.previous = oldLeft; } - - if ( newLeft ) { newLeft.next = first; } - if ( newRight ) { newRight.previous = last; } - - if ( !first.previous ) { this.firstChunk = last.next; } - if ( !last.next ) { - this.lastChunk = first.previous; - this.lastChunk.next = null; - } - - first.previous = newLeft; - last.next = newRight || null; - - if ( !newLeft ) { this.firstChunk = first; } - if ( !newRight ) { this.lastChunk = last; } - - return this; - }, - - overwrite: function overwrite ( start, end, content, options ) { - var this$1 = this; - - if ( typeof content !== 'string' ) { throw new TypeError( 'replacement content must be a string' ); } - - while ( start < 0 ) { start += this$1.original.length; } - while ( end < 0 ) { end += this$1.original.length; } - - if ( end > this.original.length ) { throw new Error( 'end is out of bounds' ); } - if ( start === end ) { throw new Error( 'Cannot overwrite a zero-length range – use appendLeft or prependRight instead' ); } - - this._split( start ); - this._split( end ); - - if ( options === true ) { - if ( !warned.storeName ) { - console.warn( 'The final argument to magicString.overwrite(...) should be an options object. See https://github.com/rich-harris/magic-string' ); // eslint-disable-line no-console - warned.storeName = true; - } - - options = { storeName: true }; - } - var storeName = options !== undefined ? options.storeName : false; - var contentOnly = options !== undefined ? options.contentOnly : false; - - if ( storeName ) { - var original = this.original.slice( start, end ); - this.storedNames[ original ] = true; - } - - var first = this.byStart[ start ]; - var last = this.byEnd[ end ]; - - if ( first ) { - if ( end > first.end && first.next !== this.byStart[ first.end ] ) { - throw new Error( 'Cannot overwrite across a split point' ); - } - - first.edit( content, storeName, contentOnly ); - - if ( first !== last ) { - var chunk = first.next; - while ( chunk !== last ) { - chunk.edit( '', false ); - chunk = chunk.next; - } - - chunk.edit( '', false ); - } - } - - else { - // must be inserting at the end - var newChunk = new Chunk( start, end, '' ).edit( content, storeName ); - - // TODO last chunk in the array may not be the last chunk, if it's moved... - last.next = newChunk; - newChunk.previous = last; - } - - return this; - }, - - prepend: function prepend ( content ) { - if ( typeof content !== 'string' ) { throw new TypeError( 'outro content must be a string' ); } - - this.intro = content + this.intro; - return this; - }, - - prependLeft: function prependLeft ( index, content ) { - if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); } - - this._split( index ); - - var chunk = this.byEnd[ index ]; - - if ( chunk ) { - chunk.prependLeft( content ); - } else { - this.intro = content + this.intro; - } - - return this; - }, - - prependRight: function prependRight ( index, content ) { - if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); } - - this._split( index ); - - var chunk = this.byStart[ index ]; - - if ( chunk ) { - chunk.prependRight( content ); - } else { - this.outro = content + this.outro; - } - - return this; - }, - - remove: function remove ( start, end ) { - var this$1 = this; - - while ( start < 0 ) { start += this$1.original.length; } - while ( end < 0 ) { end += this$1.original.length; } - - if ( start === end ) { return this; } - - if ( start < 0 || end > this.original.length ) { throw new Error( 'Character is out of bounds' ); } - if ( start > end ) { throw new Error( 'end must be greater than start' ); } - - this._split( start ); - this._split( end ); - - var chunk = this.byStart[ start ]; - - while ( chunk ) { - chunk.intro = ''; - chunk.outro = ''; - chunk.edit( '' ); - - chunk = end > chunk.end ? this$1.byStart[ chunk.end ] : null; - } - - return this; - }, - - slice: function slice ( start, end ) { - var this$1 = this; - if ( start === void 0 ) start = 0; - if ( end === void 0 ) end = this.original.length; - - while ( start < 0 ) { start += this$1.original.length; } - while ( end < 0 ) { end += this$1.original.length; } - - var result = ''; - - // find start chunk - var chunk = this.firstChunk; - while ( chunk && ( chunk.start > start || chunk.end <= start ) ) { - - // found end chunk before start - if ( chunk.start < end && chunk.end >= end ) { - return result; - } - - chunk = chunk.next; - } - - if ( chunk && chunk.edited && chunk.start !== start ) { throw new Error(("Cannot use replaced character " + start + " as slice start anchor.")); } - - var startChunk = chunk; - while ( chunk ) { - if ( chunk.intro && ( startChunk !== chunk || chunk.start === start ) ) { - result += chunk.intro; - } - - var containsEnd = chunk.start < end && chunk.end >= end; - if ( containsEnd && chunk.edited && chunk.end !== end ) { throw new Error(("Cannot use replaced character " + end + " as slice end anchor.")); } - - var sliceStart = startChunk === chunk ? start - chunk.start : 0; - var sliceEnd = containsEnd ? chunk.content.length + end - chunk.end : chunk.content.length; - - result += chunk.content.slice( sliceStart, sliceEnd ); - - if ( chunk.outro && ( !containsEnd || chunk.end === end ) ) { - result += chunk.outro; - } - - if ( containsEnd ) { - break; - } - - chunk = chunk.next; - } - - return result; - }, - - // TODO deprecate this? not really very useful - snip: function snip ( start, end ) { - var clone = this.clone(); - clone.remove( 0, start ); - clone.remove( end, clone.original.length ); - - return clone; - }, - - _split: function _split ( index ) { - var this$1 = this; - - if ( this.byStart[ index ] || this.byEnd[ index ] ) { return; } - - var chunk = this.lastSearchedChunk; - var searchForward = index > chunk.end; - - while ( true ) { - if ( chunk.contains( index ) ) { return this$1._splitChunk( chunk, index ); } - - chunk = searchForward ? - this$1.byStart[ chunk.end ] : - this$1.byEnd[ chunk.start ]; - } - }, - - _splitChunk: function _splitChunk ( chunk, index ) { - if ( chunk.edited && chunk.content.length ) { // zero-length edited chunks are a special case (overlapping replacements) - var loc = getLocator( this.original )( index ); - throw new Error( ("Cannot split a chunk that has already been edited (" + (loc.line) + ":" + (loc.column) + " – \"" + (chunk.original) + "\")") ); - } - - var newChunk = chunk.split( index ); - - this.byEnd[ index ] = chunk; - this.byStart[ index ] = newChunk; - this.byEnd[ newChunk.end ] = newChunk; - - if ( chunk === this.lastChunk ) { this.lastChunk = newChunk; } - - this.lastSearchedChunk = chunk; - return true; - }, - - toString: function toString () { - var str = this.intro; - - var chunk = this.firstChunk; - while ( chunk ) { - str += chunk.toString(); - chunk = chunk.next; - } - - return str + this.outro; - }, - - trimLines: function trimLines () { - return this.trim('[\\r\\n]'); - }, - - trim: function trim ( charType ) { - return this.trimStart( charType ).trimEnd( charType ); - }, - - trimEnd: function trimEnd ( charType ) { - var this$1 = this; - - var rx = new RegExp( ( charType || '\\s' ) + '+$' ); - - this.outro = this.outro.replace( rx, '' ); - if ( this.outro.length ) { return this; } - - var chunk = this.lastChunk; - - do { - var end = chunk.end; - var aborted = chunk.trimEnd( rx ); - - // if chunk was trimmed, we have a new lastChunk - if ( chunk.end !== end ) { - if ( this$1.lastChunk === chunk ) { - this$1.lastChunk = chunk.next; - } - - this$1.byEnd[ chunk.end ] = chunk; - this$1.byStart[ chunk.next.start ] = chunk.next; - this$1.byEnd[ chunk.next.end ] = chunk.next; - } - - if ( aborted ) { return this$1; } - chunk = chunk.previous; - } while ( chunk ); - - return this; - }, - - trimStart: function trimStart ( charType ) { - var this$1 = this; - - var rx = new RegExp( '^' + ( charType || '\\s' ) + '+' ); - - this.intro = this.intro.replace( rx, '' ); - if ( this.intro.length ) { return this; } - - var chunk = this.firstChunk; - - do { - var end = chunk.end; - var aborted = chunk.trimStart( rx ); - - if ( chunk.end !== end ) { - // special case... - if ( chunk === this$1.lastChunk ) { this$1.lastChunk = chunk.next; } - - this$1.byEnd[ chunk.end ] = chunk; - this$1.byStart[ chunk.next.start ] = chunk.next; - this$1.byEnd[ chunk.next.end ] = chunk.next; - } - - if ( aborted ) { return this$1; } - chunk = chunk.next; - } while ( chunk ); - - return this; - } -}; - -var hasOwnProp = Object.prototype.hasOwnProperty; - -function Bundle$1 ( options ) { - if ( options === void 0 ) options = {}; - - this.intro = options.intro || ''; - this.separator = options.separator !== undefined ? options.separator : '\n'; - - this.sources = []; - - this.uniqueSources = []; - this.uniqueSourceIndexByFilename = {}; -} - -Bundle$1.prototype = { - addSource: function addSource ( source ) { - if ( source instanceof MagicString$1 ) { - return this.addSource({ - content: source, - filename: source.filename, - separator: this.separator - }); - } - - if ( !isObject( source ) || !source.content ) { - throw new Error( 'bundle.addSource() takes an object with a `content` property, which should be an instance of MagicString, and an optional `filename`' ); - } - - [ 'filename', 'indentExclusionRanges', 'separator' ].forEach( function (option) { - if ( !hasOwnProp.call( source, option ) ) { source[ option ] = source.content[ option ]; } - }); - - if ( source.separator === undefined ) { // TODO there's a bunch of this sort of thing, needs cleaning up - source.separator = this.separator; - } - - if ( source.filename ) { - if ( !hasOwnProp.call( this.uniqueSourceIndexByFilename, source.filename ) ) { - this.uniqueSourceIndexByFilename[ source.filename ] = this.uniqueSources.length; - this.uniqueSources.push({ filename: source.filename, content: source.content.original }); - } else { - var uniqueSource = this.uniqueSources[ this.uniqueSourceIndexByFilename[ source.filename ] ]; - if ( source.content.original !== uniqueSource.content ) { - throw new Error( ("Illegal source: same filename (" + (source.filename) + "), different contents") ); - } - } - } - - this.sources.push( source ); - return this; - }, - - append: function append ( str, options ) { - this.addSource({ - content: new MagicString$1( str ), - separator: ( options && options.separator ) || '' - }); - - return this; - }, - - clone: function clone () { - var bundle = new Bundle$1({ - intro: this.intro, - separator: this.separator - }); - - this.sources.forEach( function (source) { - bundle.addSource({ - filename: source.filename, - content: source.content.clone(), - separator: source.separator - }); - }); - - return bundle; - }, - - generateMap: function generateMap ( options ) { - var this$1 = this; - if ( options === void 0 ) options = {}; - - var names = []; - this.sources.forEach( function (source) { - Object.keys( source.content.storedNames ).forEach( function (name) { - if ( !~names.indexOf( name ) ) { names.push( name ); } - }); - }); - - var mappings = new Mappings( options.hires ); - - if ( this.intro ) { - mappings.advance( this.intro ); - } - - this.sources.forEach( function ( source, i ) { - if ( i > 0 ) { - mappings.advance( this$1.separator ); - } - - var sourceIndex = source.filename ? this$1.uniqueSourceIndexByFilename[ source.filename ] : -1; - var magicString = source.content; - var locate = getLocator( magicString.original ); - - if ( magicString.intro ) { - mappings.advance( magicString.intro ); - } - - magicString.firstChunk.eachNext( function (chunk) { - var loc = locate( chunk.start ); - - if ( chunk.intro.length ) { mappings.advance( chunk.intro ); } - - if ( source.filename ) { - if ( chunk.edited ) { - mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); - } else { - mappings.addUneditedChunk( sourceIndex, chunk, magicString.original, loc, magicString.sourcemapLocations ); - } - } - - else { - mappings.advance( chunk.content ); - } - - if ( chunk.outro.length ) { mappings.advance( chunk.outro ); } - }); - - if ( magicString.outro ) { - mappings.advance( magicString.outro ); - } - }); - - return new SourceMap({ - file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ), - sources: this.uniqueSources.map( function (source) { - return options.file ? getRelativePath( options.file, source.filename ) : source.filename; - }), - sourcesContent: this.uniqueSources.map( function (source) { - return options.includeContent ? source.content : null; - }), - names: names, - mappings: mappings.encode() - }); - }, - - getIndentString: function getIndentString () { - var indentStringCounts = {}; - - this.sources.forEach( function (source) { - var indentStr = source.content.indentStr; - - if ( indentStr === null ) { return; } - - if ( !indentStringCounts[ indentStr ] ) { indentStringCounts[ indentStr ] = 0; } - indentStringCounts[ indentStr ] += 1; - }); - - return ( Object.keys( indentStringCounts ).sort( function ( a, b ) { - return indentStringCounts[a] - indentStringCounts[b]; - })[0] ) || '\t'; - }, - - indent: function indent ( indentStr ) { - var this$1 = this; - - if ( !arguments.length ) { - indentStr = this.getIndentString(); - } - - if ( indentStr === '' ) { return this; } // noop - - var trailingNewline = !this.intro || this.intro.slice( -1 ) === '\n'; - - this.sources.forEach( function ( source, i ) { - var separator = source.separator !== undefined ? source.separator : this$1.separator; - var indentStart = trailingNewline || ( i > 0 && /\r?\n$/.test( separator ) ); - - source.content.indent( indentStr, { - exclude: source.indentExclusionRanges, - indentStart: indentStart//: trailingNewline || /\r?\n$/.test( separator ) //true///\r?\n/.test( separator ) - }); - - // TODO this is a very slow way to determine this - trailingNewline = source.content.toString().slice( 0, -1 ) === '\n'; - }); - - if ( this.intro ) { - this.intro = indentStr + this.intro.replace( /^[^\n]/gm, function ( match, index ) { - return index > 0 ? indentStr + match : match; - }); - } - - return this; - }, - - prepend: function prepend ( str ) { - this.intro = str + this.intro; - return this; - }, - - toString: function toString () { - var this$1 = this; - - var body = this.sources.map( function ( source, i ) { - var separator = source.separator !== undefined ? source.separator : this$1.separator; - var str = ( i > 0 ? separator : '' ) + source.content.toString(); - - return str; - }).join( '' ); - - return this.intro + body; - }, - - trimLines: function trimLines () { - return this.trim('[\\r\\n]'); - }, - - trim: function trim ( charType ) { - return this.trimStart( charType ).trimEnd( charType ); - }, - - trimStart: function trimStart ( charType ) { - var this$1 = this; - - var rx = new RegExp( '^' + ( charType || '\\s' ) + '+' ); - this.intro = this.intro.replace( rx, '' ); - - if ( !this.intro ) { - var source; - var i = 0; - - do { - source = this$1.sources[i]; - - if ( !source ) { - break; - } - - source.content.trimStart( charType ); - i += 1; - } while ( source.content.toString() === '' ); // TODO faster way to determine non-empty source? - } - - return this; - }, - - trimEnd: function trimEnd ( charType ) { - var this$1 = this; - - var rx = new RegExp( ( charType || '\\s' ) + '+$' ); - - var source; - var i = this.sources.length - 1; - - do { - source = this$1.sources[i]; - - if ( !source ) { - this$1.intro = this$1.intro.replace( rx, '' ); - break; - } - - source.content.trimEnd( charType ); - i -= 1; - } while ( source.content.toString() === '' ); // TODO faster way to determine non-empty source? - - return this; - } -}; - -// Return the first non-falsy result from an array of -// maybe-sync, maybe-promise-returning functions -function first ( candidates ) { - return function () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - return candidates.reduce( ( promise, candidate ) => { - return promise.then( result => result != null ? - result : - Promise.resolve( candidate.apply( void 0, args ) ) ); - }, Promise.resolve() ); - }; -} - -function find ( array, fn ) { - for ( let i = 0; i < array.length; i += 1 ) { - if ( fn( array[i], i ) ) { return array[i]; } - } - - return null; -} - -// Reserved word lists for various dialects of the language - -var reservedWords = { - 3: "abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile", - 5: "class enum extends super const export import", - 6: "enum", - strict: "implements interface let package private protected public static yield", - strictBind: "eval arguments" -}; - -// And the keywords - -var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"; - -var keywords = { - 5: ecma5AndLessKeywords, - 6: ecma5AndLessKeywords + " const class extends export import super" -}; - -// ## Character categories - -// Big ugly regular expressions that match characters in the -// whitespace, identifier, and identifier-start categories. These -// are only applied when a character is found to actually have a -// code point above 128. -// Generated by `bin/generate-identifier-regex.js`. - -var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fd5\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7ae\ua7b0-\ua7b7\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab65\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; -var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d4-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; - -var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); -var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); - -nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; - -// These are a run-length and offset encoded representation of the -// >0xffff code points that are a valid part of identifiers. The -// offset starts at 0x10000, and each pair of numbers represents an -// offset to the next range, and then a size of the range. They were -// generated by bin/generate-identifier-regex.js - -// eslint-disable-next-line comma-spacing -var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,785,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,54,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,86,25,391,63,32,0,449,56,264,8,2,36,18,0,50,29,881,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,65,0,32,6124,20,754,9486,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,60,67,1213,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,3,5761,10591,541]; - -// eslint-disable-next-line comma-spacing -var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,10,2,4,9,83,11,7,0,161,11,6,9,7,3,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,87,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,423,9,838,7,2,7,17,9,57,21,2,13,19882,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,2214,6,110,6,6,9,792487,239]; - -// This has a complexity linear to the value of the code. The -// assumption is that looking up astral identifier characters is -// rare. -function isInAstralSet(code, set) { - var pos = 0x10000; - for (var i = 0; i < set.length; i += 2) { - pos += set[i]; - if (pos > code) { return false } - pos += set[i + 1]; - if (pos >= code) { return true } - } -} - -// Test whether a given character code starts an identifier. - -function isIdentifierStart(code, astral) { - if (code < 65) { return code === 36 } - if (code < 91) { return true } - if (code < 97) { return code === 95 } - if (code < 123) { return true } - if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) } - if (astral === false) { return false } - return isInAstralSet(code, astralIdentifierStartCodes) -} - -// Test whether a given character is part of an identifier. - -function isIdentifierChar(code, astral) { - if (code < 48) { return code === 36 } - if (code < 58) { return true } - if (code < 65) { return false } - if (code < 91) { return true } - if (code < 97) { return code === 95 } - if (code < 123) { return true } - if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) } - if (astral === false) { return false } - return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes) -} - -// ## Token types - -// The assignment of fine-grained, information-carrying type objects -// allows the tokenizer to store the information it has about a -// token in a way that is very cheap for the parser to look up. - -// All token type variables start with an underscore, to make them -// easy to recognize. - -// The `beforeExpr` property is used to disambiguate between regular -// expressions and divisions. It is set on all token types that can -// be followed by an expression (thus, a slash after them would be a -// regular expression). -// -// The `startsExpr` property is used to check if the token ends a -// `yield` expression. It is set on all token types that either can -// directly start an expression (like a quotation mark) or can -// continue an expression (like the body of a string). -// -// `isLoop` marks a keyword as starting a loop, which is important -// to know when parsing a label, in order to allow or disallow -// continue jumps to that label. - -var TokenType = function TokenType(label, conf) { - if ( conf === void 0 ) { conf = {}; } - - this.label = label; - this.keyword = conf.keyword; - this.beforeExpr = !!conf.beforeExpr; - this.startsExpr = !!conf.startsExpr; - this.isLoop = !!conf.isLoop; - this.isAssign = !!conf.isAssign; - this.prefix = !!conf.prefix; - this.postfix = !!conf.postfix; - this.binop = conf.binop || null; - this.updateContext = null; -}; - -function binop(name, prec) { - return new TokenType(name, {beforeExpr: true, binop: prec}) -} -var beforeExpr = {beforeExpr: true}; -var startsExpr = {startsExpr: true}; - -// Map keyword names to token types. - -var keywords$1 = {}; - -// Succinct definitions of keyword token types -function kw(name, options) { - if ( options === void 0 ) { options = {}; } - - options.keyword = name; - return keywords$1[name] = new TokenType(name, options) -} - -var types = { - num: new TokenType("num", startsExpr), - regexp: new TokenType("regexp", startsExpr), - string: new TokenType("string", startsExpr), - name: new TokenType("name", startsExpr), - eof: new TokenType("eof"), - - // Punctuation token types. - bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}), - bracketR: new TokenType("]"), - braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}), - braceR: new TokenType("}"), - parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}), - parenR: new TokenType(")"), - comma: new TokenType(",", beforeExpr), - semi: new TokenType(";", beforeExpr), - colon: new TokenType(":", beforeExpr), - dot: new TokenType("."), - question: new TokenType("?", beforeExpr), - arrow: new TokenType("=>", beforeExpr), - template: new TokenType("template"), - invalidTemplate: new TokenType("invalidTemplate"), - ellipsis: new TokenType("...", beforeExpr), - backQuote: new TokenType("`", startsExpr), - dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}), - - // Operators. These carry several kinds of properties to help the - // parser use them properly (the presence of these properties is - // what categorizes them as operators). - // - // `binop`, when present, specifies that this operator is a binary - // operator, and will refer to its precedence. - // - // `prefix` and `postfix` mark the operator as a prefix or postfix - // unary operator. - // - // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as - // binary operators with a very low precedence, that should result - // in AssignmentExpression nodes. - - eq: new TokenType("=", {beforeExpr: true, isAssign: true}), - assign: new TokenType("_=", {beforeExpr: true, isAssign: true}), - incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}), - prefix: new TokenType("!/~", {beforeExpr: true, prefix: true, startsExpr: true}), - logicalOR: binop("||", 1), - logicalAND: binop("&&", 2), - bitwiseOR: binop("|", 3), - bitwiseXOR: binop("^", 4), - bitwiseAND: binop("&", 5), - equality: binop("==/!=/===/!==", 6), - relational: binop("/<=/>=", 7), - bitShift: binop("<>/>>>", 8), - plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}), - modulo: binop("%", 10), - star: binop("*", 10), - slash: binop("/", 10), - starstar: new TokenType("**", {beforeExpr: true}), - - // Keyword token types. - _break: kw("break"), - _case: kw("case", beforeExpr), - _catch: kw("catch"), - _continue: kw("continue"), - _debugger: kw("debugger"), - _default: kw("default", beforeExpr), - _do: kw("do", {isLoop: true, beforeExpr: true}), - _else: kw("else", beforeExpr), - _finally: kw("finally"), - _for: kw("for", {isLoop: true}), - _function: kw("function", startsExpr), - _if: kw("if"), - _return: kw("return", beforeExpr), - _switch: kw("switch"), - _throw: kw("throw", beforeExpr), - _try: kw("try"), - _var: kw("var"), - _const: kw("const"), - _while: kw("while", {isLoop: true}), - _with: kw("with"), - _new: kw("new", {beforeExpr: true, startsExpr: true}), - _this: kw("this", startsExpr), - _super: kw("super", startsExpr), - _class: kw("class", startsExpr), - _extends: kw("extends", beforeExpr), - _export: kw("export"), - _import: kw("import"), - _null: kw("null", startsExpr), - _true: kw("true", startsExpr), - _false: kw("false", startsExpr), - _in: kw("in", {beforeExpr: true, binop: 7}), - _instanceof: kw("instanceof", {beforeExpr: true, binop: 7}), - _typeof: kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true}), - _void: kw("void", {beforeExpr: true, prefix: true, startsExpr: true}), - _delete: kw("delete", {beforeExpr: true, prefix: true, startsExpr: true}) -}; - -// Matches a whole line break (where CRLF is considered a single -// line break). Used to count lines. - -var lineBreak = /\r\n?|\n|\u2028|\u2029/; -var lineBreakG = new RegExp(lineBreak.source, "g"); - -function isNewLine(code) { - return code === 10 || code === 13 || code === 0x2028 || code === 0x2029 -} - -var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; - -var skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g; - -var ref = Object.prototype; -var hasOwnProperty = ref.hasOwnProperty; -var toString = ref.toString; - -// Checks if an object has a property. - -function has(obj, propName) { - return hasOwnProperty.call(obj, propName) -} - -var isArray = Array.isArray || (function (obj) { return ( - toString.call(obj) === "[object Array]" -); }); - -// These are used when `options.locations` is on, for the -// `startLoc` and `endLoc` properties. - -var Position = function Position(line, col) { - this.line = line; - this.column = col; -}; - -Position.prototype.offset = function offset (n) { - return new Position(this.line, this.column + n) -}; - -var SourceLocation = function SourceLocation(p, start, end) { - this.start = start; - this.end = end; - if (p.sourceFile !== null) { this.source = p.sourceFile; } -}; - -// The `getLineInfo` function is mostly useful when the -// `locations` option is off (for performance reasons) and you -// want to find the line/column position for a given character -// offset. `input` should be the code string that the offset refers -// into. - -function getLineInfo(input, offset) { - for (var line = 1, cur = 0;;) { - lineBreakG.lastIndex = cur; - var match = lineBreakG.exec(input); - if (match && match.index < offset) { - ++line; - cur = match.index + match[0].length; - } else { - return new Position(line, offset - cur) - } - } -} - -// A second optional argument can be given to further configure -// the parser process. These options are recognized: - -var defaultOptions = { - // `ecmaVersion` indicates the ECMAScript version to parse. Must - // be either 3, 5, 6 (2015), 7 (2016), or 8 (2017). This influences support - // for strict mode, the set of reserved words, and support for - // new syntax features. The default is 7. - ecmaVersion: 7, - // `sourceType` indicates the mode the code should be parsed in. - // Can be either `"script"` or `"module"`. This influences global - // strict mode and parsing of `import` and `export` declarations. - sourceType: "script", - // `onInsertedSemicolon` can be a callback that will be called - // when a semicolon is automatically inserted. It will be passed - // th position of the comma as an offset, and if `locations` is - // enabled, it is given the location as a `{line, column}` object - // as second argument. - onInsertedSemicolon: null, - // `onTrailingComma` is similar to `onInsertedSemicolon`, but for - // trailing commas. - onTrailingComma: null, - // By default, reserved words are only enforced if ecmaVersion >= 5. - // Set `allowReserved` to a boolean value to explicitly turn this on - // an off. When this option has the value "never", reserved words - // and keywords can also not be used as property names. - allowReserved: null, - // When enabled, a return at the top level is not considered an - // error. - allowReturnOutsideFunction: false, - // When enabled, import/export statements are not constrained to - // appearing at the top of the program. - allowImportExportEverywhere: false, - // When enabled, hashbang directive in the beginning of file - // is allowed and treated as a line comment. - allowHashBang: false, - // When `locations` is on, `loc` properties holding objects with - // `start` and `end` properties in `{line, column}` form (with - // line being 1-based and column 0-based) will be attached to the - // nodes. - locations: false, - // A function can be passed as `onToken` option, which will - // cause Acorn to call that function with object in the same - // format as tokens returned from `tokenizer().getToken()`. Note - // that you are not allowed to call the parser from the - // callback—that will corrupt its internal state. - onToken: null, - // A function can be passed as `onComment` option, which will - // cause Acorn to call that function with `(block, text, start, - // end)` parameters whenever a comment is skipped. `block` is a - // boolean indicating whether this is a block (`/* */`) comment, - // `text` is the content of the comment, and `start` and `end` are - // character offsets that denote the start and end of the comment. - // When the `locations` option is on, two more parameters are - // passed, the full `{line, column}` locations of the start and - // end of the comments. Note that you are not allowed to call the - // parser from the callback—that will corrupt its internal state. - onComment: null, - // Nodes have their start and end characters offsets recorded in - // `start` and `end` properties (directly on the node, rather than - // the `loc` object, which holds line/column data. To also add a - // [semi-standardized][range] `range` property holding a `[start, - // end]` array with the same numbers, set the `ranges` option to - // `true`. - // - // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678 - ranges: false, - // It is possible to parse multiple files into a single AST by - // passing the tree produced by parsing the first file as - // `program` option in subsequent parses. This will add the - // toplevel forms of the parsed file to the `Program` (top) node - // of an existing parse tree. - program: null, - // When `locations` is on, you can pass this to record the source - // file in every node's `loc` object. - sourceFile: null, - // This value, if given, is stored in every node, whether - // `locations` is on or off. - directSourceFile: null, - // When enabled, parenthesized expressions are represented by - // (non-standard) ParenthesizedExpression nodes - preserveParens: false, - plugins: {} -}; - -// Interpret and default an options object - -function getOptions(opts) { - var options = {}; - - for (var opt in defaultOptions) - { options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]; } - - if (options.ecmaVersion >= 2015) - { options.ecmaVersion -= 2009; } - - if (options.allowReserved == null) - { options.allowReserved = options.ecmaVersion < 5; } - - if (isArray(options.onToken)) { - var tokens = options.onToken; - options.onToken = function (token) { return tokens.push(token); }; - } - if (isArray(options.onComment)) - { options.onComment = pushComment(options, options.onComment); } - - return options -} - -function pushComment(options, array) { - return function(block, text, start, end, startLoc, endLoc) { - var comment = { - type: block ? "Block" : "Line", - value: text, - start: start, - end: end - }; - if (options.locations) - { comment.loc = new SourceLocation(this, startLoc, endLoc); } - if (options.ranges) - { comment.range = [start, end]; } - array.push(comment); - } -} - -// Registered plugins -var plugins = {}; - -function keywordRegexp(words) { - return new RegExp("^(?:" + words.replace(/ /g, "|") + ")$") -} - -var Parser = function Parser(options, input, startPos) { - this.options = options = getOptions(options); - this.sourceFile = options.sourceFile; - this.keywords = keywordRegexp(keywords[options.ecmaVersion >= 6 ? 6 : 5]); - var reserved = ""; - if (!options.allowReserved) { - for (var v = options.ecmaVersion;; v--) - { if (reserved = reservedWords[v]) { break } } - if (options.sourceType == "module") { reserved += " await"; } - } - this.reservedWords = keywordRegexp(reserved); - var reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict; - this.reservedWordsStrict = keywordRegexp(reservedStrict); - this.reservedWordsStrictBind = keywordRegexp(reservedStrict + " " + reservedWords.strictBind); - this.input = String(input); - - // Used to signal to callers of `readWord1` whether the word - // contained any escape sequences. This is needed because words with - // escape sequences must not be interpreted as keywords. - this.containsEsc = false; - - // Load plugins - this.loadPlugins(options.plugins); - - // Set up token state - - // The current position of the tokenizer in the input. - if (startPos) { - this.pos = startPos; - this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1; - this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length; - } else { - this.pos = this.lineStart = 0; - this.curLine = 1; - } - - // Properties of the current token: - // Its type - this.type = types.eof; - // For tokens that include more information than their type, the value - this.value = null; - // Its start and end offset - this.start = this.end = this.pos; - // And, if locations are used, the {line, column} object - // corresponding to those offsets - this.startLoc = this.endLoc = this.curPosition(); - - // Position information for the previous token - this.lastTokEndLoc = this.lastTokStartLoc = null; - this.lastTokStart = this.lastTokEnd = this.pos; - - // The context stack is used to superficially track syntactic - // context to predict whether a regular expression is allowed in a - // given position. - this.context = this.initialContext(); - this.exprAllowed = true; - - // Figure out if it's a module code. - this.inModule = options.sourceType === "module"; - this.strict = this.inModule || this.strictDirective(this.pos); - - // Used to signify the start of a potential arrow function - this.potentialArrowAt = -1; - - // Flags to track whether we are in a function, a generator, an async function. - this.inFunction = this.inGenerator = this.inAsync = false; - // Positions to delayed-check that yield/await does not exist in default parameters. - this.yieldPos = this.awaitPos = 0; - // Labels in scope. - this.labels = []; - - // If enabled, skip leading hashbang line. - if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === "#!") - { this.skipLineComment(2); } - - // Scope tracking for duplicate variable names (see scope.js) - this.scopeStack = []; - this.enterFunctionScope(); -}; - -// DEPRECATED Kept for backwards compatibility until 3.0 in case a plugin uses them -Parser.prototype.isKeyword = function isKeyword (word) { return this.keywords.test(word) }; -Parser.prototype.isReservedWord = function isReservedWord (word) { return this.reservedWords.test(word) }; - -Parser.prototype.extend = function extend (name, f) { - this[name] = f(this[name]); -}; - -Parser.prototype.loadPlugins = function loadPlugins (pluginConfigs) { - var this$1 = this; - - for (var name in pluginConfigs) { - var plugin = plugins[name]; - if (!plugin) { throw new Error("Plugin '" + name + "' not found") } - plugin(this$1, pluginConfigs[name]); - } -}; - -Parser.prototype.parse = function parse () { - var node = this.options.program || this.startNode(); - this.nextToken(); - return this.parseTopLevel(node) -}; - -var pp = Parser.prototype; - -// ## Parser utilities - -var literal = /^(?:'((?:\\.|[^'])*?)'|"((?:\\.|[^"])*?)"|;)/; -pp.strictDirective = function(start) { - var this$1 = this; - - for (;;) { - skipWhiteSpace.lastIndex = start; - start += skipWhiteSpace.exec(this$1.input)[0].length; - var match = literal.exec(this$1.input.slice(start)); - if (!match) { return false } - if ((match[1] || match[2]) == "use strict") { return true } - start += match[0].length; - } -}; - -// Predicate that tests whether the next token is of the given -// type, and if yes, consumes it as a side effect. - -pp.eat = function(type) { - if (this.type === type) { - this.next(); - return true - } else { - return false - } -}; - -// Tests whether parsed token is a contextual keyword. - -pp.isContextual = function(name) { - return this.type === types.name && this.value === name -}; - -// Consumes contextual keyword if possible. - -pp.eatContextual = function(name) { - return this.value === name && this.eat(types.name) -}; - -// Asserts that following token is given contextual keyword. - -pp.expectContextual = function(name) { - if (!this.eatContextual(name)) { this.unexpected(); } -}; - -// Test whether a semicolon can be inserted at the current position. - -pp.canInsertSemicolon = function() { - return this.type === types.eof || - this.type === types.braceR || - lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) -}; - -pp.insertSemicolon = function() { - if (this.canInsertSemicolon()) { - if (this.options.onInsertedSemicolon) - { this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); } - return true - } -}; - -// Consume a semicolon, or, failing that, see if we are allowed to -// pretend that there is a semicolon at this position. - -pp.semicolon = function() { - if (!this.eat(types.semi) && !this.insertSemicolon()) { this.unexpected(); } -}; - -pp.afterTrailingComma = function(tokType, notNext) { - if (this.type == tokType) { - if (this.options.onTrailingComma) - { this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); } - if (!notNext) - { this.next(); } - return true - } -}; - -// Expect a token of a given type. If found, consume it, otherwise, -// raise an unexpected token error. - -pp.expect = function(type) { - this.eat(type) || this.unexpected(); -}; - -// Raise an unexpected token error. - -pp.unexpected = function(pos) { - this.raise(pos != null ? pos : this.start, "Unexpected token"); -}; - -function DestructuringErrors() { - this.shorthandAssign = - this.trailingComma = - this.parenthesizedAssign = - this.parenthesizedBind = - -1; -} - -pp.checkPatternErrors = function(refDestructuringErrors, isAssign) { - if (!refDestructuringErrors) { return } - if (refDestructuringErrors.trailingComma > -1) - { this.raiseRecoverable(refDestructuringErrors.trailingComma, "Comma is not permitted after the rest element"); } - var parens = isAssign ? refDestructuringErrors.parenthesizedAssign : refDestructuringErrors.parenthesizedBind; - if (parens > -1) { this.raiseRecoverable(parens, "Parenthesized pattern"); } -}; - -pp.checkExpressionErrors = function(refDestructuringErrors, andThrow) { - var pos = refDestructuringErrors ? refDestructuringErrors.shorthandAssign : -1; - if (!andThrow) { return pos >= 0 } - if (pos > -1) { this.raise(pos, "Shorthand property assignments are valid only in destructuring patterns"); } -}; - -pp.checkYieldAwaitInDefaultParams = function() { - if (this.yieldPos && (!this.awaitPos || this.yieldPos < this.awaitPos)) - { this.raise(this.yieldPos, "Yield expression cannot be a default value"); } - if (this.awaitPos) - { this.raise(this.awaitPos, "Await expression cannot be a default value"); } -}; - -pp.isSimpleAssignTarget = function(expr) { - if (expr.type === "ParenthesizedExpression") - { return this.isSimpleAssignTarget(expr.expression) } - return expr.type === "Identifier" || expr.type === "MemberExpression" -}; - -var pp$1 = Parser.prototype; - -// ### Statement parsing - -// Parse a program. Initializes the parser, reads any number of -// statements, and wraps them in a Program node. Optionally takes a -// `program` argument. If present, the statements will be appended -// to its body instead of creating a new node. - -pp$1.parseTopLevel = function(node) { - var this$1 = this; - - var exports = {}; - if (!node.body) { node.body = []; } - while (this.type !== types.eof) { - var stmt = this$1.parseStatement(true, true, exports); - node.body.push(stmt); - } - this.next(); - if (this.options.ecmaVersion >= 6) { - node.sourceType = this.options.sourceType; - } - return this.finishNode(node, "Program") -}; - -var loopLabel = {kind: "loop"}; -var switchLabel = {kind: "switch"}; - -pp$1.isLet = function() { - if (this.type !== types.name || this.options.ecmaVersion < 6 || this.value != "let") { return false } - skipWhiteSpace.lastIndex = this.pos; - var skip = skipWhiteSpace.exec(this.input); - var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next); - if (nextCh === 91 || nextCh == 123) { return true } // '{' and '[' - if (isIdentifierStart(nextCh, true)) { - var pos = next + 1; - while (isIdentifierChar(this.input.charCodeAt(pos), true)) { ++pos; } - var ident = this.input.slice(next, pos); - if (!this.isKeyword(ident)) { return true } - } - return false -}; - -// check 'async [no LineTerminator here] function' -// - 'async /*foo*/ function' is OK. -// - 'async /*\n*/ function' is invalid. -pp$1.isAsyncFunction = function() { - if (this.type !== types.name || this.options.ecmaVersion < 8 || this.value != "async") - { return false } - - skipWhiteSpace.lastIndex = this.pos; - var skip = skipWhiteSpace.exec(this.input); - var next = this.pos + skip[0].length; - return !lineBreak.test(this.input.slice(this.pos, next)) && - this.input.slice(next, next + 8) === "function" && - (next + 8 == this.input.length || !isIdentifierChar(this.input.charAt(next + 8))) -}; - -// Parse a single statement. -// -// If expecting a statement and finding a slash operator, parse a -// regular expression literal. This is to handle cases like -// `if (foo) /blah/.exec(foo)`, where looking at the previous token -// does not help. - -pp$1.parseStatement = function(declaration, topLevel, exports) { - var starttype = this.type, node = this.startNode(), kind; - - if (this.isLet()) { - starttype = types._var; - kind = "let"; - } - - // Most types of statements are recognized by the keyword they - // start with. Many are trivial to parse, some require a bit of - // complexity. - - switch (starttype) { - case types._break: case types._continue: return this.parseBreakContinueStatement(node, starttype.keyword) - case types._debugger: return this.parseDebuggerStatement(node) - case types._do: return this.parseDoStatement(node) - case types._for: return this.parseForStatement(node) - case types._function: - if (!declaration && this.options.ecmaVersion >= 6) { this.unexpected(); } - return this.parseFunctionStatement(node, false) - case types._class: - if (!declaration) { this.unexpected(); } - return this.parseClass(node, true) - case types._if: return this.parseIfStatement(node) - case types._return: return this.parseReturnStatement(node) - case types._switch: return this.parseSwitchStatement(node) - case types._throw: return this.parseThrowStatement(node) - case types._try: return this.parseTryStatement(node) - case types._const: case types._var: - kind = kind || this.value; - if (!declaration && kind != "var") { this.unexpected(); } - return this.parseVarStatement(node, kind) - case types._while: return this.parseWhileStatement(node) - case types._with: return this.parseWithStatement(node) - case types.braceL: return this.parseBlock() - case types.semi: return this.parseEmptyStatement(node) - case types._export: - case types._import: - if (!this.options.allowImportExportEverywhere) { - if (!topLevel) - { this.raise(this.start, "'import' and 'export' may only appear at the top level"); } - if (!this.inModule) - { this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); } - } - return starttype === types._import ? this.parseImport(node) : this.parseExport(node, exports) - - // If the statement does not start with a statement keyword or a - // brace, it's an ExpressionStatement or LabeledStatement. We - // simply start parsing an expression, and afterwards, if the - // next token is a colon and the expression was a simple - // Identifier node, we switch to interpreting it as a label. - default: - if (this.isAsyncFunction() && declaration) { - this.next(); - return this.parseFunctionStatement(node, true) - } - - var maybeName = this.value, expr = this.parseExpression(); - if (starttype === types.name && expr.type === "Identifier" && this.eat(types.colon)) - { return this.parseLabeledStatement(node, maybeName, expr) } - else { return this.parseExpressionStatement(node, expr) } - } -}; - -pp$1.parseBreakContinueStatement = function(node, keyword) { - var this$1 = this; - - var isBreak = keyword == "break"; - this.next(); - if (this.eat(types.semi) || this.insertSemicolon()) { node.label = null; } - else if (this.type !== types.name) { this.unexpected(); } - else { - node.label = this.parseIdent(); - this.semicolon(); - } - - // Verify that there is an actual destination to break or - // continue to. - var i = 0; - for (; i < this.labels.length; ++i) { - var lab = this$1.labels[i]; - if (node.label == null || lab.name === node.label.name) { - if (lab.kind != null && (isBreak || lab.kind === "loop")) { break } - if (node.label && isBreak) { break } - } - } - if (i === this.labels.length) { this.raise(node.start, "Unsyntactic " + keyword); } - return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement") -}; - -pp$1.parseDebuggerStatement = function(node) { - this.next(); - this.semicolon(); - return this.finishNode(node, "DebuggerStatement") -}; - -pp$1.parseDoStatement = function(node) { - this.next(); - this.labels.push(loopLabel); - node.body = this.parseStatement(false); - this.labels.pop(); - this.expect(types._while); - node.test = this.parseParenExpression(); - if (this.options.ecmaVersion >= 6) - { this.eat(types.semi); } - else - { this.semicolon(); } - return this.finishNode(node, "DoWhileStatement") -}; - -// Disambiguating between a `for` and a `for`/`in` or `for`/`of` -// loop is non-trivial. Basically, we have to parse the init `var` -// statement or expression, disallowing the `in` operator (see -// the second parameter to `parseExpression`), and then check -// whether the next token is `in` or `of`. When there is no init -// part (semicolon immediately after the opening parenthesis), it -// is a regular `for` loop. - -pp$1.parseForStatement = function(node) { - this.next(); - this.labels.push(loopLabel); - this.enterLexicalScope(); - this.expect(types.parenL); - if (this.type === types.semi) { return this.parseFor(node, null) } - var isLet = this.isLet(); - if (this.type === types._var || this.type === types._const || isLet) { - var init$1 = this.startNode(), kind = isLet ? "let" : this.value; - this.next(); - this.parseVar(init$1, true, kind); - this.finishNode(init$1, "VariableDeclaration"); - if ((this.type === types._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init$1.declarations.length === 1 && - !(kind !== "var" && init$1.declarations[0].init)) - { return this.parseForIn(node, init$1) } - return this.parseFor(node, init$1) - } - var refDestructuringErrors = new DestructuringErrors; - var init = this.parseExpression(true, refDestructuringErrors); - if (this.type === types._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) { - this.toAssignable(init); - this.checkLVal(init); - this.checkPatternErrors(refDestructuringErrors, true); - return this.parseForIn(node, init) - } else { - this.checkExpressionErrors(refDestructuringErrors, true); - } - return this.parseFor(node, init) -}; - -pp$1.parseFunctionStatement = function(node, isAsync) { - this.next(); - return this.parseFunction(node, true, false, isAsync) -}; - -pp$1.isFunction = function() { - return this.type === types._function || this.isAsyncFunction() -}; - -pp$1.parseIfStatement = function(node) { - this.next(); - node.test = this.parseParenExpression(); - // allow function declarations in branches, but only in non-strict mode - node.consequent = this.parseStatement(!this.strict && this.isFunction()); - node.alternate = this.eat(types._else) ? this.parseStatement(!this.strict && this.isFunction()) : null; - return this.finishNode(node, "IfStatement") -}; - -pp$1.parseReturnStatement = function(node) { - if (!this.inFunction && !this.options.allowReturnOutsideFunction) - { this.raise(this.start, "'return' outside of function"); } - this.next(); - - // In `return` (and `break`/`continue`), the keywords with - // optional arguments, we eagerly look for a semicolon or the - // possibility to insert one. - - if (this.eat(types.semi) || this.insertSemicolon()) { node.argument = null; } - else { node.argument = this.parseExpression(); this.semicolon(); } - return this.finishNode(node, "ReturnStatement") -}; - -pp$1.parseSwitchStatement = function(node) { - var this$1 = this; - - this.next(); - node.discriminant = this.parseParenExpression(); - node.cases = []; - this.expect(types.braceL); - this.labels.push(switchLabel); - this.enterLexicalScope(); - - // Statements under must be grouped (by label) in SwitchCase - // nodes. `cur` is used to keep the node that we are currently - // adding statements to. - - var cur; - for (var sawDefault = false; this.type != types.braceR;) { - if (this$1.type === types._case || this$1.type === types._default) { - var isCase = this$1.type === types._case; - if (cur) { this$1.finishNode(cur, "SwitchCase"); } - node.cases.push(cur = this$1.startNode()); - cur.consequent = []; - this$1.next(); - if (isCase) { - cur.test = this$1.parseExpression(); - } else { - if (sawDefault) { this$1.raiseRecoverable(this$1.lastTokStart, "Multiple default clauses"); } - sawDefault = true; - cur.test = null; - } - this$1.expect(types.colon); - } else { - if (!cur) { this$1.unexpected(); } - cur.consequent.push(this$1.parseStatement(true)); - } - } - this.exitLexicalScope(); - if (cur) { this.finishNode(cur, "SwitchCase"); } - this.next(); // Closing brace - this.labels.pop(); - return this.finishNode(node, "SwitchStatement") -}; - -pp$1.parseThrowStatement = function(node) { - this.next(); - if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) - { this.raise(this.lastTokEnd, "Illegal newline after throw"); } - node.argument = this.parseExpression(); - this.semicolon(); - return this.finishNode(node, "ThrowStatement") -}; - -// Reused empty array added for node fields that are always empty. - -var empty = []; - -pp$1.parseTryStatement = function(node) { - this.next(); - node.block = this.parseBlock(); - node.handler = null; - if (this.type === types._catch) { - var clause = this.startNode(); - this.next(); - this.expect(types.parenL); - clause.param = this.parseBindingAtom(); - this.enterLexicalScope(); - this.checkLVal(clause.param, "let"); - this.expect(types.parenR); - clause.body = this.parseBlock(false); - this.exitLexicalScope(); - node.handler = this.finishNode(clause, "CatchClause"); - } - node.finalizer = this.eat(types._finally) ? this.parseBlock() : null; - if (!node.handler && !node.finalizer) - { this.raise(node.start, "Missing catch or finally clause"); } - return this.finishNode(node, "TryStatement") -}; - -pp$1.parseVarStatement = function(node, kind) { - this.next(); - this.parseVar(node, false, kind); - this.semicolon(); - return this.finishNode(node, "VariableDeclaration") -}; - -pp$1.parseWhileStatement = function(node) { - this.next(); - node.test = this.parseParenExpression(); - this.labels.push(loopLabel); - node.body = this.parseStatement(false); - this.labels.pop(); - return this.finishNode(node, "WhileStatement") -}; - -pp$1.parseWithStatement = function(node) { - if (this.strict) { this.raise(this.start, "'with' in strict mode"); } - this.next(); - node.object = this.parseParenExpression(); - node.body = this.parseStatement(false); - return this.finishNode(node, "WithStatement") -}; - -pp$1.parseEmptyStatement = function(node) { - this.next(); - return this.finishNode(node, "EmptyStatement") -}; - -pp$1.parseLabeledStatement = function(node, maybeName, expr) { - var this$1 = this; - - for (var i$1 = 0, list = this$1.labels; i$1 < list.length; i$1 += 1) - { - var label = list[i$1]; - - if (label.name === maybeName) - { this$1.raise(expr.start, "Label '" + maybeName + "' is already declared"); - } } - var kind = this.type.isLoop ? "loop" : this.type === types._switch ? "switch" : null; - for (var i = this.labels.length - 1; i >= 0; i--) { - var label$1 = this$1.labels[i]; - if (label$1.statementStart == node.start) { - label$1.statementStart = this$1.start; - label$1.kind = kind; - } else { break } - } - this.labels.push({name: maybeName, kind: kind, statementStart: this.start}); - node.body = this.parseStatement(true); - if (node.body.type == "ClassDeclaration" || - node.body.type == "VariableDeclaration" && node.body.kind != "var" || - node.body.type == "FunctionDeclaration" && (this.strict || node.body.generator)) - { this.raiseRecoverable(node.body.start, "Invalid labeled declaration"); } - this.labels.pop(); - node.label = expr; - return this.finishNode(node, "LabeledStatement") -}; - -pp$1.parseExpressionStatement = function(node, expr) { - node.expression = expr; - this.semicolon(); - return this.finishNode(node, "ExpressionStatement") -}; - -// Parse a semicolon-enclosed block of statements, handling `"use -// strict"` declarations when `allowStrict` is true (used for -// function bodies). - -pp$1.parseBlock = function(createNewLexicalScope) { - var this$1 = this; - if ( createNewLexicalScope === void 0 ) { createNewLexicalScope = true; } - - var node = this.startNode(); - node.body = []; - this.expect(types.braceL); - if (createNewLexicalScope) { - this.enterLexicalScope(); - } - while (!this.eat(types.braceR)) { - var stmt = this$1.parseStatement(true); - node.body.push(stmt); - } - if (createNewLexicalScope) { - this.exitLexicalScope(); - } - return this.finishNode(node, "BlockStatement") -}; - -// Parse a regular `for` loop. The disambiguation code in -// `parseStatement` will already have parsed the init statement or -// expression. - -pp$1.parseFor = function(node, init) { - node.init = init; - this.expect(types.semi); - node.test = this.type === types.semi ? null : this.parseExpression(); - this.expect(types.semi); - node.update = this.type === types.parenR ? null : this.parseExpression(); - this.expect(types.parenR); - this.exitLexicalScope(); - node.body = this.parseStatement(false); - this.labels.pop(); - return this.finishNode(node, "ForStatement") -}; - -// Parse a `for`/`in` and `for`/`of` loop, which are almost -// same from parser's perspective. - -pp$1.parseForIn = function(node, init) { - var type = this.type === types._in ? "ForInStatement" : "ForOfStatement"; - this.next(); - node.left = init; - node.right = this.parseExpression(); - this.expect(types.parenR); - this.exitLexicalScope(); - node.body = this.parseStatement(false); - this.labels.pop(); - return this.finishNode(node, type) -}; - -// Parse a list of variable declarations. - -pp$1.parseVar = function(node, isFor, kind) { - var this$1 = this; - - node.declarations = []; - node.kind = kind; - for (;;) { - var decl = this$1.startNode(); - this$1.parseVarId(decl, kind); - if (this$1.eat(types.eq)) { - decl.init = this$1.parseMaybeAssign(isFor); - } else if (kind === "const" && !(this$1.type === types._in || (this$1.options.ecmaVersion >= 6 && this$1.isContextual("of")))) { - this$1.unexpected(); - } else if (decl.id.type != "Identifier" && !(isFor && (this$1.type === types._in || this$1.isContextual("of")))) { - this$1.raise(this$1.lastTokEnd, "Complex binding patterns require an initialization value"); - } else { - decl.init = null; - } - node.declarations.push(this$1.finishNode(decl, "VariableDeclarator")); - if (!this$1.eat(types.comma)) { break } - } - return node -}; - -pp$1.parseVarId = function(decl, kind) { - decl.id = this.parseBindingAtom(kind); - this.checkLVal(decl.id, kind, false); -}; - -// Parse a function declaration or literal (depending on the -// `isStatement` parameter). - -pp$1.parseFunction = function(node, isStatement, allowExpressionBody, isAsync) { - this.initFunction(node); - if (this.options.ecmaVersion >= 6 && !isAsync) - { node.generator = this.eat(types.star); } - if (this.options.ecmaVersion >= 8) - { node.async = !!isAsync; } - - if (isStatement) { - node.id = isStatement === "nullableID" && this.type != types.name ? null : this.parseIdent(); - if (node.id) { - this.checkLVal(node.id, "var"); - } - } - - var oldInGen = this.inGenerator, oldInAsync = this.inAsync, - oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction; - this.inGenerator = node.generator; - this.inAsync = node.async; - this.yieldPos = 0; - this.awaitPos = 0; - this.inFunction = true; - this.enterFunctionScope(); - - if (!isStatement) - { node.id = this.type == types.name ? this.parseIdent() : null; } - - this.parseFunctionParams(node); - this.parseFunctionBody(node, allowExpressionBody); - - this.inGenerator = oldInGen; - this.inAsync = oldInAsync; - this.yieldPos = oldYieldPos; - this.awaitPos = oldAwaitPos; - this.inFunction = oldInFunc; - return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression") -}; - -pp$1.parseFunctionParams = function(node) { - this.expect(types.parenL); - node.params = this.parseBindingList(types.parenR, false, this.options.ecmaVersion >= 8); - this.checkYieldAwaitInDefaultParams(); -}; - -// Parse a class declaration or literal (depending on the -// `isStatement` parameter). - -pp$1.parseClass = function(node, isStatement) { - var this$1 = this; - - this.next(); - - this.parseClassId(node, isStatement); - this.parseClassSuper(node); - var classBody = this.startNode(); - var hadConstructor = false; - classBody.body = []; - this.expect(types.braceL); - while (!this.eat(types.braceR)) { - if (this$1.eat(types.semi)) { continue } - var method = this$1.startNode(); - var isGenerator = this$1.eat(types.star); - var isAsync = false; - var isMaybeStatic = this$1.type === types.name && this$1.value === "static"; - this$1.parsePropertyName(method); - method.static = isMaybeStatic && this$1.type !== types.parenL; - if (method.static) { - if (isGenerator) { this$1.unexpected(); } - isGenerator = this$1.eat(types.star); - this$1.parsePropertyName(method); - } - if (this$1.options.ecmaVersion >= 8 && !isGenerator && !method.computed && - method.key.type === "Identifier" && method.key.name === "async" && this$1.type !== types.parenL && - !this$1.canInsertSemicolon()) { - isAsync = true; - this$1.parsePropertyName(method); - } - method.kind = "method"; - var isGetSet = false; - if (!method.computed) { - var key = method.key; - if (!isGenerator && !isAsync && key.type === "Identifier" && this$1.type !== types.parenL && (key.name === "get" || key.name === "set")) { - isGetSet = true; - method.kind = key.name; - key = this$1.parsePropertyName(method); - } - if (!method.static && (key.type === "Identifier" && key.name === "constructor" || - key.type === "Literal" && key.value === "constructor")) { - if (hadConstructor) { this$1.raise(key.start, "Duplicate constructor in the same class"); } - if (isGetSet) { this$1.raise(key.start, "Constructor can't have get/set modifier"); } - if (isGenerator) { this$1.raise(key.start, "Constructor can't be a generator"); } - if (isAsync) { this$1.raise(key.start, "Constructor can't be an async method"); } - method.kind = "constructor"; - hadConstructor = true; - } - } - this$1.parseClassMethod(classBody, method, isGenerator, isAsync); - if (isGetSet) { - var paramCount = method.kind === "get" ? 0 : 1; - if (method.value.params.length !== paramCount) { - var start = method.value.start; - if (method.kind === "get") - { this$1.raiseRecoverable(start, "getter should have no params"); } - else - { this$1.raiseRecoverable(start, "setter should have exactly one param"); } - } else { - if (method.kind === "set" && method.value.params[0].type === "RestElement") - { this$1.raiseRecoverable(method.value.params[0].start, "Setter cannot use rest params"); } - } - } - } - node.body = this.finishNode(classBody, "ClassBody"); - return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression") -}; - -pp$1.parseClassMethod = function(classBody, method, isGenerator, isAsync) { - method.value = this.parseMethod(isGenerator, isAsync); - classBody.body.push(this.finishNode(method, "MethodDefinition")); -}; - -pp$1.parseClassId = function(node, isStatement) { - node.id = this.type === types.name ? this.parseIdent() : isStatement === true ? this.unexpected() : null; -}; - -pp$1.parseClassSuper = function(node) { - node.superClass = this.eat(types._extends) ? this.parseExprSubscripts() : null; -}; - -// Parses module export declaration. - -pp$1.parseExport = function(node, exports) { - var this$1 = this; - - this.next(); - // export * from '...' - if (this.eat(types.star)) { - this.expectContextual("from"); - node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected(); - this.semicolon(); - return this.finishNode(node, "ExportAllDeclaration") - } - if (this.eat(types._default)) { // export default ... - this.checkExport(exports, "default", this.lastTokStart); - var isAsync; - if (this.type === types._function || (isAsync = this.isAsyncFunction())) { - var fNode = this.startNode(); - this.next(); - if (isAsync) { this.next(); } - node.declaration = this.parseFunction(fNode, "nullableID", false, isAsync); - } else if (this.type === types._class) { - var cNode = this.startNode(); - node.declaration = this.parseClass(cNode, "nullableID"); - } else { - node.declaration = this.parseMaybeAssign(); - this.semicolon(); - } - return this.finishNode(node, "ExportDefaultDeclaration") - } - // export var|const|let|function|class ... - if (this.shouldParseExportStatement()) { - node.declaration = this.parseStatement(true); - if (node.declaration.type === "VariableDeclaration") - { this.checkVariableExport(exports, node.declaration.declarations); } - else - { this.checkExport(exports, node.declaration.id.name, node.declaration.id.start); } - node.specifiers = []; - node.source = null; - } else { // export { x, y as z } [from '...'] - node.declaration = null; - node.specifiers = this.parseExportSpecifiers(exports); - if (this.eatContextual("from")) { - node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected(); - } else { - // check for keywords used as local names - for (var i = 0, list = node.specifiers; i < list.length; i += 1) { - var spec = list[i]; - - this$1.checkUnreserved(spec.local); - } - - node.source = null; - } - this.semicolon(); - } - return this.finishNode(node, "ExportNamedDeclaration") -}; - -pp$1.checkExport = function(exports, name, pos) { - if (!exports) { return } - if (has(exports, name)) - { this.raiseRecoverable(pos, "Duplicate export '" + name + "'"); } - exports[name] = true; -}; - -pp$1.checkPatternExport = function(exports, pat) { - var this$1 = this; - - var type = pat.type; - if (type == "Identifier") - { this.checkExport(exports, pat.name, pat.start); } - else if (type == "ObjectPattern") - { for (var i = 0, list = pat.properties; i < list.length; i += 1) - { - var prop = list[i]; - - this$1.checkPatternExport(exports, prop.value); - } } - else if (type == "ArrayPattern") - { for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) { - var elt = list$1[i$1]; - - if (elt) { this$1.checkPatternExport(exports, elt); } - } } - else if (type == "AssignmentPattern") - { this.checkPatternExport(exports, pat.left); } - else if (type == "ParenthesizedExpression") - { this.checkPatternExport(exports, pat.expression); } -}; - -pp$1.checkVariableExport = function(exports, decls) { - var this$1 = this; - - if (!exports) { return } - for (var i = 0, list = decls; i < list.length; i += 1) - { - var decl = list[i]; - - this$1.checkPatternExport(exports, decl.id); - } -}; - -pp$1.shouldParseExportStatement = function() { - return this.type.keyword === "var" || - this.type.keyword === "const" || - this.type.keyword === "class" || - this.type.keyword === "function" || - this.isLet() || - this.isAsyncFunction() -}; - -// Parses a comma-separated list of module exports. - -pp$1.parseExportSpecifiers = function(exports) { - var this$1 = this; - - var nodes = [], first = true; - // export { x, y as z } [from '...'] - this.expect(types.braceL); - while (!this.eat(types.braceR)) { - if (!first) { - this$1.expect(types.comma); - if (this$1.afterTrailingComma(types.braceR)) { break } - } else { first = false; } - - var node = this$1.startNode(); - node.local = this$1.parseIdent(true); - node.exported = this$1.eatContextual("as") ? this$1.parseIdent(true) : node.local; - this$1.checkExport(exports, node.exported.name, node.exported.start); - nodes.push(this$1.finishNode(node, "ExportSpecifier")); - } - return nodes -}; - -// Parses import declaration. - -pp$1.parseImport = function(node) { - this.next(); - // import '...' - if (this.type === types.string) { - node.specifiers = empty; - node.source = this.parseExprAtom(); - } else { - node.specifiers = this.parseImportSpecifiers(); - this.expectContextual("from"); - node.source = this.type === types.string ? this.parseExprAtom() : this.unexpected(); - } - this.semicolon(); - return this.finishNode(node, "ImportDeclaration") -}; - -// Parses a comma-separated list of module imports. - -pp$1.parseImportSpecifiers = function() { - var this$1 = this; - - var nodes = [], first = true; - if (this.type === types.name) { - // import defaultObj, { x, y as z } from '...' - var node = this.startNode(); - node.local = this.parseIdent(); - this.checkLVal(node.local, "let"); - nodes.push(this.finishNode(node, "ImportDefaultSpecifier")); - if (!this.eat(types.comma)) { return nodes } - } - if (this.type === types.star) { - var node$1 = this.startNode(); - this.next(); - this.expectContextual("as"); - node$1.local = this.parseIdent(); - this.checkLVal(node$1.local, "let"); - nodes.push(this.finishNode(node$1, "ImportNamespaceSpecifier")); - return nodes - } - this.expect(types.braceL); - while (!this.eat(types.braceR)) { - if (!first) { - this$1.expect(types.comma); - if (this$1.afterTrailingComma(types.braceR)) { break } - } else { first = false; } - - var node$2 = this$1.startNode(); - node$2.imported = this$1.parseIdent(true); - if (this$1.eatContextual("as")) { - node$2.local = this$1.parseIdent(); - } else { - this$1.checkUnreserved(node$2.imported); - node$2.local = node$2.imported; - } - this$1.checkLVal(node$2.local, "let"); - nodes.push(this$1.finishNode(node$2, "ImportSpecifier")); - } - return nodes -}; - -var pp$2 = Parser.prototype; - -// Convert existing expression atom to assignable pattern -// if possible. - -pp$2.toAssignable = function(node, isBinding) { - var this$1 = this; - - if (this.options.ecmaVersion >= 6 && node) { - switch (node.type) { - case "Identifier": - if (this.inAsync && node.name === "await") - { this.raise(node.start, "Can not use 'await' as identifier inside an async function"); } - break - - case "ObjectPattern": - case "ArrayPattern": - break - - case "ObjectExpression": - node.type = "ObjectPattern"; - for (var i = 0, list = node.properties; i < list.length; i += 1) { - var prop = list[i]; - - if (prop.kind !== "init") { this$1.raise(prop.key.start, "Object pattern can't contain getter or setter"); } - this$1.toAssignable(prop.value, isBinding); - } - break - - case "ArrayExpression": - node.type = "ArrayPattern"; - this.toAssignableList(node.elements, isBinding); - break - - case "AssignmentExpression": - if (node.operator === "=") { - node.type = "AssignmentPattern"; - delete node.operator; - this.toAssignable(node.left, isBinding); - // falls through to AssignmentPattern - } else { - this.raise(node.left.end, "Only '=' operator can be used for specifying default value."); - break - } - - case "AssignmentPattern": - break - - case "ParenthesizedExpression": - this.toAssignable(node.expression, isBinding); - break - - case "MemberExpression": - if (!isBinding) { break } - - default: - this.raise(node.start, "Assigning to rvalue"); - } - } - return node -}; - -// Convert list of expression atoms to binding list. - -pp$2.toAssignableList = function(exprList, isBinding) { - var this$1 = this; - - var end = exprList.length; - if (end) { - var last = exprList[end - 1]; - if (last && last.type == "RestElement") { - --end; - } else if (last && last.type == "SpreadElement") { - last.type = "RestElement"; - var arg = last.argument; - this.toAssignable(arg, isBinding); - --end; - } - - if (this.options.ecmaVersion === 6 && isBinding && last && last.type === "RestElement" && last.argument.type !== "Identifier") - { this.unexpected(last.argument.start); } - } - for (var i = 0; i < end; i++) { - var elt = exprList[i]; - if (elt) { this$1.toAssignable(elt, isBinding); } - } - return exprList -}; - -// Parses spread element. - -pp$2.parseSpread = function(refDestructuringErrors) { - var node = this.startNode(); - this.next(); - node.argument = this.parseMaybeAssign(false, refDestructuringErrors); - return this.finishNode(node, "SpreadElement") -}; - -pp$2.parseRestBinding = function() { - var node = this.startNode(); - this.next(); - - // RestElement inside of a function parameter must be an identifier - if (this.options.ecmaVersion === 6 && this.type !== types.name) - { this.unexpected(); } - - node.argument = this.parseBindingAtom(); - - return this.finishNode(node, "RestElement") -}; - -// Parses lvalue (assignable) atom. - -pp$2.parseBindingAtom = function() { - if (this.options.ecmaVersion < 6) { return this.parseIdent() } - switch (this.type) { - case types.name: - return this.parseIdent() - - case types.bracketL: - var node = this.startNode(); - this.next(); - node.elements = this.parseBindingList(types.bracketR, true, true); - return this.finishNode(node, "ArrayPattern") - - case types.braceL: - return this.parseObj(true) - - default: - this.unexpected(); - } -}; - -pp$2.parseBindingList = function(close, allowEmpty, allowTrailingComma) { - var this$1 = this; - - var elts = [], first = true; - while (!this.eat(close)) { - if (first) { first = false; } - else { this$1.expect(types.comma); } - if (allowEmpty && this$1.type === types.comma) { - elts.push(null); - } else if (allowTrailingComma && this$1.afterTrailingComma(close)) { - break - } else if (this$1.type === types.ellipsis) { - var rest = this$1.parseRestBinding(); - this$1.parseBindingListItem(rest); - elts.push(rest); - if (this$1.type === types.comma) { this$1.raise(this$1.start, "Comma is not permitted after the rest element"); } - this$1.expect(close); - break - } else { - var elem = this$1.parseMaybeDefault(this$1.start, this$1.startLoc); - this$1.parseBindingListItem(elem); - elts.push(elem); - } - } - return elts -}; - -pp$2.parseBindingListItem = function(param) { - return param -}; - -// Parses assignment pattern around given atom if possible. - -pp$2.parseMaybeDefault = function(startPos, startLoc, left) { - left = left || this.parseBindingAtom(); - if (this.options.ecmaVersion < 6 || !this.eat(types.eq)) { return left } - var node = this.startNodeAt(startPos, startLoc); - node.left = left; - node.right = this.parseMaybeAssign(); - return this.finishNode(node, "AssignmentPattern") -}; - -// Verify that a node is an lval — something that can be assigned -// to. -// bindingType can be either: -// 'var' indicating that the lval creates a 'var' binding -// 'let' indicating that the lval creates a lexical ('let' or 'const') binding -// 'none' indicating that the binding should be checked for illegal identifiers, but not for duplicate references - -pp$2.checkLVal = function(expr, bindingType, checkClashes) { - var this$1 = this; - - switch (expr.type) { - case "Identifier": - if (this.strict && this.reservedWordsStrictBind.test(expr.name)) - { this.raiseRecoverable(expr.start, (bindingType ? "Binding " : "Assigning to ") + expr.name + " in strict mode"); } - if (checkClashes) { - if (has(checkClashes, expr.name)) - { this.raiseRecoverable(expr.start, "Argument name clash"); } - checkClashes[expr.name] = true; - } - if (bindingType && bindingType !== "none") { - if ( - bindingType === "var" && !this.canDeclareVarName(expr.name) || - bindingType !== "var" && !this.canDeclareLexicalName(expr.name) - ) { - this.raiseRecoverable(expr.start, ("Identifier '" + (expr.name) + "' has already been declared")); - } - if (bindingType === "var") { - this.declareVarName(expr.name); - } else { - this.declareLexicalName(expr.name); - } - } - break - - case "MemberExpression": - if (bindingType) { this.raiseRecoverable(expr.start, (bindingType ? "Binding" : "Assigning to") + " member expression"); } - break - - case "ObjectPattern": - for (var i = 0, list = expr.properties; i < list.length; i += 1) - { - var prop = list[i]; - - this$1.checkLVal(prop.value, bindingType, checkClashes); - } - break - - case "ArrayPattern": - for (var i$1 = 0, list$1 = expr.elements; i$1 < list$1.length; i$1 += 1) { - var elem = list$1[i$1]; - - if (elem) { this$1.checkLVal(elem, bindingType, checkClashes); } - } - break - - case "AssignmentPattern": - this.checkLVal(expr.left, bindingType, checkClashes); - break - - case "RestElement": - this.checkLVal(expr.argument, bindingType, checkClashes); - break - - case "ParenthesizedExpression": - this.checkLVal(expr.expression, bindingType, checkClashes); - break - - default: - this.raise(expr.start, (bindingType ? "Binding" : "Assigning to") + " rvalue"); - } -}; - -// A recursive descent parser operates by defining functions for all -// syntactic elements, and recursively calling those, each function -// advancing the input stream and returning an AST node. Precedence -// of constructs (for example, the fact that `!x[1]` means `!(x[1])` -// instead of `(!x)[1]` is handled by the fact that the parser -// function that parses unary prefix operators is called first, and -// in turn calls the function that parses `[]` subscripts — that -// way, it'll receive the node for `x[1]` already parsed, and wraps -// *that* in the unary operator node. -// -// Acorn uses an [operator precedence parser][opp] to handle binary -// operator precedence, because it is much more compact than using -// the technique outlined above, which uses different, nesting -// functions to specify precedence, for all of the ten binary -// precedence levels that JavaScript defines. -// -// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser - -var pp$3 = Parser.prototype; - -// Check if property name clashes with already added. -// Object/class getters and setters are not allowed to clash — -// either with each other or with an init property — and in -// strict mode, init properties are also not allowed to be repeated. - -pp$3.checkPropClash = function(prop, propHash) { - if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) - { return } - var key = prop.key; - var name; - switch (key.type) { - case "Identifier": name = key.name; break - case "Literal": name = String(key.value); break - default: return - } - var kind = prop.kind; - if (this.options.ecmaVersion >= 6) { - if (name === "__proto__" && kind === "init") { - if (propHash.proto) { this.raiseRecoverable(key.start, "Redefinition of __proto__ property"); } - propHash.proto = true; - } - return - } - name = "$" + name; - var other = propHash[name]; - if (other) { - var redefinition; - if (kind === "init") { - redefinition = this.strict && other.init || other.get || other.set; - } else { - redefinition = other.init || other[kind]; - } - if (redefinition) - { this.raiseRecoverable(key.start, "Redefinition of property"); } - } else { - other = propHash[name] = { - init: false, - get: false, - set: false - }; - } - other[kind] = true; -}; - -// ### Expression parsing - -// These nest, from the most general expression type at the top to -// 'atomic', nondivisible expression types at the bottom. Most of -// the functions will simply let the function(s) below them parse, -// and, *if* the syntactic construct they handle is present, wrap -// the AST node that the inner parser gave them in another node. - -// Parse a full expression. The optional arguments are used to -// forbid the `in` operator (in for loops initalization expressions) -// and provide reference for storing '=' operator inside shorthand -// property assignment in contexts where both object expression -// and object pattern might appear (so it's possible to raise -// delayed syntax error at correct position). - -pp$3.parseExpression = function(noIn, refDestructuringErrors) { - var this$1 = this; - - var startPos = this.start, startLoc = this.startLoc; - var expr = this.parseMaybeAssign(noIn, refDestructuringErrors); - if (this.type === types.comma) { - var node = this.startNodeAt(startPos, startLoc); - node.expressions = [expr]; - while (this.eat(types.comma)) { node.expressions.push(this$1.parseMaybeAssign(noIn, refDestructuringErrors)); } - return this.finishNode(node, "SequenceExpression") - } - return expr -}; - -// Parse an assignment expression. This includes applications of -// operators like `+=`. - -pp$3.parseMaybeAssign = function(noIn, refDestructuringErrors, afterLeftParse) { - if (this.inGenerator && this.isContextual("yield")) { return this.parseYield() } - - var ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1; - if (refDestructuringErrors) { - oldParenAssign = refDestructuringErrors.parenthesizedAssign; - oldTrailingComma = refDestructuringErrors.trailingComma; - refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1; - } else { - refDestructuringErrors = new DestructuringErrors; - ownDestructuringErrors = true; - } - - var startPos = this.start, startLoc = this.startLoc; - if (this.type == types.parenL || this.type == types.name) - { this.potentialArrowAt = this.start; } - var left = this.parseMaybeConditional(noIn, refDestructuringErrors); - if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); } - if (this.type.isAssign) { - this.checkPatternErrors(refDestructuringErrors, true); - if (!ownDestructuringErrors) { DestructuringErrors.call(refDestructuringErrors); } - var node = this.startNodeAt(startPos, startLoc); - node.operator = this.value; - node.left = this.type === types.eq ? this.toAssignable(left) : left; - refDestructuringErrors.shorthandAssign = -1; // reset because shorthand default was used correctly - this.checkLVal(left); - this.next(); - node.right = this.parseMaybeAssign(noIn); - return this.finishNode(node, "AssignmentExpression") - } else { - if (ownDestructuringErrors) { this.checkExpressionErrors(refDestructuringErrors, true); } - } - if (oldParenAssign > -1) { refDestructuringErrors.parenthesizedAssign = oldParenAssign; } - if (oldTrailingComma > -1) { refDestructuringErrors.trailingComma = oldTrailingComma; } - return left -}; - -// Parse a ternary conditional (`?:`) operator. - -pp$3.parseMaybeConditional = function(noIn, refDestructuringErrors) { - var startPos = this.start, startLoc = this.startLoc; - var expr = this.parseExprOps(noIn, refDestructuringErrors); - if (this.checkExpressionErrors(refDestructuringErrors)) { return expr } - if (this.eat(types.question)) { - var node = this.startNodeAt(startPos, startLoc); - node.test = expr; - node.consequent = this.parseMaybeAssign(); - this.expect(types.colon); - node.alternate = this.parseMaybeAssign(noIn); - return this.finishNode(node, "ConditionalExpression") - } - return expr -}; - -// Start the precedence parser. - -pp$3.parseExprOps = function(noIn, refDestructuringErrors) { - var startPos = this.start, startLoc = this.startLoc; - var expr = this.parseMaybeUnary(refDestructuringErrors, false); - if (this.checkExpressionErrors(refDestructuringErrors)) { return expr } - return expr.start == startPos && expr.type === "ArrowFunctionExpression" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, noIn) -}; - -// Parse binary operators with the operator precedence parsing -// algorithm. `left` is the left-hand side of the operator. -// `minPrec` provides context that allows the function to stop and -// defer further parser to one of its callers when it encounters an -// operator that has a lower precedence than the set it is parsing. - -pp$3.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) { - var prec = this.type.binop; - if (prec != null && (!noIn || this.type !== types._in)) { - if (prec > minPrec) { - var logical = this.type === types.logicalOR || this.type === types.logicalAND; - var op = this.value; - this.next(); - var startPos = this.start, startLoc = this.startLoc; - var right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn); - var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical); - return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn) - } - } - return left -}; - -pp$3.buildBinary = function(startPos, startLoc, left, right, op, logical) { - var node = this.startNodeAt(startPos, startLoc); - node.left = left; - node.operator = op; - node.right = right; - return this.finishNode(node, logical ? "LogicalExpression" : "BinaryExpression") -}; - -// Parse unary operators, both prefix and postfix. - -pp$3.parseMaybeUnary = function(refDestructuringErrors, sawUnary) { - var this$1 = this; - - var startPos = this.start, startLoc = this.startLoc, expr; - if (this.inAsync && this.isContextual("await")) { - expr = this.parseAwait(refDestructuringErrors); - sawUnary = true; - } else if (this.type.prefix) { - var node = this.startNode(), update = this.type === types.incDec; - node.operator = this.value; - node.prefix = true; - this.next(); - node.argument = this.parseMaybeUnary(null, true); - this.checkExpressionErrors(refDestructuringErrors, true); - if (update) { this.checkLVal(node.argument); } - else if (this.strict && node.operator === "delete" && - node.argument.type === "Identifier") - { this.raiseRecoverable(node.start, "Deleting local variable in strict mode"); } - else { sawUnary = true; } - expr = this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); - } else { - expr = this.parseExprSubscripts(refDestructuringErrors); - if (this.checkExpressionErrors(refDestructuringErrors)) { return expr } - while (this.type.postfix && !this.canInsertSemicolon()) { - var node$1 = this$1.startNodeAt(startPos, startLoc); - node$1.operator = this$1.value; - node$1.prefix = false; - node$1.argument = expr; - this$1.checkLVal(expr); - this$1.next(); - expr = this$1.finishNode(node$1, "UpdateExpression"); - } - } - - if (!sawUnary && this.eat(types.starstar)) - { return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false), "**", false) } - else - { return expr } -}; - -// Parse call, dot, and `[]`-subscript expressions. - -pp$3.parseExprSubscripts = function(refDestructuringErrors) { - var startPos = this.start, startLoc = this.startLoc; - var expr = this.parseExprAtom(refDestructuringErrors); - var skipArrowSubscripts = expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")"; - if (this.checkExpressionErrors(refDestructuringErrors) || skipArrowSubscripts) { return expr } - var result = this.parseSubscripts(expr, startPos, startLoc); - if (refDestructuringErrors && result.type === "MemberExpression") { - if (refDestructuringErrors.parenthesizedAssign >= result.start) { refDestructuringErrors.parenthesizedAssign = -1; } - if (refDestructuringErrors.parenthesizedBind >= result.start) { refDestructuringErrors.parenthesizedBind = -1; } - } - return result -}; - -pp$3.parseSubscripts = function(base, startPos, startLoc, noCalls) { - var this$1 = this; - - var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" && - this.lastTokEnd == base.end && !this.canInsertSemicolon(); - for (var computed = (void 0);;) { - if ((computed = this$1.eat(types.bracketL)) || this$1.eat(types.dot)) { - var node = this$1.startNodeAt(startPos, startLoc); - node.object = base; - node.property = computed ? this$1.parseExpression() : this$1.parseIdent(true); - node.computed = !!computed; - if (computed) { this$1.expect(types.bracketR); } - base = this$1.finishNode(node, "MemberExpression"); - } else if (!noCalls && this$1.eat(types.parenL)) { - var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this$1.yieldPos, oldAwaitPos = this$1.awaitPos; - this$1.yieldPos = 0; - this$1.awaitPos = 0; - var exprList = this$1.parseExprList(types.parenR, this$1.options.ecmaVersion >= 8, false, refDestructuringErrors); - if (maybeAsyncArrow && !this$1.canInsertSemicolon() && this$1.eat(types.arrow)) { - this$1.checkPatternErrors(refDestructuringErrors, false); - this$1.checkYieldAwaitInDefaultParams(); - this$1.yieldPos = oldYieldPos; - this$1.awaitPos = oldAwaitPos; - return this$1.parseArrowExpression(this$1.startNodeAt(startPos, startLoc), exprList, true) - } - this$1.checkExpressionErrors(refDestructuringErrors, true); - this$1.yieldPos = oldYieldPos || this$1.yieldPos; - this$1.awaitPos = oldAwaitPos || this$1.awaitPos; - var node$1 = this$1.startNodeAt(startPos, startLoc); - node$1.callee = base; - node$1.arguments = exprList; - base = this$1.finishNode(node$1, "CallExpression"); - } else if (this$1.type === types.backQuote) { - var node$2 = this$1.startNodeAt(startPos, startLoc); - node$2.tag = base; - node$2.quasi = this$1.parseTemplate({isTagged: true}); - base = this$1.finishNode(node$2, "TaggedTemplateExpression"); - } else { - return base - } - } -}; - -// Parse an atomic expression — either a single token that is an -// expression, an expression started by a keyword like `function` or -// `new`, or an expression wrapped in punctuation like `()`, `[]`, -// or `{}`. - -pp$3.parseExprAtom = function(refDestructuringErrors) { - var node, canBeArrow = this.potentialArrowAt == this.start; - switch (this.type) { - case types._super: - if (!this.inFunction) - { this.raise(this.start, "'super' outside of function or class"); } - - case types._this: - var type = this.type === types._this ? "ThisExpression" : "Super"; - node = this.startNode(); - this.next(); - return this.finishNode(node, type) - - case types.name: - var startPos = this.start, startLoc = this.startLoc; - var id = this.parseIdent(this.type !== types.name); - if (this.options.ecmaVersion >= 8 && id.name === "async" && !this.canInsertSemicolon() && this.eat(types._function)) - { return this.parseFunction(this.startNodeAt(startPos, startLoc), false, false, true) } - if (canBeArrow && !this.canInsertSemicolon()) { - if (this.eat(types.arrow)) - { return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false) } - if (this.options.ecmaVersion >= 8 && id.name === "async" && this.type === types.name) { - id = this.parseIdent(); - if (this.canInsertSemicolon() || !this.eat(types.arrow)) - { this.unexpected(); } - return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true) - } - } - return id - - case types.regexp: - var value = this.value; - node = this.parseLiteral(value.value); - node.regex = {pattern: value.pattern, flags: value.flags}; - return node - - case types.num: case types.string: - return this.parseLiteral(this.value) - - case types._null: case types._true: case types._false: - node = this.startNode(); - node.value = this.type === types._null ? null : this.type === types._true; - node.raw = this.type.keyword; - this.next(); - return this.finishNode(node, "Literal") - - case types.parenL: - var start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow); - if (refDestructuringErrors) { - if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr)) - { refDestructuringErrors.parenthesizedAssign = start; } - if (refDestructuringErrors.parenthesizedBind < 0) - { refDestructuringErrors.parenthesizedBind = start; } - } - return expr - - case types.bracketL: - node = this.startNode(); - this.next(); - node.elements = this.parseExprList(types.bracketR, true, true, refDestructuringErrors); - return this.finishNode(node, "ArrayExpression") - - case types.braceL: - return this.parseObj(false, refDestructuringErrors) - - case types._function: - node = this.startNode(); - this.next(); - return this.parseFunction(node, false) - - case types._class: - return this.parseClass(this.startNode(), false) - - case types._new: - return this.parseNew() - - case types.backQuote: - return this.parseTemplate() - - default: - this.unexpected(); - } -}; - -pp$3.parseLiteral = function(value) { - var node = this.startNode(); - node.value = value; - node.raw = this.input.slice(this.start, this.end); - this.next(); - return this.finishNode(node, "Literal") -}; - -pp$3.parseParenExpression = function() { - this.expect(types.parenL); - var val = this.parseExpression(); - this.expect(types.parenR); - return val -}; - -pp$3.parseParenAndDistinguishExpression = function(canBeArrow) { - var this$1 = this; - - var startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8; - if (this.options.ecmaVersion >= 6) { - this.next(); - - var innerStartPos = this.start, innerStartLoc = this.startLoc; - var exprList = [], first = true, lastIsComma = false; - var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart, innerParenStart; - this.yieldPos = 0; - this.awaitPos = 0; - while (this.type !== types.parenR) { - first ? first = false : this$1.expect(types.comma); - if (allowTrailingComma && this$1.afterTrailingComma(types.parenR, true)) { - lastIsComma = true; - break - } else if (this$1.type === types.ellipsis) { - spreadStart = this$1.start; - exprList.push(this$1.parseParenItem(this$1.parseRestBinding())); - if (this$1.type === types.comma) { this$1.raise(this$1.start, "Comma is not permitted after the rest element"); } - break - } else { - if (this$1.type === types.parenL && !innerParenStart) { - innerParenStart = this$1.start; - } - exprList.push(this$1.parseMaybeAssign(false, refDestructuringErrors, this$1.parseParenItem)); - } - } - var innerEndPos = this.start, innerEndLoc = this.startLoc; - this.expect(types.parenR); - - if (canBeArrow && !this.canInsertSemicolon() && this.eat(types.arrow)) { - this.checkPatternErrors(refDestructuringErrors, false); - this.checkYieldAwaitInDefaultParams(); - if (innerParenStart) { this.unexpected(innerParenStart); } - this.yieldPos = oldYieldPos; - this.awaitPos = oldAwaitPos; - return this.parseParenArrowList(startPos, startLoc, exprList) - } - - if (!exprList.length || lastIsComma) { this.unexpected(this.lastTokStart); } - if (spreadStart) { this.unexpected(spreadStart); } - this.checkExpressionErrors(refDestructuringErrors, true); - this.yieldPos = oldYieldPos || this.yieldPos; - this.awaitPos = oldAwaitPos || this.awaitPos; - - if (exprList.length > 1) { - val = this.startNodeAt(innerStartPos, innerStartLoc); - val.expressions = exprList; - this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc); - } else { - val = exprList[0]; - } - } else { - val = this.parseParenExpression(); - } - - if (this.options.preserveParens) { - var par = this.startNodeAt(startPos, startLoc); - par.expression = val; - return this.finishNode(par, "ParenthesizedExpression") - } else { - return val - } -}; - -pp$3.parseParenItem = function(item) { - return item -}; - -pp$3.parseParenArrowList = function(startPos, startLoc, exprList) { - return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList) -}; - -// New's precedence is slightly tricky. It must allow its argument to -// be a `[]` or dot subscript expression, but not a call — at least, -// not without wrapping it in parentheses. Thus, it uses the noCalls -// argument to parseSubscripts to prevent it from consuming the -// argument list. - -var empty$1 = []; - -pp$3.parseNew = function() { - var node = this.startNode(); - var meta = this.parseIdent(true); - if (this.options.ecmaVersion >= 6 && this.eat(types.dot)) { - node.meta = meta; - node.property = this.parseIdent(true); - if (node.property.name !== "target") - { this.raiseRecoverable(node.property.start, "The only valid meta property for new is new.target"); } - if (!this.inFunction) - { this.raiseRecoverable(node.start, "new.target can only be used in functions"); } - return this.finishNode(node, "MetaProperty") - } - var startPos = this.start, startLoc = this.startLoc; - node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true); - if (this.eat(types.parenL)) { node.arguments = this.parseExprList(types.parenR, this.options.ecmaVersion >= 8, false); } - else { node.arguments = empty$1; } - return this.finishNode(node, "NewExpression") -}; - -// Parse template expression. - -pp$3.parseTemplateElement = function(ref) { - var isTagged = ref.isTagged; - - var elem = this.startNode(); - if (this.type === types.invalidTemplate) { - if (!isTagged) { - this.raiseRecoverable(this.start, "Bad escape sequence in untagged template literal"); - } - elem.value = { - raw: this.value, - cooked: null - }; - } else { - elem.value = { - raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"), - cooked: this.value - }; - } - this.next(); - elem.tail = this.type === types.backQuote; - return this.finishNode(elem, "TemplateElement") -}; - -pp$3.parseTemplate = function(ref) { - var this$1 = this; - if ( ref === void 0 ) { ref = {}; } - var isTagged = ref.isTagged; if ( isTagged === void 0 ) { isTagged = false; } - - var node = this.startNode(); - this.next(); - node.expressions = []; - var curElt = this.parseTemplateElement({isTagged: isTagged}); - node.quasis = [curElt]; - while (!curElt.tail) { - this$1.expect(types.dollarBraceL); - node.expressions.push(this$1.parseExpression()); - this$1.expect(types.braceR); - node.quasis.push(curElt = this$1.parseTemplateElement({isTagged: isTagged})); - } - this.next(); - return this.finishNode(node, "TemplateLiteral") -}; - -// Parse an object literal or binding pattern. - -pp$3.isAsyncProp = function(prop) { - return !prop.computed && prop.key.type === "Identifier" && prop.key.name === "async" && - (this.type === types.name || this.type === types.num || this.type === types.string || this.type === types.bracketL || this.type.keyword) && - !lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) -}; - -pp$3.parseObj = function(isPattern, refDestructuringErrors) { - var this$1 = this; - - var node = this.startNode(), first = true, propHash = {}; - node.properties = []; - this.next(); - while (!this.eat(types.braceR)) { - if (!first) { - this$1.expect(types.comma); - if (this$1.afterTrailingComma(types.braceR)) { break } - } else { first = false; } - - var prop = this$1.startNode(), isGenerator = (void 0), isAsync = (void 0), startPos = (void 0), startLoc = (void 0); - if (this$1.options.ecmaVersion >= 6) { - prop.method = false; - prop.shorthand = false; - if (isPattern || refDestructuringErrors) { - startPos = this$1.start; - startLoc = this$1.startLoc; - } - if (!isPattern) - { isGenerator = this$1.eat(types.star); } - } - this$1.parsePropertyName(prop); - if (!isPattern && this$1.options.ecmaVersion >= 8 && !isGenerator && this$1.isAsyncProp(prop)) { - isAsync = true; - this$1.parsePropertyName(prop, refDestructuringErrors); - } else { - isAsync = false; - } - this$1.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors); - this$1.checkPropClash(prop, propHash); - node.properties.push(this$1.finishNode(prop, "Property")); - } - return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression") -}; - -pp$3.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors) { - if ((isGenerator || isAsync) && this.type === types.colon) - { this.unexpected(); } - - if (this.eat(types.colon)) { - prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors); - prop.kind = "init"; - } else if (this.options.ecmaVersion >= 6 && this.type === types.parenL) { - if (isPattern) { this.unexpected(); } - prop.kind = "init"; - prop.method = true; - prop.value = this.parseMethod(isGenerator, isAsync); - } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" && - (prop.key.name === "get" || prop.key.name === "set") && - (this.type != types.comma && this.type != types.braceR)) { - if (isGenerator || isAsync || isPattern) { this.unexpected(); } - prop.kind = prop.key.name; - this.parsePropertyName(prop); - prop.value = this.parseMethod(false); - var paramCount = prop.kind === "get" ? 0 : 1; - if (prop.value.params.length !== paramCount) { - var start = prop.value.start; - if (prop.kind === "get") - { this.raiseRecoverable(start, "getter should have no params"); } - else - { this.raiseRecoverable(start, "setter should have exactly one param"); } - } else { - if (prop.kind === "set" && prop.value.params[0].type === "RestElement") - { this.raiseRecoverable(prop.value.params[0].start, "Setter cannot use rest params"); } - } - } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") { - this.checkUnreserved(prop.key); - prop.kind = "init"; - if (isPattern) { - prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); - } else if (this.type === types.eq && refDestructuringErrors) { - if (refDestructuringErrors.shorthandAssign < 0) - { refDestructuringErrors.shorthandAssign = this.start; } - prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key); - } else { - prop.value = prop.key; - } - prop.shorthand = true; - } else { this.unexpected(); } -}; - -pp$3.parsePropertyName = function(prop) { - if (this.options.ecmaVersion >= 6) { - if (this.eat(types.bracketL)) { - prop.computed = true; - prop.key = this.parseMaybeAssign(); - this.expect(types.bracketR); - return prop.key - } else { - prop.computed = false; - } - } - return prop.key = this.type === types.num || this.type === types.string ? this.parseExprAtom() : this.parseIdent(true) -}; - -// Initialize empty function node. - -pp$3.initFunction = function(node) { - node.id = null; - if (this.options.ecmaVersion >= 6) { - node.generator = false; - node.expression = false; - } - if (this.options.ecmaVersion >= 8) - { node.async = false; } -}; - -// Parse object or class method. - -pp$3.parseMethod = function(isGenerator, isAsync) { - var node = this.startNode(), oldInGen = this.inGenerator, oldInAsync = this.inAsync, - oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction; - - this.initFunction(node); - if (this.options.ecmaVersion >= 6) - { node.generator = isGenerator; } - if (this.options.ecmaVersion >= 8) - { node.async = !!isAsync; } - - this.inGenerator = node.generator; - this.inAsync = node.async; - this.yieldPos = 0; - this.awaitPos = 0; - this.inFunction = true; - this.enterFunctionScope(); - - this.expect(types.parenL); - node.params = this.parseBindingList(types.parenR, false, this.options.ecmaVersion >= 8); - this.checkYieldAwaitInDefaultParams(); - this.parseFunctionBody(node, false); - - this.inGenerator = oldInGen; - this.inAsync = oldInAsync; - this.yieldPos = oldYieldPos; - this.awaitPos = oldAwaitPos; - this.inFunction = oldInFunc; - return this.finishNode(node, "FunctionExpression") -}; - -// Parse arrow function expression with given parameters. - -pp$3.parseArrowExpression = function(node, params, isAsync) { - var oldInGen = this.inGenerator, oldInAsync = this.inAsync, - oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldInFunc = this.inFunction; - - this.enterFunctionScope(); - this.initFunction(node); - if (this.options.ecmaVersion >= 8) - { node.async = !!isAsync; } - - this.inGenerator = false; - this.inAsync = node.async; - this.yieldPos = 0; - this.awaitPos = 0; - this.inFunction = true; - - node.params = this.toAssignableList(params, true); - this.parseFunctionBody(node, true); - - this.inGenerator = oldInGen; - this.inAsync = oldInAsync; - this.yieldPos = oldYieldPos; - this.awaitPos = oldAwaitPos; - this.inFunction = oldInFunc; - return this.finishNode(node, "ArrowFunctionExpression") -}; - -// Parse function body and check parameters. - -pp$3.parseFunctionBody = function(node, isArrowFunction) { - var isExpression = isArrowFunction && this.type !== types.braceL; - var oldStrict = this.strict, useStrict = false; - - if (isExpression) { - node.body = this.parseMaybeAssign(); - node.expression = true; - this.checkParams(node, false); - } else { - var nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params); - if (!oldStrict || nonSimple) { - useStrict = this.strictDirective(this.end); - // If this is a strict mode function, verify that argument names - // are not repeated, and it does not try to bind the words `eval` - // or `arguments`. - if (useStrict && nonSimple) - { this.raiseRecoverable(node.start, "Illegal 'use strict' directive in function with non-simple parameter list"); } - } - // Start a new scope with regard to labels and the `inFunction` - // flag (restore them to their old value afterwards). - var oldLabels = this.labels; - this.labels = []; - if (useStrict) { this.strict = true; } - - // Add the params to varDeclaredNames to ensure that an error is thrown - // if a let/const declaration in the function clashes with one of the params. - this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && this.isSimpleParamList(node.params)); - node.body = this.parseBlock(false); - node.expression = false; - this.labels = oldLabels; - } - this.exitFunctionScope(); - - if (this.strict && node.id) { - // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval' - this.checkLVal(node.id, "none"); - } - this.strict = oldStrict; -}; - -pp$3.isSimpleParamList = function(params) { - for (var i = 0, list = params; i < list.length; i += 1) - { - var param = list[i]; - - if (param.type !== "Identifier") { return false - } } - return true -}; - -// Checks function params for various disallowed patterns such as using "eval" -// or "arguments" and duplicate parameters. - -pp$3.checkParams = function(node, allowDuplicates) { - var this$1 = this; - - var nameHash = {}; - for (var i = 0, list = node.params; i < list.length; i += 1) - { - var param = list[i]; - - this$1.checkLVal(param, "var", allowDuplicates ? null : nameHash); - } -}; - -// Parses a comma-separated list of expressions, and returns them as -// an array. `close` is the token type that ends the list, and -// `allowEmpty` can be turned on to allow subsequent commas with -// nothing in between them to be parsed as `null` (which is needed -// for array literals). - -pp$3.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructuringErrors) { - var this$1 = this; - - var elts = [], first = true; - while (!this.eat(close)) { - if (!first) { - this$1.expect(types.comma); - if (allowTrailingComma && this$1.afterTrailingComma(close)) { break } - } else { first = false; } - - var elt = (void 0); - if (allowEmpty && this$1.type === types.comma) - { elt = null; } - else if (this$1.type === types.ellipsis) { - elt = this$1.parseSpread(refDestructuringErrors); - if (refDestructuringErrors && this$1.type === types.comma && refDestructuringErrors.trailingComma < 0) - { refDestructuringErrors.trailingComma = this$1.start; } - } else { - elt = this$1.parseMaybeAssign(false, refDestructuringErrors); - } - elts.push(elt); - } - return elts -}; - -// Parse the next token as an identifier. If `liberal` is true (used -// when parsing properties), it will also convert keywords into -// identifiers. - -pp$3.checkUnreserved = function(ref) { - var start = ref.start; - var end = ref.end; - var name = ref.name; - - if (this.inGenerator && name === "yield") - { this.raiseRecoverable(start, "Can not use 'yield' as identifier inside a generator"); } - if (this.inAsync && name === "await") - { this.raiseRecoverable(start, "Can not use 'await' as identifier inside an async function"); } - if (this.isKeyword(name)) - { this.raise(start, ("Unexpected keyword '" + name + "'")); } - if (this.options.ecmaVersion < 6 && - this.input.slice(start, end).indexOf("\\") != -1) { return } - var re = this.strict ? this.reservedWordsStrict : this.reservedWords; - if (re.test(name)) - { this.raiseRecoverable(start, ("The keyword '" + name + "' is reserved")); } -}; - -pp$3.parseIdent = function(liberal, isBinding) { - var node = this.startNode(); - if (liberal && this.options.allowReserved == "never") { liberal = false; } - if (this.type === types.name) { - node.name = this.value; - } else if (this.type.keyword) { - node.name = this.type.keyword; - } else { - this.unexpected(); - } - this.next(); - this.finishNode(node, "Identifier"); - if (!liberal) { this.checkUnreserved(node); } - return node -}; - -// Parses yield expression inside generator. - -pp$3.parseYield = function() { - if (!this.yieldPos) { this.yieldPos = this.start; } - - var node = this.startNode(); - this.next(); - if (this.type == types.semi || this.canInsertSemicolon() || (this.type != types.star && !this.type.startsExpr)) { - node.delegate = false; - node.argument = null; - } else { - node.delegate = this.eat(types.star); - node.argument = this.parseMaybeAssign(); - } - return this.finishNode(node, "YieldExpression") -}; - -pp$3.parseAwait = function() { - if (!this.awaitPos) { this.awaitPos = this.start; } - - var node = this.startNode(); - this.next(); - node.argument = this.parseMaybeUnary(null, true); - return this.finishNode(node, "AwaitExpression") -}; - -var pp$4 = Parser.prototype; - -// This function is used to raise exceptions on parse errors. It -// takes an offset integer (into the current `input`) to indicate -// the location of the error, attaches the position to the end -// of the error message, and then raises a `SyntaxError` with that -// message. - -pp$4.raise = function(pos, message) { - var loc = getLineInfo(this.input, pos); - message += " (" + loc.line + ":" + loc.column + ")"; - var err = new SyntaxError(message); - err.pos = pos; err.loc = loc; err.raisedAt = this.pos; - throw err -}; - -pp$4.raiseRecoverable = pp$4.raise; - -pp$4.curPosition = function() { - if (this.options.locations) { - return new Position(this.curLine, this.pos - this.lineStart) - } -}; - -var pp$5 = Parser.prototype; - -// Object.assign polyfill -var assign$1 = Object.assign || function(target) { - var sources = [], len = arguments.length - 1; - while ( len-- > 0 ) { sources[ len ] = arguments[ len + 1 ]; } - - for (var i = 0, list = sources; i < list.length; i += 1) { - var source = list[i]; - - for (var key in source) { - if (has(source, key)) { - target[key] = source[key]; - } - } - } - return target -}; - -// The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names. - -pp$5.enterFunctionScope = function() { - // var: a hash of var-declared names in the current lexical scope - // lexical: a hash of lexically-declared names in the current lexical scope - // childVar: a hash of var-declared names in all child lexical scopes of the current lexical scope (within the current function scope) - // parentLexical: a hash of lexically-declared names in all parent lexical scopes of the current lexical scope (within the current function scope) - this.scopeStack.push({var: {}, lexical: {}, childVar: {}, parentLexical: {}}); -}; - -pp$5.exitFunctionScope = function() { - this.scopeStack.pop(); -}; - -pp$5.enterLexicalScope = function() { - var parentScope = this.scopeStack[this.scopeStack.length - 1]; - var childScope = {var: {}, lexical: {}, childVar: {}, parentLexical: {}}; - - this.scopeStack.push(childScope); - assign$1(childScope.parentLexical, parentScope.lexical, parentScope.parentLexical); -}; - -pp$5.exitLexicalScope = function() { - var childScope = this.scopeStack.pop(); - var parentScope = this.scopeStack[this.scopeStack.length - 1]; - - assign$1(parentScope.childVar, childScope.var, childScope.childVar); -}; - -/** - * A name can be declared with `var` if there are no variables with the same name declared with `let`/`const` - * in the current lexical scope or any of the parent lexical scopes in this function. - */ -pp$5.canDeclareVarName = function(name) { - var currentScope = this.scopeStack[this.scopeStack.length - 1]; - - return !has(currentScope.lexical, name) && !has(currentScope.parentLexical, name) -}; - -/** - * A name can be declared with `let`/`const` if there are no variables with the same name declared with `let`/`const` - * in the current scope, and there are no variables with the same name declared with `var` in the current scope or in - * any child lexical scopes in this function. - */ -pp$5.canDeclareLexicalName = function(name) { - var currentScope = this.scopeStack[this.scopeStack.length - 1]; - - return !has(currentScope.lexical, name) && !has(currentScope.var, name) && !has(currentScope.childVar, name) -}; - -pp$5.declareVarName = function(name) { - this.scopeStack[this.scopeStack.length - 1].var[name] = true; -}; - -pp$5.declareLexicalName = function(name) { - this.scopeStack[this.scopeStack.length - 1].lexical[name] = true; -}; - -var Node = function Node(parser, pos, loc) { - this.type = ""; - this.start = pos; - this.end = 0; - if (parser.options.locations) - { this.loc = new SourceLocation(parser, loc); } - if (parser.options.directSourceFile) - { this.sourceFile = parser.options.directSourceFile; } - if (parser.options.ranges) - { this.range = [pos, 0]; } -}; - -// Start an AST node, attaching a start offset. - -var pp$6 = Parser.prototype; - -pp$6.startNode = function() { - return new Node(this, this.start, this.startLoc) -}; - -pp$6.startNodeAt = function(pos, loc) { - return new Node(this, pos, loc) -}; - -// Finish an AST node, adding `type` and `end` properties. - -function finishNodeAt(node, type, pos, loc) { - node.type = type; - node.end = pos; - if (this.options.locations) - { node.loc.end = loc; } - if (this.options.ranges) - { node.range[1] = pos; } - return node -} - -pp$6.finishNode = function(node, type) { - return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc) -}; - -// Finish node at given position - -pp$6.finishNodeAt = function(node, type, pos, loc) { - return finishNodeAt.call(this, node, type, pos, loc) -}; - -// The algorithm used to determine whether a regexp can appear at a -// given point in the program is loosely based on sweet.js' approach. -// See https://github.com/mozilla/sweet.js/wiki/design - -var TokContext = function TokContext(token, isExpr, preserveSpace, override, generator) { - this.token = token; - this.isExpr = !!isExpr; - this.preserveSpace = !!preserveSpace; - this.override = override; - this.generator = !!generator; -}; - -var types$1 = { - b_stat: new TokContext("{", false), - b_expr: new TokContext("{", true), - b_tmpl: new TokContext("${", false), - p_stat: new TokContext("(", false), - p_expr: new TokContext("(", true), - q_tmpl: new TokContext("`", true, true, function (p) { return p.tryReadTemplateToken(); }), - f_stat: new TokContext("function", false), - f_expr: new TokContext("function", true), - f_expr_gen: new TokContext("function", true, false, null, true), - f_gen: new TokContext("function", false, false, null, true) -}; - -var pp$7 = Parser.prototype; - -pp$7.initialContext = function() { - return [types$1.b_stat] -}; - -pp$7.braceIsBlock = function(prevType) { - var parent = this.curContext(); - if (parent === types$1.f_expr || parent === types$1.f_stat) - { return true } - if (prevType === types.colon && (parent === types$1.b_stat || parent === types$1.b_expr)) - { return !parent.isExpr } - - // The check for `tt.name && exprAllowed` detects whether we are - // after a `yield` or `of` construct. See the `updateContext` for - // `tt.name`. - if (prevType === types._return || prevType == types.name && this.exprAllowed) - { return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) } - if (prevType === types._else || prevType === types.semi || prevType === types.eof || prevType === types.parenR || prevType == types.arrow) - { return true } - if (prevType == types.braceL) - { return parent === types$1.b_stat } - if (prevType == types._var || prevType == types.name) - { return false } - return !this.exprAllowed -}; - -pp$7.inGeneratorContext = function() { - var this$1 = this; - - for (var i = this.context.length - 1; i >= 1; i--) { - var context = this$1.context[i]; - if (context.token === "function") - { return context.generator } - } - return false -}; - -pp$7.updateContext = function(prevType) { - var update, type = this.type; - if (type.keyword && prevType == types.dot) - { this.exprAllowed = false; } - else if (update = type.updateContext) - { update.call(this, prevType); } - else - { this.exprAllowed = type.beforeExpr; } -}; - -// Token-specific context update code - -types.parenR.updateContext = types.braceR.updateContext = function() { - if (this.context.length == 1) { - this.exprAllowed = true; - return - } - var out = this.context.pop(); - if (out === types$1.b_stat && this.curContext().token === "function") { - out = this.context.pop(); - } - this.exprAllowed = !out.isExpr; -}; - -types.braceL.updateContext = function(prevType) { - this.context.push(this.braceIsBlock(prevType) ? types$1.b_stat : types$1.b_expr); - this.exprAllowed = true; -}; - -types.dollarBraceL.updateContext = function() { - this.context.push(types$1.b_tmpl); - this.exprAllowed = true; -}; - -types.parenL.updateContext = function(prevType) { - var statementParens = prevType === types._if || prevType === types._for || prevType === types._with || prevType === types._while; - this.context.push(statementParens ? types$1.p_stat : types$1.p_expr); - this.exprAllowed = true; -}; - -types.incDec.updateContext = function() { - // tokExprAllowed stays unchanged -}; - -types._function.updateContext = types._class.updateContext = function(prevType) { - if (prevType.beforeExpr && prevType !== types.semi && prevType !== types._else && - !((prevType === types.colon || prevType === types.braceL) && this.curContext() === types$1.b_stat)) - { this.context.push(types$1.f_expr); } - else - { this.context.push(types$1.f_stat); } - this.exprAllowed = false; -}; - -types.backQuote.updateContext = function() { - if (this.curContext() === types$1.q_tmpl) - { this.context.pop(); } - else - { this.context.push(types$1.q_tmpl); } - this.exprAllowed = false; -}; - -types.star.updateContext = function(prevType) { - if (prevType == types._function) { - var index = this.context.length - 1; - if (this.context[index] === types$1.f_expr) - { this.context[index] = types$1.f_expr_gen; } - else - { this.context[index] = types$1.f_gen; } - } - this.exprAllowed = true; -}; - -types.name.updateContext = function(prevType) { - var allowed = false; - if (this.options.ecmaVersion >= 6) { - if (this.value == "of" && !this.exprAllowed || - this.value == "yield" && this.inGeneratorContext()) - { allowed = true; } - } - this.exprAllowed = allowed; -}; - -// Object type used to represent tokens. Note that normally, tokens -// simply exist as properties on the parser object. This is only -// used for the onToken callback and the external tokenizer. - -var Token = function Token(p) { - this.type = p.type; - this.value = p.value; - this.start = p.start; - this.end = p.end; - if (p.options.locations) - { this.loc = new SourceLocation(p, p.startLoc, p.endLoc); } - if (p.options.ranges) - { this.range = [p.start, p.end]; } -}; - -// ## Tokenizer - -var pp$8 = Parser.prototype; - -// Are we running under Rhino? -var isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]"; - -// Move to the next token - -pp$8.next = function() { - if (this.options.onToken) - { this.options.onToken(new Token(this)); } - - this.lastTokEnd = this.end; - this.lastTokStart = this.start; - this.lastTokEndLoc = this.endLoc; - this.lastTokStartLoc = this.startLoc; - this.nextToken(); -}; - -pp$8.getToken = function() { - this.next(); - return new Token(this) -}; - -// If we're in an ES6 environment, make parsers iterable -if (typeof Symbol !== "undefined") - { pp$8[Symbol.iterator] = function() { - var this$1 = this; - - return { - next: function () { - var token = this$1.getToken(); - return { - done: token.type === types.eof, - value: token - } - } - } - }; } - -// Toggle strict mode. Re-reads the next number or string to please -// pedantic tests (`"use strict"; 010;` should fail). - -pp$8.curContext = function() { - return this.context[this.context.length - 1] -}; - -// Read a single token, updating the parser object's token-related -// properties. - -pp$8.nextToken = function() { - var curContext = this.curContext(); - if (!curContext || !curContext.preserveSpace) { this.skipSpace(); } - - this.start = this.pos; - if (this.options.locations) { this.startLoc = this.curPosition(); } - if (this.pos >= this.input.length) { return this.finishToken(types.eof) } - - if (curContext.override) { return curContext.override(this) } - else { this.readToken(this.fullCharCodeAtPos()); } -}; - -pp$8.readToken = function(code) { - // Identifier or keyword. '\uXXXX' sequences are allowed in - // identifiers, so '\' also dispatches to that. - if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */) - { return this.readWord() } - - return this.getTokenFromCode(code) -}; - -pp$8.fullCharCodeAtPos = function() { - var code = this.input.charCodeAt(this.pos); - if (code <= 0xd7ff || code >= 0xe000) { return code } - var next = this.input.charCodeAt(this.pos + 1); - return (code << 10) + next - 0x35fdc00 -}; - -pp$8.skipBlockComment = function() { - var this$1 = this; - - var startLoc = this.options.onComment && this.curPosition(); - var start = this.pos, end = this.input.indexOf("*/", this.pos += 2); - if (end === -1) { this.raise(this.pos - 2, "Unterminated comment"); } - this.pos = end + 2; - if (this.options.locations) { - lineBreakG.lastIndex = start; - var match; - while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) { - ++this$1.curLine; - this$1.lineStart = match.index + match[0].length; - } - } - if (this.options.onComment) - { this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos, - startLoc, this.curPosition()); } -}; - -pp$8.skipLineComment = function(startSkip) { - var this$1 = this; - - var start = this.pos; - var startLoc = this.options.onComment && this.curPosition(); - var ch = this.input.charCodeAt(this.pos += startSkip); - while (this.pos < this.input.length && !isNewLine(ch)) { - ch = this$1.input.charCodeAt(++this$1.pos); - } - if (this.options.onComment) - { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, - startLoc, this.curPosition()); } -}; - -// Called at the start of the parse and after every token. Skips -// whitespace and comments, and. - -pp$8.skipSpace = function() { - var this$1 = this; - - loop: while (this.pos < this.input.length) { - var ch = this$1.input.charCodeAt(this$1.pos); - switch (ch) { - case 32: case 160: // ' ' - ++this$1.pos; - break - case 13: - if (this$1.input.charCodeAt(this$1.pos + 1) === 10) { - ++this$1.pos; - } - case 10: case 8232: case 8233: - ++this$1.pos; - if (this$1.options.locations) { - ++this$1.curLine; - this$1.lineStart = this$1.pos; - } - break - case 47: // '/' - switch (this$1.input.charCodeAt(this$1.pos + 1)) { - case 42: // '*' - this$1.skipBlockComment(); - break - case 47: - this$1.skipLineComment(2); - break - default: - break loop - } - break - default: - if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { - ++this$1.pos; - } else { - break loop - } - } - } -}; - -// Called at the end of every token. Sets `end`, `val`, and -// maintains `context` and `exprAllowed`, and skips the space after -// the token, so that the next one's `start` will point at the -// right position. - -pp$8.finishToken = function(type, val) { - this.end = this.pos; - if (this.options.locations) { this.endLoc = this.curPosition(); } - var prevType = this.type; - this.type = type; - this.value = val; - - this.updateContext(prevType); -}; - -// ### Token reading - -// This is the function that is called to fetch the next token. It -// is somewhat obscure, because it works in character codes rather -// than characters, and because operator parsing has been inlined -// into it. -// -// All in the name of speed. -// -pp$8.readToken_dot = function() { - var next = this.input.charCodeAt(this.pos + 1); - if (next >= 48 && next <= 57) { return this.readNumber(true) } - var next2 = this.input.charCodeAt(this.pos + 2); - if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.' - this.pos += 3; - return this.finishToken(types.ellipsis) - } else { - ++this.pos; - return this.finishToken(types.dot) - } -}; - -pp$8.readToken_slash = function() { // '/' - var next = this.input.charCodeAt(this.pos + 1); - if (this.exprAllowed) { ++this.pos; return this.readRegexp() } - if (next === 61) { return this.finishOp(types.assign, 2) } - return this.finishOp(types.slash, 1) -}; - -pp$8.readToken_mult_modulo_exp = function(code) { // '%*' - var next = this.input.charCodeAt(this.pos + 1); - var size = 1; - var tokentype = code === 42 ? types.star : types.modulo; - - // exponentiation operator ** and **= - if (this.options.ecmaVersion >= 7 && next === 42) { - ++size; - tokentype = types.starstar; - next = this.input.charCodeAt(this.pos + 2); - } - - if (next === 61) { return this.finishOp(types.assign, size + 1) } - return this.finishOp(tokentype, size) -}; - -pp$8.readToken_pipe_amp = function(code) { // '|&' - var next = this.input.charCodeAt(this.pos + 1); - if (next === code) { return this.finishOp(code === 124 ? types.logicalOR : types.logicalAND, 2) } - if (next === 61) { return this.finishOp(types.assign, 2) } - return this.finishOp(code === 124 ? types.bitwiseOR : types.bitwiseAND, 1) -}; - -pp$8.readToken_caret = function() { // '^' - var next = this.input.charCodeAt(this.pos + 1); - if (next === 61) { return this.finishOp(types.assign, 2) } - return this.finishOp(types.bitwiseXOR, 1) -}; - -pp$8.readToken_plus_min = function(code) { // '+-' - var next = this.input.charCodeAt(this.pos + 1); - if (next === code) { - if (next == 45 && !this.inModule && this.input.charCodeAt(this.pos + 2) == 62 && - (this.lastTokEnd === 0 || lineBreak.test(this.input.slice(this.lastTokEnd, this.pos)))) { - // A `-->` line comment - this.skipLineComment(3); - this.skipSpace(); - return this.nextToken() - } - return this.finishOp(types.incDec, 2) - } - if (next === 61) { return this.finishOp(types.assign, 2) } - return this.finishOp(types.plusMin, 1) -}; - -pp$8.readToken_lt_gt = function(code) { // '<>' - var next = this.input.charCodeAt(this.pos + 1); - var size = 1; - if (next === code) { - size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2; - if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types.assign, size + 1) } - return this.finishOp(types.bitShift, size) - } - if (next == 33 && code == 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) == 45 && - this.input.charCodeAt(this.pos + 3) == 45) { - // ` - From 1d7ff7b517cdaf397ef966a7a2ac548906a34126 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 23 Dec 2021 15:29:16 +0800 Subject: [PATCH 120/148] fix index.ts --- build/processLib.js | 6 +----- index.ts | 9 +-------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/build/processLib.js b/build/processLib.js index 65acca6c6..9a88b6f5b 100644 --- a/build/processLib.js +++ b/build/processLib.js @@ -60,8 +60,4 @@ transform().then(() => { }); }).then(() => { console.log(chalk.green('Libs can be bundled!')); -}) - - - -// Do bundling test to check if transform is correct. \ No newline at end of file +}); \ No newline at end of file diff --git a/index.ts b/index.ts index 167141c58..70a5e0a35 100644 --- a/index.ts +++ b/index.ts @@ -1,8 +1 @@ -export * from './src/zrender'; -export * from './src/export'; - -import {registerPainter} from './src/zrender'; -import CanvasPainter from './src/canvas/Painter'; -import SVGPainter from './src/svg/Painter'; -registerPainter('canvas', CanvasPainter); -registerPainter('svg', SVGPainter); +export * from './lib/all'; \ No newline at end of file From e6483e772f2e2c3346ea78af7afce42026a50bb5 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 23 Dec 2021 16:55:44 +0800 Subject: [PATCH 121/148] chore: fix globby in windows --- build/processLib.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/build/processLib.js b/build/processLib.js index 9a88b6f5b..05373ddb5 100644 --- a/build/processLib.js +++ b/build/processLib.js @@ -27,7 +27,16 @@ function addJsExtension(moduleName) { } async function transform() { - const libFiles = await globby(path.join(__dirname, '../lib/**/*.js')); + const libFiles = await globby([ + '**/*.js' + ], { + cwd: path.join(__dirname, '../lib'), + absolute: true + }); + + if (libFiles.length === 0) { + throw new Error('No lib files found.') + } for (let file of libFiles) { const code = fs.readFileSync(file, 'utf-8'); From 301879e711ea593e04b636926ce880f2f75d3533 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 24 Dec 2021 11:15:30 +0800 Subject: [PATCH 122/148] fix import parsing case Case: import { AAAA, // BBB } from 'module-10'; --- build/processLib.js | 4 ---- build/transformImport.js | 16 +++++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/build/processLib.js b/build/processLib.js index 05373ddb5..972b33fce 100644 --- a/build/processLib.js +++ b/build/processLib.js @@ -54,10 +54,6 @@ async function transform() { ) } -async function testLibBundling() { - -} - transform().then(() => { console.log(chalk.green('Added .js extensions.')); console.log(chalk.gray('Start testing generated libs...')); diff --git a/build/transformImport.js b/build/transformImport.js index 722829d1a..8113ab285 100644 --- a/build/transformImport.js +++ b/build/transformImport.js @@ -3,7 +3,7 @@ // Reference: // https://regexr.com/47jlq // https://gist.github.com/manekinekko/7e58a17bc62a9be47172 -const regexp = /((?:(?:import)|(?:export))\s+?(?:(?:(?:[\w*\s{},]*)\s+from\s+?)|))(?:(?:"(.*?)")|(?:'(.*?)'))([\s]*?(?:;|$|))/g; +const regexp = /((?:(?:import)|(?:export))\s+?(?:(?:(?:[\w*\s{},\/\/\*]*)\s+from\s+?)|))(?:(?:"(.*?)")|(?:'(.*?)'))([\s]*?(?:;|$|))/g; module.exports.transformImport = function (code, processModuleName) { return code.replace(regexp, (str, prefix, moduleNameA, moduleNameB, postfix) => { @@ -52,6 +52,11 @@ import "module-8"; import "module-9" // comment no problem +import { + AAAA, + // BBB +} from 'module-10'; + import "module-b' // doesn't match -> the opening and closing quation mark are different importing hya from 'ttt' @@ -62,9 +67,9 @@ import fbsfrom '' // Export expressions. export { aaa }; -export * from "module-10"; +export * from "module-11"; -export { aaa } from "module-11"; +export { aaa } from "module-12"; // Should not be parsed export default aaa; @@ -90,7 +95,8 @@ module.exports.runTest = function () { 'module-8', 'module-9', 'module-10', - 'module-11' + 'module-11', + 'module-12' ] let cursor = 0; module.exports.transformImport(testCases, (moduleName) => { @@ -106,4 +112,4 @@ module.exports.runTest = function () { console.log('All test passed!') } -// module.exports.runTest(); \ No newline at end of file +module.exports.runTest(); \ No newline at end of file From 4460f547ec9e1c04e1419e41adb2a83c33e07956 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 24 Dec 2021 11:27:15 +0800 Subject: [PATCH 123/148] fix broken code --- build/processLib.js | 1 - build/transformImport.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build/processLib.js b/build/processLib.js index 972b33fce..17d37d9be 100644 --- a/build/processLib.js +++ b/build/processLib.js @@ -57,7 +57,6 @@ async function transform() { transform().then(() => { console.log(chalk.green('Added .js extensions.')); console.log(chalk.gray('Start testing generated libs...')); - return testLibBundling(); }).then(() => { return rollup.rollup({ input: path.resolve(__dirname, '../index.js'), diff --git a/build/transformImport.js b/build/transformImport.js index 8113ab285..32d5e590d 100644 --- a/build/transformImport.js +++ b/build/transformImport.js @@ -112,4 +112,4 @@ module.exports.runTest = function () { console.log('All test passed!') } -module.exports.runTest(); \ No newline at end of file +// module.exports.runTest(); \ No newline at end of file From ae7a62529aa42fc86f7898672917063accadfc9f Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 24 Dec 2021 11:29:59 +0800 Subject: [PATCH 124/148] fix issue from deepscan --- build/transformImport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/transformImport.js b/build/transformImport.js index 32d5e590d..58d6812dc 100644 --- a/build/transformImport.js +++ b/build/transformImport.js @@ -3,7 +3,7 @@ // Reference: // https://regexr.com/47jlq // https://gist.github.com/manekinekko/7e58a17bc62a9be47172 -const regexp = /((?:(?:import)|(?:export))\s+?(?:(?:(?:[\w*\s{},\/\/\*]*)\s+from\s+?)|))(?:(?:"(.*?)")|(?:'(.*?)'))([\s]*?(?:;|$|))/g; +const regexp = /((?:(?:import)|(?:export))\s+?(?:(?:(?:[\w*\s{},\/]*)\s+from\s+?)|))(?:(?:"(.*?)")|(?:'(.*?)'))([\s]*?(?:;|$|))/g; module.exports.transformImport = function (code, processModuleName) { return code.replace(regexp, (str, prefix, moduleNameA, moduleNameB, postfix) => { From e0d636e39cdf9ef17e410613ef4510ab6cddedfb Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 24 Dec 2021 14:19:26 +0800 Subject: [PATCH 125/148] fix package-lock.json --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 58af8f9b8..2aef283d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1189,9 +1189,9 @@ } }, "@rollup/plugin-node-resolve": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.1.tgz", - "integrity": "sha512-6QKtRevXLrmEig9UiMYt2fSvee9TyltGRfw+qSs6xjUnxwjOzTOqy+/Lpxsgjb8mJn1EQNbCDAvt89O4uzL5kw==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", From 94db9e171094ccaa00e7511e21fee8c77b5e99d2 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 28 Dec 2021 20:13:23 +0800 Subject: [PATCH 126/148] feat(sector): the sector supports arbitrary corner radius. --- src/graphic/helper/roundSector.ts | 149 ++++++++++++++++++------------ src/graphic/shape/Sector.ts | 16 +++- test/sector.html | 20 +++- 3 files changed, 122 insertions(+), 63 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index f11b6b8b9..c5b7ab5cf 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -1,4 +1,6 @@ +import { parsePercent } from '../../contain/text'; import PathProxy, { normalizeArcAngles } from '../../core/PathProxy'; +import { isArray, map } from '../../core/util'; const PI = Math.PI; const PI2 = PI * 2; @@ -12,31 +14,22 @@ const mathMax = Math.max; const mathMin = Math.min; const e = 1e-4; -type CornerTangents = { - cx: number - cy: number - x0: number - y0: number - x1: number - y1: number -}; - function intersect( x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number ): [number, number] { - const x10 = x1 - x0; - const y10 = y1 - y0; - const x32 = x3 - x2; - const y32 = y3 - y2; - let t = y32 * x10 - x32 * y10; + const dx10 = x1 - x0; + const dy10 = y1 - y0; + const dx32 = x3 - x2; + const dy32 = y3 - y2; + let t = dy32 * dx10 - dx32 * dy10; if (t * t < e) { return; } - t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t; - return [x0 + t * x10, y0 + t * y10]; + t = (dx32 * (y0 - y2) - dy32 * (x0 - x2)) / t; + return [x0 + t * dx10, y0 + t * dy10]; } // Compute perpendicular offset line of length rc. @@ -45,7 +38,7 @@ function computeCornerTangents( x1: number, y1: number, radius: number, cr: number, clockwise: boolean -): CornerTangents { +) { const x01 = x0 - x1; const y01 = y0 - y1; const lo = (clockwise ? cr : -cr) / mathSqrt(x01 * x01 + y01 * y01); @@ -89,6 +82,46 @@ function computeCornerTangents( }; } +function calcCornerCircleCenter(x: number, y: number, r: number, angle: number) { + return { + x: x + r * mathCos(angle), + y: y + r * mathSin(angle) + }; +} + +// For compactibility, don't use normalizeCssArray +// 5 represents [5, 5, 5, 5] +// [5] represents [5, 5, 0, 0] +// [5, 10] represents [5, 5, 10, 10] +// [5, 10, 15] represents [5, 10, 15, 15] +// [5, 10, 15, 20] represents [5, 10, 15, 20] +function normalizeCornerRadius( + cr: number | string | (number | string)[], + r0: number, + r: number +) { + let arr: (number | string)[]; + if (isArray(cr)) { + const len = cr.length; + if (len === 4) { + arr = cr; + } + else if (len === 3) { + arr = cr.concat(cr[len - 1]); + } + else if (len === 2) { + arr = [cr[0], cr[0], cr[1], cr[1]]; + } + else { + arr = [cr[0], cr[0], 0, 0]; + } + } + else { + arr = [cr, cr, cr, cr]; + } + return map(arr, (cr, idx) => parsePercent(cr, idx < 2 ? r0 : r)); +} + export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { cx: number cy: number @@ -97,11 +130,11 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { clockwise?: boolean, r?: number, r0?: number, - cornerRadius?: number, - innerCornerRadius?: number + cr?: number | string | (number | string)[] }) { - let radius = mathMax(shape.r, 0); - let innerRadius = mathMax(shape.r0 || 0, 0); + const { r, r0 } = shape; + let radius = mathMax(r, 0); + let innerRadius = mathMax(r0 || 0, 0); const hasRadius = radius > 0; const hasInnerRadius = innerRadius > 0; @@ -123,8 +156,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { } const clockwise = !!shape.clockwise; - const startAngle = shape.startAngle; - const endAngle = shape.endAngle; + const { startAngle, endAngle, cx, cy, cr } = shape; // PENDING: whether normalizing angles is required? let arc: number; @@ -138,10 +170,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { arc = mathAbs(tmpAngles[0] - tmpAngles[1]); } - const cx = shape.cx; - const cy = shape.cy; - const cornerRadius = shape.cornerRadius || 0; - const innerCornerRadius = shape.innerCornerRadius || 0; + const [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cr, r0, r); // is a point if (!(radius > e)) { @@ -149,27 +178,28 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { } // is a circle or annulus else if (arc > PI2 - e) { - ctx.moveTo( - cx + radius * mathCos(startAngle), - cy + radius * mathSin(startAngle) - ); + const { x, y } = calcCornerCircleCenter(cx, cy, radius, startAngle); + ctx.moveTo(x, y); ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); if (innerRadius > e) { - ctx.moveTo( - cx + innerRadius * mathCos(endAngle), - cy + innerRadius * mathSin(endAngle) - ); + const { x, y } = calcCornerCircleCenter(cx, cy, innerRadius, endAngle); + ctx.moveTo(x, y); ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); } } // is a circular or annular sector else { const halfRd = mathAbs(radius - innerRadius) / 2; - const cr = mathMin(halfRd, cornerRadius); - const icr = mathMin(halfRd, innerCornerRadius); - let cr0 = icr; - let cr1 = cr; + let ocrs = mathMin(halfRd, ocrStart); + let ocre = mathMin(halfRd, ocrEnd); + let icrs = mathMin(halfRd, icrStart); + let icre = mathMin(halfRd, icrEnd); + + let ocrMax = mathMax(ocrs, ocre); + let icrMax = mathMax(icrs, icre); + let limitedOcrMax = ocrMax; + let limitedIcrMax = icrMax; const xrs = radius * mathCos(startAngle); const yrs = radius * mathSin(startAngle); @@ -182,7 +212,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { let yirs; // draw corner radius - if (cr > e || icr > e) { + if (ocrMax > e || icrMax > e) { xre = radius * mathCos(endAngle); yre = radius * mathSin(endAngle); xirs = innerRadius * mathCos(startAngle); @@ -200,8 +230,8 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { mathACos((x0 * x1 + y0 * y1) / (mathSqrt(x0 * x0 + y0 * y0) * mathSqrt(x1 * x1 + y1 * y1))) / 2 ); const b = mathSqrt(it[0] * it[0] + it[1] * it[1]); - cr0 = mathMin(icr, (innerRadius - b) / (a - 1)); - cr1 = mathMin(cr, (radius - b) / (a + 1)); + limitedOcrMax = mathMin(ocrMax, (radius - b) / (a + 1)); + limitedIcrMax = mathMin(icrMax, (innerRadius - b) / (a - 1)); } } } @@ -211,25 +241,27 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { ctx.moveTo(cx + xrs, cy + yrs); } // the outer ring has corners - else if (cr1 > e) { - const ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, cr1, clockwise); - const ct1 = computeCornerTangents(xre, yre, xire, yire, radius, cr1, clockwise); + else if (limitedOcrMax > e) { + const crStart = mathMin(ocrStart, limitedOcrMax); + const crEnd = mathMin(ocrEnd, limitedOcrMax); + const ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, crStart, clockwise); + const ct1 = computeCornerTangents(xre, yre, xire, yire, radius, crEnd, clockwise); ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); // Have the corners merged? - if (cr1 < cr) { + if (limitedOcrMax < ocrMax) { // eslint-disable-next-line max-len - ctx.arc(cx + ct0.cx, cy + ct0.cy, cr1, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); + ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedOcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); } else { // draw the two corners and the ring // eslint-disable-next-line max-len - ctx.arc(cx + ct0.cx, cy + ct0.cy, cr1, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); + ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); // eslint-disable-next-line max-len ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise); // eslint-disable-next-line max-len - ctx.arc(cx + ct1.cx, cy + ct1.cy, cr1, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); + ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); } } // the outer ring is a circular arc @@ -243,31 +275,30 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { ctx.lineTo(cx + xire, cy + yire); } // the inner ring has corners - else if (cr0 > e) { - const ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -cr0, clockwise); - const ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -cr0, clockwise); + else if (limitedIcrMax > e) { + const crStart = mathMin(icrStart, limitedIcrMax); + const crEnd = mathMin(icrEnd, limitedIcrMax); + const ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -crEnd, clockwise); + const ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -crStart, clockwise); ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); // Have the corners merged? - if (cr0 < icr) { + if (limitedIcrMax < icrMax) { // eslint-disable-next-line max-len - ctx.arc(cx + ct0.cx, cy + ct0.cy, cr0, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); + ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedIcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); } // draw the two corners and the ring else { // eslint-disable-next-line max-len - ctx.arc(cx + ct0.cx, cy + ct0.cy, cr0, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); + ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); // eslint-disable-next-line max-len ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise); // eslint-disable-next-line max-len - ctx.arc(cx + ct1.cx, cy + ct1.cy, cr0, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); + ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); } } // the inner ring is just a circular arc else { - // FIXME: if no lineTo, svg renderer will perform an abnormal drawing behavior. - ctx.lineTo(cx + xire, cy + yire); - ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); } } diff --git a/src/graphic/shape/Sector.ts b/src/graphic/shape/Sector.ts index 0b3f6cdfb..12da2c9da 100644 --- a/src/graphic/shape/Sector.ts +++ b/src/graphic/shape/Sector.ts @@ -9,8 +9,20 @@ export class SectorShape { startAngle = 0 endAngle = Math.PI * 2 clockwise = true - cornerRadius = 0 - innerCornerRadius = 0 + /** + * Corner radius of sector + * + * clockwise, from inside to outside, four corners are + * inner start -> inner end + * outer start -> outer end + * + * cr = 5 => [5, 5, 5, 5] + * cr = [5] => [5, 5, 0, 0] + * cr = [5, 10] => [5, 10, 5, 10] + * cr = [5, 10, 15] => [5, 10, 15, 15] + * cr = [5, 10, 15, 20] => [5, 10, 15, 20] + */ + cr: number | string | (number | string)[] = 0 } export interface SectorProps extends PathProps { diff --git a/test/sector.html b/test/sector.html index 791187e62..64fc05960 100644 --- a/test/sector.html +++ b/test/sector.html @@ -93,8 +93,24 @@ endAngle: 0, r0: 50, r: 100, - cornerRadius: 10, - innerCornerRadius: 10 + // cornerRadius: 10, + // innerCornerRadius: 10, + cr: [10, 10] + } + })); + + zr.add(new zrender.Sector({ + position: [400, 150], + scale: [1, 1], + style: { + stroke: 'black' + }, + shape: { + startAngle: Math.PI * -60 / 180, + endAngle: Math.PI * 60 / 180, + r0: 50, + r: 140, + cr: [5, 10, 15, 20] } })); From 53819e99ea5f5b4132d6eb1b4bfe23c95f30b8ea Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 28 Dec 2021 20:18:16 +0800 Subject: [PATCH 127/148] chore(build): process lib extension after compilation in watch mode. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f849f7ad1..378244797 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "build:bundle": "node build/build.js", "build:lib": "npx tsc -m ES2015 --outDir lib && node build/processLib.js", "watch:bundle": "node build/build.js --watch", - "watch:lib": "npx tsc -w -m ES2015 --outDir lib", + "watch:lib": "npx tsc-watch -m ES2015 --outDir lib --synchronousWatchDirectory --onSuccess \"node build/processLib.js\"", "test": "npx jest --config test/ut/jest.config.js", "lint": "npx eslint src/**/*.ts" }, From d9283206305957644987bc8bc09bc30206575484 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 28 Dec 2021 20:27:48 +0800 Subject: [PATCH 128/148] feat(sector): rename `cr` to `cornerRadius`. --- src/graphic/helper/roundSector.ts | 12 ++++++------ src/graphic/shape/Sector.ts | 12 ++++++------ test/sector.html | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index c5b7ab5cf..584c321c2 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -82,7 +82,7 @@ function computeCornerTangents( }; } -function calcCornerCircleCenter(x: number, y: number, r: number, angle: number) { +function calcCircleCenter(x: number, y: number, r: number, angle: number) { return { x: x + r * mathCos(angle), y: y + r * mathSin(angle) @@ -130,7 +130,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { clockwise?: boolean, r?: number, r0?: number, - cr?: number | string | (number | string)[] + cornerRadius?: number | string | (number | string)[] }) { const { r, r0 } = shape; let radius = mathMax(r, 0); @@ -156,7 +156,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { } const clockwise = !!shape.clockwise; - const { startAngle, endAngle, cx, cy, cr } = shape; + const { startAngle, endAngle, cx, cy, cornerRadius } = shape; // PENDING: whether normalizing angles is required? let arc: number; @@ -170,7 +170,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { arc = mathAbs(tmpAngles[0] - tmpAngles[1]); } - const [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cr, r0, r); + const [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius, r0, r); // is a point if (!(radius > e)) { @@ -178,12 +178,12 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { } // is a circle or annulus else if (arc > PI2 - e) { - const { x, y } = calcCornerCircleCenter(cx, cy, radius, startAngle); + const { x, y } = calcCircleCenter(cx, cy, radius, startAngle); ctx.moveTo(x, y); ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); if (innerRadius > e) { - const { x, y } = calcCornerCircleCenter(cx, cy, innerRadius, endAngle); + const { x, y } = calcCircleCenter(cx, cy, innerRadius, endAngle); ctx.moveTo(x, y); ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); } diff --git a/src/graphic/shape/Sector.ts b/src/graphic/shape/Sector.ts index 12da2c9da..5aba88844 100644 --- a/src/graphic/shape/Sector.ts +++ b/src/graphic/shape/Sector.ts @@ -16,13 +16,13 @@ export class SectorShape { * inner start -> inner end * outer start -> outer end * - * cr = 5 => [5, 5, 5, 5] - * cr = [5] => [5, 5, 0, 0] - * cr = [5, 10] => [5, 10, 5, 10] - * cr = [5, 10, 15] => [5, 10, 15, 15] - * cr = [5, 10, 15, 20] => [5, 10, 15, 20] + * 5 => [5, 5, 5, 5] + * [5] => [5, 5, 0, 0] + * [5, 10] => [5, 10, 5, 10] + * [5, 10, 15] => [5, 10, 15, 15] + * [5, 10, 15, 20] => [5, 10, 15, 20] */ - cr: number | string | (number | string)[] = 0 + cornerRadius: number | string | (number | string)[] = 0 } export interface SectorProps extends PathProps { diff --git a/test/sector.html b/test/sector.html index 64fc05960..7344829b0 100644 --- a/test/sector.html +++ b/test/sector.html @@ -95,7 +95,7 @@ r: 100, // cornerRadius: 10, // innerCornerRadius: 10, - cr: [10, 10] + cornerRadius: [10, 10] } })); @@ -110,7 +110,7 @@ endAngle: Math.PI * 60 / 180, r0: 50, r: 140, - cr: [5, 10, 15, 20] + cornerRadius: [5, 10, 15, 20] } })); From 022217b977ad9ff479befdc4ab8421e91f068fc0 Mon Sep 17 00:00:00 2001 From: plainheart Date: Wed, 29 Dec 2021 13:46:50 +0800 Subject: [PATCH 129/148] fix(sector): use normalized `innerRadius` & `radius` instead of raw `r0` & `r`. --- src/graphic/helper/roundSector.ts | 7 +++---- test/sector.html | 7 ++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 584c321c2..bce8cb8e8 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -132,9 +132,8 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { r0?: number, cornerRadius?: number | string | (number | string)[] }) { - const { r, r0 } = shape; - let radius = mathMax(r, 0); - let innerRadius = mathMax(r0 || 0, 0); + let radius = mathMax(shape.r, 0); + let innerRadius = mathMax(shape.r0 || 0, 0); const hasRadius = radius > 0; const hasInnerRadius = innerRadius > 0; @@ -170,7 +169,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { arc = mathAbs(tmpAngles[0] - tmpAngles[1]); } - const [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius, r0, r); + const [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius, innerRadius, radius); // is a point if (!(radius > e)) { diff --git a/test/sector.html b/test/sector.html index 7344829b0..9d8164047 100644 --- a/test/sector.html +++ b/test/sector.html @@ -106,11 +106,12 @@ stroke: 'black' }, shape: { - startAngle: Math.PI * -60 / 180, - endAngle: Math.PI * 60 / 180, + startAngle: Math.PI * -160 / 180, + endAngle: Math.PI * -20 / 180, r0: 50, r: 140, - cornerRadius: [5, 10, 15, 20] + cornerRadius: [5, 15, 0, '50%'], + // clockwise: false } })); From b3864e061c2bc5576d2dbc2c4d73a60e8f50a829 Mon Sep 17 00:00:00 2001 From: plainheart Date: Wed, 29 Dec 2021 13:54:48 +0800 Subject: [PATCH 130/148] test: support changing the renderer. --- test/sector.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/sector.html b/test/sector.html index 9d8164047..1651301e4 100644 --- a/test/sector.html +++ b/test/sector.html @@ -24,7 +24,9 @@ import * as zrender from '../index.js'; // 初始化zrender - var zr = zrender.init(document.getElementById('main')); + var zr = zrender.init(document.getElementById('main'), { + renderer: window.__ZRENDER__DEFAULT__RENDERER__ + }); zr.add(new zrender.Sector({ position: [100, 100], From cecdab1c3bee955f83fb4035ca591099725074a2 Mon Sep 17 00:00:00 2001 From: plainheart Date: Wed, 29 Dec 2021 18:27:28 +0800 Subject: [PATCH 131/148] fix(sector): 1) use `r - r0` as the relative value of percentage if the sector is annular. 2) skip normalize if no corner radius. --- src/graphic/helper/roundSector.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index bce8cb8e8..b4879b89a 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -89,7 +89,7 @@ function calcCircleCenter(x: number, y: number, r: number, angle: number) { }; } -// For compactibility, don't use normalizeCssArray +// For compatibility, don't use normalizeCssArray // 5 represents [5, 5, 5, 5] // [5] represents [5, 5, 0, 0] // [5, 10] represents [5, 5, 10, 10] @@ -119,7 +119,9 @@ function normalizeCornerRadius( else { arr = [cr, cr, cr, cr]; } - return map(arr, (cr, idx) => parsePercent(cr, idx < 2 ? r0 : r)); + // use `r - r0` if the sector is annular + const dr = r && r0 ? r - r0 : r; + return map(arr, cr => parsePercent(cr, dr)); } export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { @@ -169,7 +171,16 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { arc = mathAbs(tmpAngles[0] - tmpAngles[1]); } - const [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius, innerRadius, radius); + let icrStart; + let icrEnd; + let ocrStart; + let ocrEnd; + if (cornerRadius) { + [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius, innerRadius, radius); + } + else { + icrStart = icrEnd = ocrStart = ocrEnd = 0; + } // is a point if (!(radius > e)) { From f4ed2d731c1137a0ba057fa0bb3d34e92d8ddfc2 Mon Sep 17 00:00:00 2001 From: plainheart Date: Wed, 29 Dec 2021 18:29:43 +0800 Subject: [PATCH 132/148] fix(sector): remove unnecessary condition. --- src/graphic/helper/roundSector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index b4879b89a..40734c1a8 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -120,7 +120,7 @@ function normalizeCornerRadius( arr = [cr, cr, cr, cr]; } // use `r - r0` if the sector is annular - const dr = r && r0 ? r - r0 : r; + const dr = r0 ? r - r0 : r; return map(arr, cr => parsePercent(cr, dr)); } From 3bb9b1be6758ecda9503fc786927dd5077004684 Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 30 Dec 2021 08:25:25 +0800 Subject: [PATCH 133/148] fix(sector): avoid unnecessary invocation. --- src/graphic/helper/roundSector.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 40734c1a8..c05c95f25 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -103,6 +103,9 @@ function normalizeCornerRadius( let arr: (number | string)[]; if (isArray(cr)) { const len = cr.length; + if (!len) { + return []; + } if (len === 4) { arr = cr; } @@ -171,17 +174,6 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { arc = mathAbs(tmpAngles[0] - tmpAngles[1]); } - let icrStart; - let icrEnd; - let ocrStart; - let ocrEnd; - if (cornerRadius) { - [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius, innerRadius, radius); - } - else { - icrStart = icrEnd = ocrStart = ocrEnd = 0; - } - // is a point if (!(radius > e)) { ctx.moveTo(cx, cy); @@ -200,6 +192,14 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { } // is a circular or annular sector else { + let icrStart; + let icrEnd; + let ocrStart; + let ocrEnd; + if (cornerRadius) { + [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius, innerRadius, radius); + } + const halfRd = mathAbs(radius - innerRadius) / 2; let ocrs = mathMin(halfRd, ocrStart); let ocre = mathMin(halfRd, ocrEnd); From dcd5bf5d2693142870418670fea533c2b00af638 Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 30 Dec 2021 08:38:33 +0800 Subject: [PATCH 134/148] fix(sector): use `const` instead of `let`. --- src/graphic/helper/roundSector.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index c05c95f25..537afd010 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -201,13 +201,13 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { } const halfRd = mathAbs(radius - innerRadius) / 2; - let ocrs = mathMin(halfRd, ocrStart); - let ocre = mathMin(halfRd, ocrEnd); - let icrs = mathMin(halfRd, icrStart); - let icre = mathMin(halfRd, icrEnd); + const ocrs = mathMin(halfRd, ocrStart); + const ocre = mathMin(halfRd, ocrEnd); + const icrs = mathMin(halfRd, icrStart); + const icre = mathMin(halfRd, icrEnd); - let ocrMax = mathMax(ocrs, ocre); - let icrMax = mathMax(icrs, icre); + const ocrMax = mathMax(ocrs, ocre); + const icrMax = mathMax(icrs, icre); let limitedOcrMax = ocrMax; let limitedIcrMax = icrMax; From 8aa2f5cce552145c4ed87cc3481c4a5b3c66b1fd Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 31 Dec 2021 13:50:50 +0800 Subject: [PATCH 135/148] fix(sector): a small tweak. --- src/graphic/helper/roundSector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 537afd010..791e3d62a 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -104,7 +104,7 @@ function normalizeCornerRadius( if (isArray(cr)) { const len = cr.length; if (!len) { - return []; + return cr; } if (len === 4) { arr = cr; From 55b5d4e6d3ef61739b4ed715a8077b79d743332d Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 31 Dec 2021 22:11:30 +0800 Subject: [PATCH 136/148] fix(sector): fix types and tweak comment. --- src/graphic/helper/roundSector.ts | 4 ++-- src/graphic/shape/Sector.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 791e3d62a..8e7b6dff1 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -99,12 +99,12 @@ function normalizeCornerRadius( cr: number | string | (number | string)[], r0: number, r: number -) { +): number[] { let arr: (number | string)[]; if (isArray(cr)) { const len = cr.length; if (!len) { - return cr; + return cr as number[]; } if (len === 4) { arr = cr; diff --git a/src/graphic/shape/Sector.ts b/src/graphic/shape/Sector.ts index 5aba88844..3fa0337c9 100644 --- a/src/graphic/shape/Sector.ts +++ b/src/graphic/shape/Sector.ts @@ -18,7 +18,7 @@ export class SectorShape { * * 5 => [5, 5, 5, 5] * [5] => [5, 5, 0, 0] - * [5, 10] => [5, 10, 5, 10] + * [5, 10] => [5, 5, 10, 10] * [5, 10, 15] => [5, 10, 15, 15] * [5, 10, 15, 20] => [5, 10, 15, 20] */ From f3a06ed2021d9666a2b0e1a92478e88742a4af33 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 4 Jan 2022 11:33:08 +0800 Subject: [PATCH 137/148] fix(sector): recover some previous code. --- src/graphic/helper/roundSector.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 8e7b6dff1..68d1e320f 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -180,13 +180,17 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { } // is a circle or annulus else if (arc > PI2 - e) { - const { x, y } = calcCircleCenter(cx, cy, radius, startAngle); - ctx.moveTo(x, y); + ctx.moveTo( + cx + radius * mathCos(startAngle), + cy + radius * mathSin(startAngle) + ); ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); if (innerRadius > e) { - const { x, y } = calcCircleCenter(cx, cy, innerRadius, endAngle); - ctx.moveTo(x, y); + ctx.moveTo( + cx + innerRadius * mathCos(endAngle), + cy + innerRadius * mathSin(endAngle) + ); ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); } } From c6a5dfa77dbb93d8e943ecba82a83b71556fcd98 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 4 Jan 2022 11:36:17 +0800 Subject: [PATCH 138/148] chore: remove unused function. --- src/graphic/helper/roundSector.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 68d1e320f..5dee7ea1b 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -82,13 +82,6 @@ function computeCornerTangents( }; } -function calcCircleCenter(x: number, y: number, r: number, angle: number) { - return { - x: x + r * mathCos(angle), - y: y + r * mathSin(angle) - }; -} - // For compatibility, don't use normalizeCssArray // 5 represents [5, 5, 5, 5] // [5] represents [5, 5, 0, 0] From 0ee1b32be650f80cb262f8270860cb233e278c68 Mon Sep 17 00:00:00 2001 From: Di Wu Date: Tue, 4 Jan 2022 14:35:36 +0800 Subject: [PATCH 139/148] fix(svg): fix animation on strokePercent will cause partially drawing. --- src/svg/graphic.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 66b33b907..1a853473e 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -133,6 +133,7 @@ const buitinShapesDef: Record = { interface PathWithSVGBuildPath extends Path { __svgPathVersion: number __svgPathBuilder: SVGPathRebuilder + __svgPathStrokePercent: number } function hasShapeAnimation(el: Displayable) { @@ -185,7 +186,7 @@ export function brushSVGPath(el: Path, scope: BrushScope) { let svgPathBuilder = elExt.__svgPathBuilder; if (elExt.__svgPathVersion !== pathVersion || !svgPathBuilder - || strokePercent < 1 + || el.style.strokePercent !== elExt.__svgPathStrokePercent ) { if (!svgPathBuilder) { svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder(); @@ -194,6 +195,7 @@ export function brushSVGPath(el: Path, scope: BrushScope) { path.rebuildPath(svgPathBuilder, strokePercent); svgPathBuilder.generateStr(); elExt.__svgPathVersion = pathVersion; + elExt.__svgPathStrokePercent = el.style.strokePercent; } attrs.d = svgPathBuilder.getStr(); From 6547a9b47b2571341cdfb372bec53260eb92abed Mon Sep 17 00:00:00 2001 From: Di Wu Date: Tue, 4 Jan 2022 15:48:16 +0800 Subject: [PATCH 140/148] Use variable 'strokePercent' directly. --- src/svg/graphic.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 1a853473e..ba4892197 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -186,7 +186,7 @@ export function brushSVGPath(el: Path, scope: BrushScope) { let svgPathBuilder = elExt.__svgPathBuilder; if (elExt.__svgPathVersion !== pathVersion || !svgPathBuilder - || el.style.strokePercent !== elExt.__svgPathStrokePercent + || strokePercent !== elExt.__svgPathStrokePercent ) { if (!svgPathBuilder) { svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder(); @@ -195,7 +195,7 @@ export function brushSVGPath(el: Path, scope: BrushScope) { path.rebuildPath(svgPathBuilder, strokePercent); svgPathBuilder.generateStr(); elExt.__svgPathVersion = pathVersion; - elExt.__svgPathStrokePercent = el.style.strokePercent; + elExt.__svgPathStrokePercent = strokePercent; } attrs.d = svgPathBuilder.getStr(); From ef28006bd9318739e14aae6c921083ea62f13ed7 Mon Sep 17 00:00:00 2001 From: plainheart Date: Tue, 4 Jan 2022 22:25:36 +0800 Subject: [PATCH 141/148] fix(sector): fix some broken cases when sector arc is small. --- src/graphic/helper/roundSector.ts | 15 +++++++++------ test/sector.html | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 5dee7ea1b..0a2983c63 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -257,18 +257,18 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); // Have the corners merged? - if (limitedOcrMax < ocrMax) { + if (limitedOcrMax < ocrMax && crStart === crEnd) { // eslint-disable-next-line max-len ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedOcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); } else { // draw the two corners and the ring // eslint-disable-next-line max-len - ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); + crStart > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); // eslint-disable-next-line max-len ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise); // eslint-disable-next-line max-len - ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); + crEnd > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); } } // the outer ring is a circular arc @@ -290,22 +290,25 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); // Have the corners merged? - if (limitedIcrMax < icrMax) { + if (limitedIcrMax < icrMax && crStart === crEnd) { // eslint-disable-next-line max-len ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedIcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); } // draw the two corners and the ring else { // eslint-disable-next-line max-len - ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); + crEnd > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); // eslint-disable-next-line max-len ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise); // eslint-disable-next-line max-len - ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); + crStart > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); } } // the inner ring is just a circular arc else { + // FIXME: if no lineTo, svg renderer will perform an abnormal drawing behavior. + ctx.lineTo(cx + xire, cy + yire); + ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); } } diff --git a/test/sector.html b/test/sector.html index 1651301e4..64794119f 100644 --- a/test/sector.html +++ b/test/sector.html @@ -116,6 +116,35 @@ // clockwise: false } })); + + zr.add(new zrender.Sector({ + position: [400, 350], + scale: [1, 1], + style: { + stroke: 'black' + }, + shape: { + startAngle: Math.PI * -160 / 180, + endAngle: Math.PI * -20 / 180, + r0: 50, + r: 140, + cornerRadius: [0, 0, 0, 20] + } + })); + + zr.add(new zrender.Sector({ + position: [400, 550], + scale: [1, 1], + style: { + stroke: 'black' + }, + shape: { + startAngle: Math.PI * -160 / 180, + endAngle: Math.PI * -20 / 180, + r: 140, + cornerRadius: [0, 0, '20%', 20] + } + }));
From f11e39e41c595ef2cbe1c850d626d54e271788af Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 6 Jan 2022 22:44:18 +0800 Subject: [PATCH 142/148] refactor(sector): remove support for percentage value of corner radius. --- src/graphic/helper/roundSector.ts | 28 +++++++++++----------------- src/graphic/shape/Sector.ts | 2 +- test/sector.html | 4 ++-- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 0a2983c63..f157e169e 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -88,36 +88,30 @@ function computeCornerTangents( // [5, 10] represents [5, 5, 10, 10] // [5, 10, 15] represents [5, 10, 15, 15] // [5, 10, 15, 20] represents [5, 10, 15, 20] -function normalizeCornerRadius( - cr: number | string | (number | string)[], - r0: number, - r: number -): number[] { - let arr: (number | string)[]; +function normalizeCornerRadius(cr: number | number[]): number[] { + let arr: number[]; if (isArray(cr)) { const len = cr.length; if (!len) { return cr as number[]; } - if (len === 4) { - arr = cr; - } - else if (len === 3) { - arr = cr.concat(cr[len - 1]); + if (len === 1) { + arr = [cr[0], cr[0], 0, 0]; } else if (len === 2) { arr = [cr[0], cr[0], cr[1], cr[1]]; } + else if (len === 3) { + arr = cr.concat(cr[2]); + } else { - arr = [cr[0], cr[0], 0, 0]; + arr = cr; } } else { arr = [cr, cr, cr, cr]; } - // use `r - r0` if the sector is annular - const dr = r0 ? r - r0 : r; - return map(arr, cr => parsePercent(cr, dr)); + return arr; } export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { @@ -128,7 +122,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { clockwise?: boolean, r?: number, r0?: number, - cornerRadius?: number | string | (number | string)[] + cornerRadius?: number | number[] }) { let radius = mathMax(shape.r, 0); let innerRadius = mathMax(shape.r0 || 0, 0); @@ -194,7 +188,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { let ocrStart; let ocrEnd; if (cornerRadius) { - [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius, innerRadius, radius); + [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius); } const halfRd = mathAbs(radius - innerRadius) / 2; diff --git a/src/graphic/shape/Sector.ts b/src/graphic/shape/Sector.ts index 3fa0337c9..96550325f 100644 --- a/src/graphic/shape/Sector.ts +++ b/src/graphic/shape/Sector.ts @@ -22,7 +22,7 @@ export class SectorShape { * [5, 10, 15] => [5, 10, 15, 15] * [5, 10, 15, 20] => [5, 10, 15, 20] */ - cornerRadius: number | string | (number | string)[] = 0 + cornerRadius: number | number[] = 0 } export interface SectorProps extends PathProps { diff --git a/test/sector.html b/test/sector.html index 64794119f..e0930fea3 100644 --- a/test/sector.html +++ b/test/sector.html @@ -112,7 +112,7 @@ endAngle: Math.PI * -20 / 180, r0: 50, r: 140, - cornerRadius: [5, 15, 0, '50%'], + cornerRadius: [5, 15, 0, 70], // clockwise: false } })); @@ -142,7 +142,7 @@ startAngle: Math.PI * -160 / 180, endAngle: Math.PI * -20 / 180, r: 140, - cornerRadius: [0, 0, '20%', 20] + cornerRadius: [0, 0, 35, 20] } })); From 4f5c6d64648ec15719b9c761addb34c23ed6fc65 Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 6 Jan 2022 23:21:28 +0800 Subject: [PATCH 143/148] fix: remove unused import. --- src/graphic/helper/roundSector.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index f157e169e..42128502b 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -1,6 +1,5 @@ -import { parsePercent } from '../../contain/text'; import PathProxy, { normalizeArcAngles } from '../../core/PathProxy'; -import { isArray, map } from '../../core/util'; +import { isArray } from '../../core/util'; const PI = Math.PI; const PI2 = PI * 2; From 8eb919d7ba170cd4261a8edda1fa446e1fbb345a Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 7 Jan 2022 18:30:00 +0800 Subject: [PATCH 144/148] chore: update the description and keywords in package.json --- package.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 378244797..394200ad1 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,13 @@ { "name": "zrender", "version": "5.2.1", - "description": "A lightweight canvas library.", + "description": "A lightweight graphic library providing 2d draw for Apache ECharts", "keywords": [ "canvas", - "2d" + "svg", + "2d", + "html5", + "vector-graphics" ], "repository": { "type": "git", From c42b08230600190a6b8dbaa52aa98f95cb1fced0 Mon Sep 17 00:00:00 2001 From: plainheart Date: Sun, 9 Jan 2022 01:06:31 +0800 Subject: [PATCH 145/148] fix(animator): `NaN` shouldn't be considered as the number type. --- src/animation/Animator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index dd1cd0e76..be22e63c1 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -4,7 +4,7 @@ import Clip from './Clip'; import * as color from '../tool/color'; -import {extend, isArrayLike, isFunction, isGradientObject, isNumber, isString, keys, logError, map} from '../core/util'; +import {eqNaN, extend, isArrayLike, isFunction, isGradientObject, isNumber, isString, keys, logError, map} from '../core/util'; import {ArrayLike, Dictionary} from '../core/types'; import easingFuncs, { AnimationEasing } from './easing'; import Animation from './Animation'; @@ -306,7 +306,7 @@ class Track { } } else { - if (isNumber(value)) { + if (isNumber(rawValue) && !eqNaN(rawValue)) { valType = VALUE_TYPE_NUMBER; } else if (isString(rawValue)) { From a40f31a211dcecbd83869c1945da1ff5e4c93369 Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 13 Jan 2022 14:20:09 +0800 Subject: [PATCH 146/148] fix(srctor): 1) don't use `normalizeArcAngles` anymore. 2) optimize the logic, avoid unnecessary calculation if no arc. --- src/graphic/helper/roundSector.ts | 103 ++++++++++++++++-------------- test/sector.html | 31 ++++++++- 2 files changed, 84 insertions(+), 50 deletions(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 42128502b..176bf3f85 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -148,17 +148,9 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { const clockwise = !!shape.clockwise; const { startAngle, endAngle, cx, cy, cornerRadius } = shape; - // PENDING: whether normalizing angles is required? - let arc: number; - // FIXME: there may be a precision issue in `normalizeArcAngles` - if (startAngle === endAngle) { - arc = 0; - } - else { - const tmpAngles = [startAngle, endAngle]; - normalizeArcAngles(tmpAngles, !clockwise); - arc = mathAbs(tmpAngles[0] - tmpAngles[1]); - } + let arc = mathAbs(endAngle - startAngle); + const mod = arc && arc % PI2; + mod && (arc = mod); // is a point if (!(radius > e)) { @@ -186,58 +178,71 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { let icrEnd; let ocrStart; let ocrEnd; - if (cornerRadius) { - [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius); - } - const halfRd = mathAbs(radius - innerRadius) / 2; - const ocrs = mathMin(halfRd, ocrStart); - const ocre = mathMin(halfRd, ocrEnd); - const icrs = mathMin(halfRd, icrStart); - const icre = mathMin(halfRd, icrEnd); + let ocrs; + let ocre; + let icrs; + let icre; - const ocrMax = mathMax(ocrs, ocre); - const icrMax = mathMax(icrs, icre); - let limitedOcrMax = ocrMax; - let limitedIcrMax = icrMax; + let ocrMax; + let icrMax; + let limitedOcrMax; + let limitedIcrMax; + + let xre; + let yre; + let xirs; + let yirs; const xrs = radius * mathCos(startAngle); const yrs = radius * mathSin(startAngle); const xire = innerRadius * mathCos(endAngle); const yire = innerRadius * mathSin(endAngle); - let xre; - let yre; - let xirs; - let yirs; + const hasArc = arc > e; + if (hasArc) { + if (cornerRadius) { + [icrStart, icrEnd, ocrStart, ocrEnd] = normalizeCornerRadius(cornerRadius); + } + + const halfRd = mathAbs(radius - innerRadius) / 2; + ocrs = mathMin(halfRd, ocrStart); + ocre = mathMin(halfRd, ocrEnd); + icrs = mathMin(halfRd, icrStart); + icre = mathMin(halfRd, icrEnd); + + limitedOcrMax = ocrMax = mathMax(ocrs, ocre); + limitedIcrMax = icrMax = mathMax(icrs, icre); - // draw corner radius - if (ocrMax > e || icrMax > e) { - xre = radius * mathCos(endAngle); - yre = radius * mathSin(endAngle); - xirs = innerRadius * mathCos(startAngle); - yirs = innerRadius * mathSin(startAngle); + // draw corner radius + if (ocrMax > e || icrMax > e) { + xre = radius * mathCos(endAngle); + yre = radius * mathSin(endAngle); + xirs = innerRadius * mathCos(startAngle); + yirs = innerRadius * mathSin(startAngle); - // restrict the max value of corner radius - if (arc < PI) { - const it = intersect(xrs, yrs, xirs, yirs, xre, yre, xire, yire); - if (it) { - const x0 = xrs - it[0]; - const y0 = yrs - it[1]; - const x1 = xre - it[0]; - const y1 = yre - it[1]; - const a = 1 / mathSin( - mathACos((x0 * x1 + y0 * y1) / (mathSqrt(x0 * x0 + y0 * y0) * mathSqrt(x1 * x1 + y1 * y1))) / 2 - ); - const b = mathSqrt(it[0] * it[0] + it[1] * it[1]); - limitedOcrMax = mathMin(ocrMax, (radius - b) / (a + 1)); - limitedIcrMax = mathMin(icrMax, (innerRadius - b) / (a - 1)); + // restrict the max value of corner radius + if (arc < PI) { + const it = intersect(xrs, yrs, xirs, yirs, xre, yre, xire, yire); + if (it) { + const x0 = xrs - it[0]; + const y0 = yrs - it[1]; + const x1 = xre - it[0]; + const y1 = yre - it[1]; + const a = 1 / mathSin( + // eslint-disable-next-line max-len + mathACos((x0 * x1 + y0 * y1) / (mathSqrt(x0 * x0 + y0 * y0) * mathSqrt(x1 * x1 + y1 * y1))) / 2 + ); + const b = mathSqrt(it[0] * it[0] + it[1] * it[1]); + limitedOcrMax = mathMin(ocrMax, (radius - b) / (a + 1)); + limitedIcrMax = mathMin(icrMax, (innerRadius - b) / (a - 1)); + } } } } // the sector is collapsed to a line - if (!(arc > e)) { + if (!hasArc) { ctx.moveTo(cx + xrs, cy + yrs); } // the outer ring has corners @@ -271,7 +276,7 @@ export function buildPath(ctx: CanvasRenderingContext2D | PathProxy, shape: { } // no inner ring, is a circular sector - if (!(innerRadius > e) || !(arc > e)) { + if (!(innerRadius > e) || !hasArc) { ctx.lineTo(cx + xire, cy + yire); } // the inner ring has corners diff --git a/test/sector.html b/test/sector.html index e0930fea3..828c096c5 100644 --- a/test/sector.html +++ b/test/sector.html @@ -101,6 +101,35 @@ } })); + zr.add(new zrender.Sector({ + position: [100, 300], + scale: [1, 1], + style: { + stroke: 'black' + }, + shape: { + startAngle: 0.6981317007977319, + endAngle: 0.6981317007977318, + r: 100, + //clockwise: false + } + })); + + zr.add(new zrender.Sector({ + position: [100, 550], + scale: [1, 1], + style: { + stroke: 'black' + }, + shape: { + startAngle: Math.PI * 2, + endAngle: -1, + cornerRadius: 20, + r: 100, + //clockwise: false + } + })); + zr.add(new zrender.Sector({ position: [400, 150], scale: [1, 1], @@ -146,6 +175,6 @@ } })); -
+
\ No newline at end of file From 25033efe9d310cfa0fb797f0b6ee1ee08a5c5b48 Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 13 Jan 2022 14:26:36 +0800 Subject: [PATCH 147/148] fix(sector): remove unused import. --- src/graphic/helper/roundSector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphic/helper/roundSector.ts b/src/graphic/helper/roundSector.ts index 176bf3f85..1c2099214 100644 --- a/src/graphic/helper/roundSector.ts +++ b/src/graphic/helper/roundSector.ts @@ -1,4 +1,4 @@ -import PathProxy, { normalizeArcAngles } from '../../core/PathProxy'; +import PathProxy from '../../core/PathProxy'; import { isArray } from '../../core/util'; const PI = Math.PI; From e417624950eff946906cfec3cf4d39d41cee3904 Mon Sep 17 00:00:00 2001 From: pissang Date: Fri, 14 Jan 2022 20:30:21 +0800 Subject: [PATCH 148/148] fix(animation): fix gradient animation --- src/animation/Animator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/animation/Animator.ts b/src/animation/Animator.ts index dd1cd0e76..edfcf7cd7 100644 --- a/src/animation/Animator.ts +++ b/src/animation/Animator.ts @@ -543,7 +543,7 @@ class Track { target[propName] = { type: isLinearGradient ? 'linear' : 'radial', x: interpolateNumber(val.x, nextVal.x, w), - y: interpolateNumber(val.x, nextVal.x, w), + y: interpolateNumber(val.y, nextVal.y, w), // TODO performance colorStops: map(val.colorStops, (colorStop, idx) => { const nextColorStop = nextVal.colorStops[idx];