| 
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]; 
 | 
        }); 
 | 
} 
 |