|   | 
| import env from './env'; | 
| import {buildTransformer} from './fourPointsTransform'; | 
| import {Dictionary} from './types'; | 
|   | 
| const EVENT_SAVED_PROP = '___zrEVENTSAVED'; | 
| const _calcOut: number[] = []; | 
|   | 
| type SavedInfo = { | 
|     markers?: HTMLDivElement[] | 
|     trans?: ReturnType<typeof buildTransformer> | 
|     invTrans?: ReturnType<typeof buildTransformer> | 
|     srcCoords?: number[] | 
| } | 
|   | 
| /** | 
|  * Transform "local coord" from `elFrom` to `elTarget`. | 
|  * "local coord": the coord based on the input `el`. The origin point is at | 
|  *     the position of "left: 0; top: 0;" in the `el`. | 
|  * | 
|  * Support when CSS transform is used. | 
|  * | 
|  * Having the `out` (that is, `[outX, outY]`), we can create an DOM element | 
|  * and set the CSS style as "left: outX; top: outY;" and append it to `elTarge` | 
|  * to locate the element. | 
|  * | 
|  * For example, this code below positions a child of `document.body` on the event | 
|  * point, no matter whether `body` has `margin`/`paddin`/`transfrom`/... : | 
|  * ```js | 
|  * transformLocalCoord(out, container, document.body, event.offsetX, event.offsetY); | 
|  * if (!eqNaN(out[0])) { | 
|  *     // Then locate the tip element on the event point. | 
|  *     var tipEl = document.createElement('div'); | 
|  *     tipEl.style.cssText = 'position: absolute; left:' + out[0] + ';top:' + out[1] + ';'; | 
|  *     document.body.appendChild(tipEl); | 
|  * } | 
|  * ``` | 
|  * | 
|  * Notice: In some env this method is not supported. If called, `out` will be `[NaN, NaN]`. | 
|  * | 
|  * @param {Array.<number>} out [inX: number, inY: number] The output.. | 
|  *        If can not transform, `out` will not be modified but return `false`. | 
|  * @param {HTMLElement} elFrom The `[inX, inY]` is based on elFrom. | 
|  * @param {HTMLElement} elTarget The `out` is based on elTarget. | 
|  * @param {number} inX | 
|  * @param {number} inY | 
|  * @return {boolean} Whether transform successfully. | 
|  */ | 
| export function transformLocalCoord( | 
|     out: number[], | 
|     elFrom: HTMLElement, | 
|     elTarget: HTMLElement, | 
|     inX: number, | 
|     inY: number | 
| ) { | 
|     return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true) | 
|         && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]); | 
| } | 
|   | 
| /** | 
|  * Transform between a "viewport coord" and a "local coord". | 
|  * "viewport coord": the coord based on the left-top corner of the viewport | 
|  *     of the browser. | 
|  * "local coord": the coord based on the input `el`. The origin point is at | 
|  *     the position of "left: 0; top: 0;" in the `el`. | 
|  * | 
|  * Support the case when CSS transform is used on el. | 
|  * | 
|  * @param out [inX: number, inY: number] The output. If `inverse: false`, | 
|  *        it represents "local coord", otherwise "vireport coord". | 
|  *        If can not transform, `out` will not be modified but return `false`. | 
|  * @param el The "local coord" is based on the `el`, see comment above. | 
|  * @param inX If `inverse: false`, | 
|  *        it represents "vireport coord", otherwise "local coord". | 
|  * @param inY If `inverse: false`, | 
|  *        it represents "vireport coord", otherwise "local coord". | 
|  * @param inverse | 
|  *        `true`: from "viewport coord" to "local coord". | 
|  *        `false`: from "local coord" to "viewport coord". | 
|  * @return {boolean} Whether transform successfully. | 
|  */ | 
| export function transformCoordWithViewport( | 
|     out: number[], | 
|     el: HTMLElement, | 
|     inX: number, | 
|     inY: number, | 
|     inverse?: boolean | 
| ) { | 
|     if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) { | 
|         const saved = (el as any)[EVENT_SAVED_PROP] || ((el as any)[EVENT_SAVED_PROP] = {}); | 
|         const markers = prepareCoordMarkers(el, saved); | 
|         const transformer = preparePointerTransformer(markers, saved, inverse); | 
|         if (transformer) { | 
|             transformer(out, inX, inY); | 
|             return true; | 
|         } | 
|     } | 
|     return false; | 
| } | 
|   | 
| function prepareCoordMarkers(el: HTMLElement, saved: SavedInfo) { | 
|     let markers = saved.markers; | 
|     if (markers) { | 
|         return markers; | 
|     } | 
|   | 
|     markers = saved.markers = []; | 
|     const propLR = ['left', 'right']; | 
|     const propTB = ['top', 'bottom']; | 
|   | 
|     for (let i = 0; i < 4; i++) { | 
|         const marker = document.createElement('div'); | 
|         const stl = marker.style; | 
|         const idxLR = i % 2; | 
|         const idxTB = (i >> 1) % 2; | 
|         stl.cssText = [ | 
|             'position: absolute', | 
|             'visibility: hidden', | 
|             'padding: 0', | 
|             'margin: 0', | 
|             'border-width: 0', | 
|             'user-select: none', | 
|             'width:0', | 
|             'height:0', | 
|             // 'width: 5px', | 
|             // 'height: 5px', | 
|             propLR[idxLR] + ':0', | 
|             propTB[idxTB] + ':0', | 
|             propLR[1 - idxLR] + ':auto', | 
|             propTB[1 - idxTB] + ':auto', | 
|             '' | 
|         ].join('!important;'); | 
|         el.appendChild(marker); | 
|         markers.push(marker); | 
|     } | 
|   | 
|     return markers; | 
| } | 
|   | 
| function preparePointerTransformer(markers: HTMLDivElement[], saved: SavedInfo, inverse?: boolean) { | 
|     const transformerName: 'invTrans' | 'trans' = inverse ? 'invTrans' : 'trans'; | 
|     const transformer = saved[transformerName]; | 
|     const oldSrcCoords = saved.srcCoords; | 
|     const srcCoords = []; | 
|     const destCoords = []; | 
|     let oldCoordTheSame = true; | 
|   | 
|     for (let i = 0; i < 4; i++) { | 
|         const rect = markers[i].getBoundingClientRect(); | 
|         const ii = 2 * i; | 
|         const x = rect.left; | 
|         const y = rect.top; | 
|         srcCoords.push(x, y); | 
|         oldCoordTheSame = oldCoordTheSame && oldSrcCoords && x === oldSrcCoords[ii] && y === oldSrcCoords[ii + 1]; | 
|         destCoords.push(markers[i].offsetLeft, markers[i].offsetTop); | 
|     } | 
|     // Cache to avoid time consuming of `buildTransformer`. | 
|     return (oldCoordTheSame && transformer) | 
|         ? transformer | 
|         : ( | 
|             saved.srcCoords = srcCoords, | 
|             saved[transformerName] = inverse | 
|                 ? buildTransformer(destCoords, srcCoords) | 
|                 : buildTransformer(srcCoords, destCoords) | 
|         ); | 
| } | 
|   | 
| export function isCanvasEl(el: HTMLElement): el is HTMLCanvasElement { | 
|     return el.nodeName.toUpperCase() === 'CANVAS'; | 
| } | 
|   | 
| const replaceReg = /([&<>"'])/g; | 
| const replaceMap: Dictionary<string> = { | 
|     '&': '&', | 
|     '<': '<', | 
|     '>': '>', | 
|     '"': '"', | 
|     '\'': ''' | 
| }; | 
|   | 
| export function encodeHTML(source: string): string { | 
|     return source == null | 
|         ? '' | 
|         : (source + '').replace(replaceReg, function (str, c) { | 
|             return replaceMap[c]; | 
|         }); | 
| } |