zk/src/main/resources/web/js/zk/flex.ts

Summary

Maintainability
F
1 wk
Test Coverage
/* flex.ts

Purpose:

Description:

History:
    Fri Jun  3 14:14:21 TST 2011, Created by jumperchen

Copyright (C) 2011 Potix Corporation. All Rights Reserved.

This program is distributed under GPL Version 3.0 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
*/
export type FlexOrient = 'w' | 'h';
export interface FlexSize {
    width?: string | number;
    height?: string | number;
}

function _getTextSize(zkc: zk.JQZK, zkp: zk.JQZK, zkpOffset: zk.Offset): zk.Offset {
    let $zkc = zkc.jq,
        $prev = $zkc.prev(),
        pos: zk.Offset = [0, 0],
        coldVal: Record<string, string>,
        poldVal: Record<string, string>,
        zs: CSSStyleDeclaration | undefined, ps: CSSStyleDeclaration | undefined;

    if ($prev.length) {
        ps = $prev[0].style;
        // ZK-700 ignore prev if not displayed
        if (!zk($prev[0]).isRealVisible()) // B65-ZK-1925: Use isRealVisible() to determine it is visible or not
            return pos;
        else {
            zs = $zkc[0].style;
            // store the old value
            coldVal = {};
            poldVal = {};
            for (let margins = ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'],
                     len = margins.length; len-- > 0;) {
                coldVal[margins[len]] = zs[margins[len]] as string;
                poldVal[margins[len]] = ps[margins[len]] as string;

                // clean margin
                zs[margins[len]] = ps[margins[len]] = '0px';
            }

            let offset = $prev.zk.revisedOffset();
            pos[0] = offset[0] + $prev.zk.offsetWidth();
            pos[1] = offset[1] + $prev.zk.offsetHeight();
        }
    } else {
        pos[0] = zkpOffset[0] + zkp.sumStyles('l', jq.paddings) + zkp.sumStyles('l', jq.borders);
        pos[1] = zkpOffset[1] + zkp.sumStyles('t', jq.paddings) + zkp.sumStyles('t', jq.borders);
    }

    // ZK-700
    const offset = zkc.revisedOffset();
    pos[0] = offset[0] - pos[0];
    pos[1] = offset[1] - pos[1];

    // revert the values
    zk.copy(zs, coldVal!);
    zk.copy(ps, poldVal!);

    return [Math.max(0, pos[0]), Math.max(0, pos[1])]; // ZK-2414 and 3298164
}

// check whether the two elements are the same baseline, if so, we need to
// sum them together.
function _isSameBaseline(ref: zk.Dimension & {_hgh?: number; _wdh?: number}, cur: zk.Dimension, vertical): boolean {
    let hgh: number, wdh: number;
    if (vertical) {
        hgh = ref._hgh ?? (ref._hgh = ref.top + ref.height);
        wdh = ref._wdh ?? (ref._wdh = ref.left + ref.width);
        return cur.top >= hgh || cur.left < wdh;
    } else {
        hgh = ref._hgh ?? (ref._hgh = ref.top + ref.height);
        wdh = ref._wdh ?? (ref._wdh = ref.left + ref.width);
        return cur.left >= wdh || cur.top < hgh;
    }
}

function _fixMinFlex(isVflex?): ((wgt: zk.Widget, wgtn: HTMLElement, o: FlexOrient,
                                  min?: number) => number) {
    let flexsz: string, sizePos: string, flex: string, offsetPos: string, marginPos: string, maxFlexPos: string, sumFlexPos: string,
        index: number, contentPos: string;
    if (isVflex) {
        flexsz = '_vflexsz';
        sizePos = 'height';
        flex = '_vflex';
        offsetPos = 'offsetHeight';
        marginPos = 'marginHeight';
        maxFlexPos = '_maxFlexHeight';
        sumFlexPos = '_sumFlexHeight';
        index = 1;
        contentPos = 'getContentEdgeHeight_';
    } else {
        flexsz = '_hflexsz';
        sizePos = 'width';
        flex = '_hflex';
        offsetPos = 'offsetWidth';
        marginPos = 'marginWidth';
        maxFlexPos = '_maxFlexWidth';
        sumFlexPos = '_sumFlexWidth';
        index = 0;
        contentPos = 'getContentEdgeWidth_';
    }
    return function (wgt: zk.Widget, wgtn: HTMLElement, o: FlexOrient, min?: number) {
        if (wgt[flexsz] === undefined) { //cached?
            let cwgt = wgt.firstChild, //bug #2928109
                n = wgtn,
                zkn = zk(n),
                max = 0;

            if (min != null)
                max = min;
            else {
                const map = {};
                map[sizePos] = 'auto';
                wgt.setFlexSize_(map, true);
                let totalsz = 0,
                    vmax = 0;
                if (cwgt && cwgt.desktop) { //try child widgets, bug ZK-1575: should check if child widget is bind to desktop
                    let first: zk.Widget | undefined = cwgt,
                        refDim: zk.Dimension | undefined;


                    // ZK-2248: ignore widget dimension in vflex/hflex calculation
                    while (first && first.ignoreFlexSize_(o))
                        first = first.nextSibling;

                    for (; cwgt; cwgt = cwgt.nextSibling) { //bug 3132199: hflex="min" in hlayout
                        if (!cwgt.ignoreFlexSize_(o)) {
                            let c = cwgt.$n();
                            if (c) { //node might not exist if rod on
                                //Skip absolute or fixed DOM element size
                                const cpos = c.style.position;
                                if (cpos == 'absolute' || cpos == 'fixed')
                                    continue;

                                let zkc = zk(c),
                                    sz = 0;
                                if (cwgt[flex] == 'min') {
                                    if (zkc.isVisible()) {
                                        sz += cwgt[flexsz] === undefined ? zFlex.fixMinFlex(cwgt, c, o) : cwgt[flexsz];
                                    }
                                } else {
                                    cwgt.beforeParentMinFlex_(o);
                                    sz += wgt.getChildMinSize_(o, cwgt) // fixed for B50-3157031.zul
                                        + (zkc[marginPos] as CallableFunction)();
                                }

                                //bug #3006276: East/West bottom cut if East/West higher than Center.
                                if (cwgt[maxFlexPos] && sz > vmax) //@See West/East/Center
                                    vmax = sz;
                                else if (cwgt[sumFlexPos]) //@See North/South
                                    totalsz += sz;
                                else if (!cwgt[maxFlexPos] && first != cwgt
                                        && _isSameBaseline(refDim || (refDim = zk(first).dimension(true)), zkc.dimension(true), isVflex))
                                    max += sz;
                                else if (sz > max)
                                    max = sz;
                            }
                        }
                    }
                } else {
                    let c = wgtn.firstChild;
                    if (c) { //no child widget, try html element directly
                        //feature 3000339: The hflex of the cloumn will calculate by max width
                        let isText = c.nodeType == 3,
                            ignore = wgt.ignoreChildNodeOffset_(o),
                            first = c,
                            refDim: zk.Dimension | undefined;
                        for (; c; c = c.nextSibling) {
                            //Skip absolute or fixed DOM element size
                            isText = c.nodeType == 3; // Bug ZK-2275, we need to update c.nodeType
                            if (!isText) {
                                const cpos = (c as HTMLElement).style.position;
                                if (cpos == 'absolute' || cpos == 'fixed')
                                    continue;
                            }

                            let zkc = zk(c),
                                sz = 0;
                            if (ignore) {
                                for (let el = c.firstChild; el; el = el.nextSibling) {
                                    let txt = el && el.nodeType == 3 ? el.nodeValue : undefined,
                                        zel: zk.JQZK;
                                    if (txt) {
                                        const dim = zkc.textSize(txt);
                                        if (dim[1] > sz)
                                            sz = dim[1];
                                    } else if ((zel = zk(el)).isVisible()) {
                                        const s = (zel[offsetPos] as (() => number))() + (zel[marginPos] as (() => number))();
                                        if (s > sz)
                                            sz = s;
                                    }
                                }
                            } else {
                                if (isText)
                                    sz = c.nodeValue ? zkn.textSize(c.nodeValue)[index] : 0;
                                else {
                                    sz = (zkc[offsetPos] as (() => number))() + (zkc[marginPos] as (() => number))();
                                }
                            }
                            if (isText) {
                                if (sz > max)
                                    max = sz;
                            } else {
                                const curDim = zkc.dimension(true);
                                if (_isSameBaseline(refDim || (refDim = zk(first).dimension(true)),
                                        curDim, isVflex))
                                    max += sz;
                                else if (sz > max)
                                    max = sz;
                            }
                        }
                    } else //no kids at all, use self
                        max = (zkn[offsetPos] as (() => number))();
                }
                if (vmax)
                    totalsz += vmax;
                if (totalsz > max)
                    max = totalsz;
            }

            //bug #3005284: (Chrome)Groupbox hflex="min" in borderlayout wrong sized
            //bug #3006707: The title of the groupbox shouldn't be strikethrough(Chrome)
            let margin = wgt.getMarginSize_(o);
            if (zk.webkit && margin < 0)
                margin = 0;


            // ZK-970: refixed for caption
            let map: Record<string, number> = {},
                wn = wgt.$n()!,
                hasChildren = zk.isLoaded('zul.wgt') && (wgt instanceof zul.wgt.Caption) && wgt.nChildren > 0,
                size = hasChildren ? (zk(wgt.$n('cave'))[offsetPos] as (() => number))() : max;

            map[sizePos] = size + (wgt[contentPos] as ((n) => number))(size);
            wgt.setFlexSize_(map, true);
            const szInfo = {height: wn.offsetHeight, width: wn.offsetWidth};
            if (szInfo[sizePos] >= 0)
                wgt[flexsz] = szInfo[sizePos] as number + margin;

            wgt.afterChildrenMinFlex_(o);

            // notify my parent when my size has been changed and my parent is not in min flex
            // Bug ZK-2117
            if (wgt.parent && wgt.parent[flex] != 'min')
                wgt.parent.afterChildMinFlexChanged_(wgt, o);
        }
        return wgt[flexsz] as number;
    };
}

const _fixMinVflex = _fixMinFlex(true),
    _fixMinHflex = _fixMinFlex();

function _zero(): 0 {
    return 0;
}

export namespace flex_global {
    export const zFlex = { //static methods
        /**
         * beforeSize for read was created to prevent tremendous forced reflows
         * as a result of doing both read and write in beforeSize.
         * @since 9.5.1
         */
        beforeSizeForRead(this: zk.Widget): void {
            const wgt = this,
                n = wgt.$n();
            // ZK-4154 prevent from forced reflow
            wgt._beforeSizeHasScroll = !!(n && (n.scrollTop || n.scrollLeft));
        },
        beforeSize(this: zk.Widget, ctl: unknown, opts: unknown, cleanup: boolean): void {
            let wgt = this, p;
            if (cleanup)
                wgt.clearCachedSize_();

            //bug#3042306: H/Vflex in IE6 can't shrink; others cause scrollbar space
            if (!zk.mounting && wgt.isRealVisible()) {
                if (wgt._hflex && wgt._hflex != 'min') {
                    wgt.resetSize_('w');
                    // Bug ZK-597
                    delete wgt._flexFixed;
                    if ((p = wgt.parent))
                        (p as zk.Widget).afterResetChildSize_('w');
                }
                if (wgt._vflex && wgt._vflex != 'min') {
                    wgt.resetSize_('h');
                    // Bug ZK-597
                    delete wgt._flexFixed;
                    if ((p = wgt.parent))
                        (p as zk.Widget).afterResetChildSize_('h');
                }
                delete wgt._beforeSizeHasScroll;
            }
        },

        beforeSizeClearCachedSize(this: zk.Widget, ctl: unknown, opts: unknown, cleanup: boolean): void {
            const wgt = this;
            if (cleanup)
                wgt.clearCachedSize_();
        },

        onSize(this: zk.Widget): void {
            zFlex.fixFlex(this);
        },
        fixFlex(wgt: zk.Widget): void {
            let hflexWgt;
            if (wgt._flexFixed || (!wgt._nvflex && !wgt._nhflex)) { //other vflex/hflex sibliing has done it!
                delete wgt._flexFixed;
                return;
            }
            //avoid firedown("onSize") calling in again
            if ((wgt._vflex === undefined || (wgt._vflexsz && wgt._vflex == 'min'))
                && (wgt._hflex === undefined || (wgt._hflexsz && wgt._hflex == 'min')))
                return;

            if (wgt.ignoreFlexSize_('w') && wgt.ignoreFlexSize_('h'))
                return;

            if (!wgt.parent?.beforeChildrenFlex_(wgt)) { //don't do fixflex if return false
                return;
            }
            wgt._flexFixed = true;

            let pretxt = false, //pre node is a text node
                vflexs: zk.Widget[] = [],
                vflexsz = 0,
                hflexs: zk.Widget[] = [],
                hflexsz = 0,
                p = wgt.$n()?.parentNode as HTMLElement,
                zkp = zk(p),
                psz = wgt.getParentSize_(p),
                hgh = psz.height,
                wdh = psz.width,
                c = p.firstElementChild as HTMLElement,
                vflexsRe: zk.Widget[] = [],
                hasVScroll = zkp.hasVScroll(),
                hasHScroll = zkp.hasHScroll(),
                meshBodyHasVScroll = false,
                meshBodyHasHScroll = false,
                scrollbarWidth = jq.scrollbarWidth(),
                isMeshLoaded = zk.isLoaded('zul.mesh');

            // B86-ZK-4123
            if (isMeshLoaded && (wgt instanceof zul.mesh.HeaderWidget)) {
                const mesh = wgt.parent.parent;
                if (mesh && mesh._nativebar) {
                    const body = mesh.ebody as HTMLElement;
                    meshBodyHasVScroll = zk(body).hasVScroll();
                    meshBodyHasHScroll = zk(body).hasHScroll();
                }
            }

            // Bug 3185686, B50-ZK-452
            if (hasVScroll || meshBodyHasVScroll) //with vertical scrollbar
                wdh -= scrollbarWidth;

            // B50-3312936.zul
            if (hasHScroll || meshBodyHasHScroll) //with horizontal scrollbar
                hgh -= scrollbarWidth;

            for (let zkpOffset: zk.Offset | undefined; c; c = c.nextElementSibling as HTMLElement) {
                //In ZK, we assume all text node is space (otherwise, it will be span enclosed)
                if (c.nodeType === 3) { //a text node
                    pretxt = true;
                    continue;
                }
                //Skip absolute or fixed DOM element size
                const cpos = c.style.position;
                if (cpos == 'absolute' || cpos == 'fixed')
                    continue;

                const zkc = zk(c);
                if (zkc.isVisible(true)) {
                    let offhgh = zkc.offsetHeight(),
                        offwdh = offhgh > 0 ? zkc.offsetWidth() : 0,
                        cwgt = zk.Widget.$(c, {exact: true});

                    //ZK-2776: we can't and shouldn't set width to auxheader
                    if (cwgt && isMeshLoaded && (cwgt instanceof zul.mesh.Auxheader))
                        continue;

                    //Bug ZK-1647: should consider header width
                    //Bug Flex-138: skip if width exists
                    if (offwdh == 0 && cwgt && isMeshLoaded
                        && (cwgt instanceof zul.mesh.HeaderWidget)) {
                        // don't use jq().width() in chrome for performance issue
                        const hdfaker = cwgt.$n('hdfaker');
                        offwdh = hdfaker ? zk.parseInt(hdfaker.style.width) || hdfaker.offsetWidth : 0; //use faker width
                    }

                    //Bug ZK-1706: should consider all text node size
                    if (pretxt) {
                        if (!zkpOffset)
                            zkpOffset = zkp.revisedOffset();
                        const size = _getTextSize(zkc, zkp, zkpOffset);
                        if (!cwgt || !cwgt.isExcludedHflex_()) // fixed ZK-1706 sideeffect
                            wdh -= size[0];
                        if (!cwgt || !cwgt.isExcludedVflex_()) // fixed ZK-1706 sideeffect for B60-ZK-917.zul
                            hgh -= size[1];
                    }
                    //horizontal size
                    if (cwgt && cwgt._nhflex && cwgt.isVisible()) { //Bug ZK-3883: to prevent invisible cwgt
                        if (cwgt !== wgt)
                            cwgt._flexFixed = true; //tell other hflex siblings I have done it.
                        if (cwgt._hflex == 'min') {
                            wdh -= zFlex.fixMinFlex(cwgt, c, 'w');
                        } else {
                            hflexs.push(cwgt);
                            hflexsz += cwgt._nhflex;
                        }
                    } else if ((!cwgt &&
                            // panelchild cannot include panel's bottombar.
                            (!zk.isLoaded('zul.wnd') || !(wgt instanceof zul.wnd.Panelchildren)))
                        || (cwgt && !cwgt.isExcludedHflex_())) {
                        wdh -= offwdh;
                        wdh -= zkc.marginWidth();
                    }

                    //vertical size
                    if (cwgt && cwgt._nvflex && cwgt.isVisible()) { //Bug ZK-3883: to prevent invisible cwgt
                        if (cwgt !== wgt)
                            cwgt._flexFixed = true; //tell other vflex siblings I have done it.
                        if (cwgt._vflex == 'min') {
                            hgh -= zFlex.fixMinFlex(cwgt, c, 'h');
                        } else {
                            vflexs.push(cwgt);
                            vflexsRe.push(cwgt);
                            vflexsz += cwgt._nvflex;
                        }
                    } else if (!cwgt || !cwgt.isExcludedVflex_()) {
                        hgh -= offhgh;
                        hgh -= zkc.marginHeight();
                    }

                    pretxt = false;
                }
            }

            // ZK-3411: use local function for setting up height
            let setHghForVflexChild = function (vfxs: zk.Widget[], h: number, lsz: number): void {
                    for (let j = vfxs.length - 1; j > 0; --j) {
                        const cwgt = vfxs.shift()!,
                            vsz = cwgt.isExcludedVflex_() ? h : (cwgt._nvflex! * h / vflexsz) | 0; //cast to integer
                        cwgt.setFlexSize_({height: vsz});
                        cwgt._vflexsz = vsz;
                        if (!cwgt.isExcludedVflex_())
                            lsz -= vsz;
                    }
                    //last one with vflex
                    if (vfxs.length) {
                        const cwgt = vfxs.shift()!;
                        cwgt.setFlexSize_({height: lsz});
                        cwgt._vflexsz = lsz;
                    }
                },
                lastsz = hgh = Math.max(hgh, 0);

            //setup the height for the vflex child
            //avoid floating number calculation error(TODO: shall distribute error evenly)
            setHghForVflexChild(vflexs, hgh, lastsz);

            //3042306: H/Vflex in IE6 can't shrink; others cause scrollbar space
            //vertical scrollbar might disappear after height was set
            const newpsz = wgt.getParentSize_(p);
            if (newpsz.width > psz.width) {//yes, the scrollbar gone!
                wdh += (newpsz.width - psz.width);
            } else if (!zkp.hasVScroll() && hasVScroll) { // ZK-3858: Window inside a <center> with autoscroll true doesn't resize itself correctly
                wdh += scrollbarWidth;
            }

            //setup the width for the hflex child
            //avoid floating number calculation error(TODO: shall distribute error evenly)
            lastsz = wdh = Math.max(wdh, 0);
            for (let j = hflexs.length - 1; j > 0; --j) {
                hflexWgt = hflexs.shift();
                if (!hflexWgt) break;
                const cwgt = hflexWgt as zk.Widget, //{n: node, f: hflex}
                    hsz = cwgt.isExcludedHflex_() ? wdh : (cwgt._nhflex! * wdh / hflexsz) | 0; //cast to integer
                cwgt.setFlexSize_({width: hsz});
                cwgt._hflexsz = hsz;
                if (!cwgt.isExcludedHflex_())
                    lastsz -= hsz;
            }
            //last one with hflex
            hflexWgt = hflexs.shift();
            if (hflexWgt) {
                const cwgt = hflexWgt as zk.Widget;
                cwgt.setFlexSize_({width: lastsz});
                cwgt._hflexsz = lastsz;
            }

            // ZK-3411: height need to be reset if the horizontal scrollbar disappeared
            const fixForParentHeight = newpsz.height > psz.height,
                fixForParentHScroll = !zkp.hasHScroll() && hasHScroll;
            if (fixForParentHeight || fixForParentHScroll) {   //horizontal scrollbar disappeared
                if (fixForParentHeight) {
                    hgh += (newpsz.height - psz.height);
                } else if (fixForParentHScroll) { // ZK-3858: Window inside a <center> with autoscroll true doesn't resize itself correctly
                    hgh += scrollbarWidth;
                }
                lastsz = hgh = Math.max(hgh, 0);
                setHghForVflexChild(vflexsRe, hgh, lastsz);
            }

            //notify parent widget that all of its children with hflex/vflex is done.
            wgt.parent.afterChildrenFlex_(wgt);
            wgt._flexFixed = false;
        },
        onFitSize(this: zk.Widget): void {
            const wgt = this,
                c = wgt.$n();
            if (c && zk(c).isVisible()) {
                // Bug ZK-3014: offsetWidth will be available only when wgt is real visible
                if (wgt._hflex == 'min' && wgt._hflexsz === undefined && wgt.isRealVisible() && !wgt.ignoreFlexSize_('w'))
                    zFlex.fixMinFlex(wgt, c, 'w');
                // Bug ZK-3014: offsetHeight will be available only when wgt is real visible
                if (wgt._vflex == 'min' && wgt._vflexsz === undefined && wgt.isRealVisible() && !wgt.ignoreFlexSize_('h'))
                    zFlex.fixMinFlex(wgt, c, 'h');
            }
        },
        fixMinFlex(wgt: zk.Widget, wgtn: HTMLElement, o: FlexOrient): number {
            //find the max size of all children
            return (o == 'h' ? _fixMinVflex : o == 'w' ? _fixMinHflex : _zero)(wgt, wgtn, o, wgt.beforeMinFlex_(o));
        },
        applyCSSFlex(this: zk.Widget): void {
            const wgt = this;
            if (!wgt._nvflex && !wgt._nhflex)
                return;

            let cssFlexAppliedInfo = wgt._cssFlexApplied,
                pwgt = wgt.parent;
            if (!pwgt) return;
            let minFlexInfoList: zk.MinFlexInfo[] | undefined = [];
            const fContainer = (pwgt instanceof zk.Page) ? pwgt.$n() : pwgt.getFlexContainer_();

            if ((cssFlexAppliedInfo && cssFlexAppliedInfo.flexApplied)) { //other vflex/hflex sibling has done it!
                minFlexInfoList = cssFlexAppliedInfo.minFlexInfoList;
                if (minFlexInfoList) { //still need to call fixMinFlex
                    for (let i = 0, l = minFlexInfoList.length; i < l; i++) {
                        const info = minFlexInfoList[i];
                        zFlex.fixMinFlex(info.wgt, info.wgtn, info.orient);
                    }
                    pwgt.afterChildrenFlex_(wgt);
                }
                // Fix ZK-5153
                const fcWgt: zk.Widget & {_cssflexContainer?: boolean} | undefined = zk.$(fContainer),
                    fcJQ = jq(fContainer);
                if (fcWgt && fcWgt._cssflexContainer && !fcJQ.hasClass('z-flex') /* fix for B95-ZK-4764.zul */) {
                    fcJQ.addClass('z-flex').addClass(zFlex.getFlexInfo(wgt).isFlexRow ? 'z-flex-row' : 'z-flex-column');
                }
                return;
            }
            if (fContainer == null) { //using old flex
                wgt._cssflex = false;
                return;
            }

            const flexInfo = zFlex.getFlexInfo(wgt);

            // reassigned after getFlexInfo
            cssFlexAppliedInfo = wgt._cssFlexApplied!;

            let isRow = flexInfo.isFlexRow,
                fccs: HTMLElement[] = flexInfo.flexContainerChildren,
                cwgts = flexInfo.childrenWidgets,
                isAllMin = true,
                cwgtsz: Record<string, string>[] = [],
                flexItemClass = 'z-flex-item';

            for (let i = 0, length = fccs.length; i < length; i++) {
                let fcc = fccs[i],
                    jqFcc = jq(fcc),
                    cwgt = cwgts[i],
                    c = cwgt.$n()!,
                    flexs = [cwgt._nvflex, cwgt._nhflex],
                    sz: Record<string, string> = {},
                    dim = isRow ? 'width' : 'height',
                    flex = flexs[isRow ? 1 : 0];

                if (flex && flex > 0) {
                    isAllMin = false;
                    jqFcc.addClass(flexItemClass);
                    if (flex != 1 || fcc.style.flexGrow)
                        fcc.style.flexGrow = flex as unknown as string; //update flex
                    if (fcc != c && !c.style[dim]) {
                        let cMarginSize = isRow ? zk(c).marginHeight() : zk(c).marginWidth(),
                            cDimValue = cMarginSize > 0 ? 'calc(100% - ' + jq.px0(cMarginSize) + ')' : '100%'; //handle margin issue
                        c.style[dim] = cDimValue;
                    } else

                        sz[dim] = 'auto';
                } else if (flex && flex < 0) {//min
                    const orient = dim.substring(0, 1) as FlexOrient;
                    zFlex.fixMinFlex(cwgt, c, orient);
                    minFlexInfoList.push({wgt: cwgt, wgtn: c, orient: orient});
                }

                //check else flex
                flex = flexs[isRow ? 0 : 1];
                dim = isRow ? 'height' : 'width';
                if (flex && flex > 0) {
                    const marginSize = isRow ? zk(jqFcc).marginHeight() : zk(jqFcc).marginWidth(); //handle margin issue
                    fcc.style[dim] = marginSize > 0 ? 'calc(100% - ' + jq.px0(marginSize) + ')' : '100%';
                    if (fcc != c) {
                        let cMarginSize = isRow ? zk(c).marginHeight() : zk(c).marginWidth(),
                            cDimValue = cMarginSize > 0 ? 'calc(100% - ' + jq.px0(cMarginSize) + ')' : '100%'; //handle margin issue
                        c.style[dim] = cDimValue;
                    } else
                        sz[dim] = 'auto';
                } else if (flex && flex < 0) {//min
                    const orient = dim.substring(0, 1) as FlexOrient;
                    zFlex.fixMinFlex(cwgt, c, orient);
                    minFlexInfoList.push({wgt: cwgt, wgtn: c, orient: orient});
                }
                cwgtsz.push(sz);
            }

            if (minFlexInfoList.length > 0)
                cssFlexAppliedInfo.minFlexInfoList = minFlexInfoList;

            const fcWgt: zk.Widget & {_cssflexContainer?: boolean} | undefined = zk.$(fContainer);
            if (!isAllMin) {
                if (fcWgt) {
                    fcWgt._cssflexContainer = true;
                }
                jq(fContainer).addClass('z-flex').addClass(isRow ? 'z-flex-row' : 'z-flex-column');
            } else {
                if (fcWgt) {
                    delete fcWgt._cssflexContainer;
                }
            }

            for (let i = 0, length = cwgtsz.length; i < length; i++) {
                const szInfo = cwgtsz[i];
                if (Object.keys(szInfo).length > 0) //es5
                    cwgts[i].setFlexSize_(szInfo);
            }

            pwgt.afterChildrenFlex_(wgt);
        },
        clearCSSFlex(wgt: zk.Widget, o: FlexOrient, clearAllSiblings?: boolean): void {
            if (!wgt._cssFlexApplied) return;

            const pwgt = wgt.parent;
            if (!pwgt) return;
            let fContainer = pwgt instanceof zk.Page ? pwgt.$n() : pwgt.getFlexContainer_(),
                isHorizontal = o == 'h',
                flexInfo = zFlex.getFlexInfo(wgt),
                isRow = flexInfo.isFlexRow,
                fccs = flexInfo.flexContainerChildren,
                cwgts = flexInfo.childrenWidgets,
                noSibFlex = true,
                flexItemClass = 'z-flex-item';

            for (let i = 0, length = fccs.length; i < length; i++) {
                let fcc = fccs[i],
                    jqFcc = jq(fcc),
                    cwgt = cwgts[i],
                    isTargetWgt = cwgt == wgt,
                    c = cwgt.$n()!,
                    dim = isRow ? 'width' : 'height';

                if ((clearAllSiblings || isTargetWgt) && (isHorizontal && isRow) || (!isHorizontal && !isRow)) {
                    fcc.style.flexGrow = '';
                    jqFcc.removeClass(flexItemClass);
                    if (fcc != c && !c.style[dim])
                        c.style[dim] = '';
                    delete cwgt._cssFlexApplied;
                }

                //check else flex
                dim = isRow ? 'height' : 'width';
                if (clearAllSiblings || isTargetWgt) {
                    if ((isHorizontal && !isRow) || (!isHorizontal && isRow)) {
                        fcc.style[dim] = '';
                        if (fcc != c)
                            c.style[dim] = '';
                        delete cwgt._cssFlexApplied;
                    }
                } else
                    noSibFlex = noSibFlex ? !jqFcc.hasClass(flexItemClass) : false;
            }

            if (clearAllSiblings || noSibFlex) {
                const fcWgt: zk.Widget & {_cssflexContainer?: boolean} | undefined = zk.$(fContainer);
                if (fcWgt) delete fcWgt._cssflexContainer;
                jq(fContainer).removeClass('z-flex').removeClass(isRow ? 'z-flex-row' : 'z-flex-column');
            }

            wgt.afterClearFlex_();
        },
        getFlexInfo(wgt: zk.Widget): { isFlexRow: boolean; flexContainerChildren: HTMLElement[]; childrenWidgets: zk.Widget[] } {
            let pwgt = wgt.parent,
                cwgt = pwgt!.firstChild,
                fContainer = pwgt!.getFlexContainer_()!,
                fcc = fContainer.firstElementChild as HTMLElement,
                flexD = pwgt!.getFlexDirection_(),
                isRow = 'row' == flexD,
                fccs: HTMLElement[] = [],
                cwgts: zk.Widget[] = [],
                checkColumn = flexD == null,
                toColumn = false;

            for (let c: HTMLElement | undefined; fcc && cwgt;) { // assume cwgt -> fcc
                c = cwgt.$n();
                if (cwgt.ignoreFlexSize_(isRow ? 'w' : 'h') || (c && !fContainer.contains(c))) { // skip ignored or moved dom (ex.caption)
                    cwgt._cssFlexApplied = {flexApplied: true};
                    cwgt = cwgt.nextSibling;
                    continue;
                }
                if (c && fContainer.contains(c)) {
                    if (fcc.contains(c)) {
                        cwgt._cssFlexApplied = {flexApplied: true};
                        fccs.push(fcc);
                        cwgts.push(cwgt);
                        if (checkColumn && jq(fcc).css('display') === 'block') { // no default direction, check first block
                            toColumn = true;
                            checkColumn = false;
                        }
                    } else {
                        fcc = fcc.nextElementSibling as HTMLElement; //skip c not in fcc (ex. splitter)
                        continue;
                    }
                }
                fcc = fcc.nextElementSibling as HTMLElement;
                cwgt = cwgt.nextSibling;
            }

            if (flexD == null) {//should alter flex-direction
                isRow = !toColumn;
                return {
                    isFlexRow: isRow,
                    flexContainerChildren: fccs,
                    childrenWidgets: cwgts
                };
            }
            return {
                isFlexRow: isRow,
                flexContainerChildren: fccs,
                childrenWidgets: cwgts
            };
        }
    };
}
zk.copy(window, flex_global);
const _xWidget = zk.augment(zk.Widget.prototype, {
    /** @internal */
    domClass_(no?: zk.DomClassOptions): string {
        let domClass = /*safe*/ _xWidget.domClass_.call(this, no),
            n = this.$n() as HTMLElement | undefined;
        if (n) {
            const jqn = jq(n),
                flexClasses = ['z-flex', 'z-flex-row', 'z-flex-column', 'z-flex-item'];
            for (let i = 0, length = flexClasses.length; i < length; i++) {
                const flexClass = flexClasses[i];
                if (jqn.hasClass(flexClass)) {
                    domClass += ' ' + flexClass;
                }
            }
        }
        return domClass;
    }
});